github.com/moontrade/nogc@v0.1.7/alloc_tinygowasm.go (about)

     1  //go:build tinygo.wasm
     2  // +build tinygo.wasm
     3  
     4  package nogc
     5  
     6  import (
     7  	"github.com/moontrade/nogc/alloc/tlsf"
     8  	"unsafe"
     9  )
    10  
    11  ////////////////////////////////////////////////////////////////////////////////////
    12  // Global Allocator
    13  ////////////////////////////////////////////////////////////////////////////////////
    14  
    15  var allocator *tlsf.Heap
    16  
    17  func HeapInstance() *tlsf.Heap {
    18  	return allocator
    19  }
    20  
    21  ////////////////////////////////////////////////////////////////////////////////////
    22  // Global allocator convenience
    23  ////////////////////////////////////////////////////////////////////////////////////
    24  
    25  // Alloc calls Alloc on the system allocator
    26  //export malloc
    27  func Alloc(size uintptr) Pointer {
    28  	return Pointer(allocator.Alloc(size))
    29  }
    30  func AllocCap(size uintptr) (Pointer, uintptr) {
    31  	ptr := allocator.Alloc(size)
    32  	return Pointer(ptr), tlsf.SizeOf(ptr)
    33  }
    34  func AllocZeroed(size uintptr) Pointer {
    35  	return Pointer(allocator.AllocZeroed(size))
    36  }
    37  func AllocZeroedCap(size uintptr) (Pointer, uintptr) {
    38  	ptr := allocator.AllocZeroed(size)
    39  	return Pointer(ptr), tlsf.SizeOf(ptr)
    40  }
    41  
    42  // Alloc calls Alloc on the system allocator
    43  //export calloc
    44  func Calloc(num, size uintptr) Pointer {
    45  	return Pointer(allocator.AllocZeroed(num * size))
    46  }
    47  func CallocCap(num, size uintptr) (Pointer, uintptr) {
    48  	ptr := allocator.AllocZeroed(num * size)
    49  	return Pointer(ptr), tlsf.SizeOf(ptr)
    50  }
    51  
    52  // Realloc calls Realloc on the system allocator
    53  //export realloc
    54  func Realloc(p Pointer, size uintptr) Pointer {
    55  	return Pointer(allocator.Realloc(uintptr(p), size))
    56  }
    57  func ReallocCap(p Pointer, size uintptr) (Pointer, uintptr) {
    58  	newPtr := allocator.Realloc(uintptr(p), size)
    59  	return Pointer(newPtr), tlsf.SizeOf(newPtr)
    60  }
    61  
    62  // Free calls Free on the system allocator
    63  //export free
    64  func Free(p Pointer) {
    65  	allocator.Free(uintptr(p))
    66  }
    67  
    68  ////export malloc_usable_size
    69  func Sizeof(p Pointer) uintptr {
    70  	return uintptr(tlsf.SizeOf(uintptr(p)))
    71  }
    72  
    73  func Scope(fn func(a AutoFree)) {
    74  	a := NewAuto(32)
    75  	defer a.Free()
    76  	fn(a)
    77  }
    78  
    79  //// Scope creates an AutoFree free list that automatically reclaims memory
    80  //// after callback finishes.
    81  //func (a *Heap) Scope(fn func(a AutoFree)) {
    82  //	auto := NewAuto(a.AsAllocator(), 32)
    83  //	fn(auto)
    84  //	auto.Free()
    85  //}
    86  
    87  ////////////////////////////////////////////////////////////////////////////////////
    88  // tinygo hooks
    89  ////////////////////////////////////////////////////////////////////////////////////
    90  
    91  const wasmPageSize = 64 * 1024
    92  
    93  func WASMMemorySize(index int32) int32 {
    94  	return wasm_memory_size(index)
    95  }
    96  
    97  func WASMMemorySizeBytes(index int32) int32 {
    98  	return wasm_memory_size(index) * wasmPageSize
    99  }
   100  
   101  //export llvm.wasm.memory.size.i32
   102  func wasm_memory_size(index int32) int32
   103  
   104  //export llvm.wasm.memory.grow.i32
   105  func wasm_memory_grow(index, pages int32) int32
   106  
   107  func growBy(pages int32) (uintptr, uintptr) {
   108  	before := wasm_memory_size(0)
   109  	wasm_memory_grow(0, pages)
   110  	after := wasm_memory_size(0)
   111  	if before == after {
   112  		return 0, 0
   113  	}
   114  	return uintptr(before * wasmPageSize), uintptr(after * wasmPageSize)
   115  }
   116  
   117  func allocatorPointer() uintptr {
   118  	return uintptr(unsafe.Pointer(allocator))
   119  }
   120  
   121  //go:linkname initAllocator runtime.initAllocator
   122  func initAllocator(heapStart, heapEnd uintptr) {
   123  	//println("initAllocator", uint(heapStart), uint(heapEnd))
   124  	//println("globals", uint(globalsStart), uint(globalsEnd))
   125  
   126  	// Did we get called twice?
   127  	if allocator != nil {
   128  		return
   129  	}
   130  
   131  	var (
   132  		pagesBefore = wasm_memory_size(0)
   133  		pagesAfter  = pagesBefore
   134  	)
   135  
   136  	// Need to add a page initially to fit minimum size required by allocator?
   137  	if heapStart == 0 || heapStart+unsafe.Sizeof(tlsf.Heap{})+tlsf.RootSize+tlsf.ALMask+16 >
   138  		uintptr(pagesBefore)*uintptr(wasmPageSize) {
   139  		// Just need a single page. Root size is much smaller than a single WASM page.
   140  		wasm_memory_grow(0, 1)
   141  		pagesAfter = wasm_memory_size(0)
   142  	}
   143  
   144  	// Bootstrap allocator which will take over all allocations from now on.
   145  	allocator = tlsf.Bootstrap(
   146  		heapStart,
   147  		uintptr(pagesAfter)*uintptr(wasmPageSize),
   148  		1,
   149  		GrowMin,
   150  	)
   151  }
   152  
   153  func SetGrow(grow tlsf.Grow) {
   154  	if allocator == nil {
   155  		initAllocator(0, 0)
   156  	}
   157  	if allocator != nil {
   158  		allocator.Grow = grow
   159  	}
   160  }
   161  
   162  // GrowByDouble will double the heap on each Grow
   163  func GrowByDouble(pagesBefore, pagesNeeded int32, minSize uintptr) (pagesAdded int32, start, end uintptr) {
   164  	if pagesBefore > pagesNeeded {
   165  		pagesAdded = pagesBefore
   166  	} else {
   167  		pagesAdded = pagesNeeded
   168  	}
   169  	start, end = growBy(pagesAdded)
   170  	if start == 0 {
   171  		pagesAdded = pagesNeeded
   172  		start, end = growBy(pagesAdded)
   173  		if start == 0 {
   174  			return 0, 0, 0
   175  		}
   176  	}
   177  	return
   178  }
   179  
   180  var (
   181  	growByPages int32 = 1
   182  )
   183  
   184  // GrowBy will Grow by the number of pages specified or by the minimum needed, whichever is greater.
   185  func GrowBy(pages int32) tlsf.Grow {
   186  	growByPages = pages
   187  	return doGrowByPages
   188  }
   189  
   190  func doGrowByPages(pagesBefore, pagesNeeded int32, minSize uintptr) (pagesAdded int32, start, end uintptr) {
   191  	if growByPages > pagesNeeded {
   192  		pagesAdded = growByPages
   193  	} else {
   194  		pagesAdded = pagesNeeded
   195  	}
   196  	start, end = growBy(pagesAdded)
   197  	if start == 0 {
   198  		pagesAdded = pagesNeeded
   199  		start, end = growBy(pagesAdded)
   200  		if start == 0 {
   201  			return 0, 0, 0
   202  		}
   203  	}
   204  	return
   205  }
   206  
   207  // GrowByMin will Grow by a single page or by the minimum needed, whichever is greater.
   208  func GrowMin(pagesBefore, pagesNeeded int32, minSize uintptr) (int32, uintptr, uintptr) {
   209  	start, end := growBy(pagesNeeded)
   210  	if start == 0 {
   211  		return 0, 0, 0
   212  	}
   213  	return pagesNeeded, start, end
   214  }