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 }