@@ -2,18 +2,79 @@ package cabi
22
33import "unsafe"
44
5+ // The Go runtime-internal sbrk function.
6+ // Note that this isn't *really* sbrk, and doesn't necessarily result in heap
7+ // growth. Instead, it reserves a block of memory of size n bytes, which then
8+ // isn't used by Go's GC allocator.
9+ // The heap will only actually grown if the requested allocation doesn't fit.
10+ //go:linkname sbrk runtime.sbrk
11+ func sbrk (n uintptr ) unsafe.Pointer
12+
13+ // Returns the memory region from `v` to `v + n` to the Go runtime, enabling
14+ // its use by the garbage collector.
15+ //go:linkname sysFreeOS runtime.sysFreeOS
16+ func sysFreeOS (v unsafe.Pointer , n uintptr )
17+
18+ var useGCAllocations = false
19+
20+ func init () {
21+ // Once `init` is called, the runtime has been initialized, and we can
22+ // start using managed memory.
23+ useGCAllocations = true
24+ }
25+
526// realloc allocates or reallocates memory for Component Model calls across
627// the host-guest boundary.
728//
8- // Note: the use of uintptr assumes 32-bit pointers when compiled for wasm or wasm32.
29+ // Note: while in Go, as opposed to TinyGo, `uintptr` is 64-bit, `go:wasmexport`
30+ // properly coerces the parameters/return values to and from 32-bit values when
31+ // compiled for wasm or wasm32.
32+ // TODO: file bug for sysReserveAlignedSbrk not always releasing the memlock.
933//go:wasmexport cabi_realloc
1034func realloc (ptr unsafe.Pointer , size , align , newsize uintptr ) unsafe.Pointer {
35+
36+ // If the Go runtime has not been initialized, we need to
37+ // allocate memory using sbrk directly.
38+ // This happens because the runtime itself does two calls to
39+ // imported functions during initialization, which result in
40+ // calls to `cabi_realloc` before the runtime is fully initialized.
41+ if ! useGCAllocations {
42+ if newsize == 0 {
43+ if ptr != nil {
44+ // Free the old pointer if it is not nil.
45+ // Note that in practice, this case isn't ever hit:
46+ // `cabi_realloc` isn't called with a non-nil pointer before the
47+ // Go runtime is fully initialized.
48+ sysFreeOS (ptr , size )
49+ }
50+
51+ return nil
52+ }
53+
54+ alignedSize := newsize + offset (newsize , align )
55+ unaligned := sbrk (alignedSize )
56+ off := offset (uintptr (unaligned ), align )
57+ newptr := unsafe .Add (unaligned , off )
58+ if ptr != nil && newptr != nil && size > 0 {
59+ // Copy the old data to the new pointer.
60+ // Note that in practice, this case isn't ever hit:
61+ // `cabi_realloc` isn't called with a non-nil pointer.
62+ copy (unsafe .Slice ((* byte )(newptr ), size ), unsafe .Slice ((* byte )(ptr ), size ))
63+ }
64+ if ptr != nil {
65+ // Free the old pointer if it is not nil.
66+ sysFreeOS (ptr , size )
67+ }
68+ return newptr
69+ }
70+
1171 if newsize <= size {
1272 return unsafe .Add (ptr , offset (uintptr (ptr ), align ))
1373 }
1474 newptr := alloc (newsize , align )
1575 if size > 0 {
1676 copy (unsafe .Slice ((* byte )(newptr ), newsize ), unsafe .Slice ((* byte )(ptr ), size ))
77+
1778 }
1879 return newptr
1980}
@@ -29,6 +90,16 @@ func offset(ptr, align uintptr) uintptr {
2990// It attempts to align the allocated memory by allocating a slice of
3091// a type that matches the desired alignment. It aligns to 16 bytes for
3192// values of align other than 1, 2, 4, or 8.
93+ //
94+ // The allocation itself is done using the Go runtime's GC allocator.
95+ // We then return a raw pointer to the underlying memory, without keeping
96+ // any references to the allocated object itself. That would be very bad
97+ // indeed if there was a risk of the GC running before we're done using
98+ // the memory, or before it's rooted by another GC object.
99+ //
100+ // Since WebAssembly is a single-threaded environment, and the use of the
101+ // returned memory is tightly controlled by generated bindings code, neither
102+ // of these issues is a problem in practice.
32103func alloc (size , align uintptr ) unsafe.Pointer {
33104 switch align {
34105 case 1 :
0 commit comments