diff options
author | Ayke van Laethem <[email protected]> | 2024-10-22 15:53:57 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2024-10-23 09:13:30 +0100 |
commit | 0f95b4102d83b6977277ea7d5e87ad538eddc5ef (patch) | |
tree | 8a63d2ea5f7028a9edeaa663f7782b9d4b9a101e | |
parent | 24c11d4ba5f722ef646ae0e1ee06b90b2411590c (diff) | |
download | tinygo-0f95b4102d83b6977277ea7d5e87ad538eddc5ef.tar.gz tinygo-0f95b4102d83b6977277ea7d5e87ad538eddc5ef.zip |
wasm: use precise GC for WebAssembly (including WASI)
With a few small modifications, all the problems with `-gc=precise` in
WebAssembly seem to have been fixed.
I didn't do any performance measurements, but this is supposed to
improve GC performance.
-rw-r--r-- | src/internal/task/task.go | 3 | ||||
-rw-r--r-- | src/internal/task/task_asyncify.go | 30 | ||||
-rw-r--r-- | src/internal/task/task_stack.go | 3 | ||||
-rw-r--r-- | targets/wasip1.json | 1 | ||||
-rw-r--r-- | targets/wasip2.json | 1 | ||||
-rw-r--r-- | targets/wasm.json | 1 |
6 files changed, 25 insertions, 14 deletions
diff --git a/src/internal/task/task.go b/src/internal/task/task.go index c45739097..c1ee57dda 100644 --- a/src/internal/task/task.go +++ b/src/internal/task/task.go @@ -30,3 +30,6 @@ type Task struct { // the given function and falls back to the default stack size. It is replaced // with a load from a special section just before codegen. func getGoroutineStackSize(fn uintptr) uintptr + +//go:linkname runtime_alloc runtime.alloc +func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer diff --git a/src/internal/task/task_asyncify.go b/src/internal/task/task_asyncify.go index b16ddd7d8..55a1044e4 100644 --- a/src/internal/task/task_asyncify.go +++ b/src/internal/task/task_asyncify.go @@ -35,11 +35,16 @@ type state struct { type stackState struct { // asyncify is the stack pointer of the asyncify stack. // This starts from the bottom and grows upwards. - asyncifysp uintptr + asyncifysp unsafe.Pointer // asyncify is stack pointer of the C stack. // This starts from the top and grows downwards. - csp uintptr + csp unsafe.Pointer + + // Pointer to the first (lowest address) of the stack. It must never be + // overwritten. It can be checked from time to time to see whether a stack + // overflow happened in the past. + canaryPtr *uintptr } // start creates and starts a new goroutine with the given function and arguments. @@ -63,12 +68,18 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) { s.args = args // Create a stack. - stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0))) + stack := runtime_alloc(stackSize, nil) + + // Set up the stack canary, a random number that should be checked when + // switching from the task back to the scheduler. The stack canary pointer + // points to the first word of the stack. If it has changed between now and + // the next stack switch, there was a stack overflow. + s.canaryPtr = (*uintptr)(stack) + *s.canaryPtr = stackCanary // Calculate stack base addresses. - s.asyncifysp = uintptr(unsafe.Pointer(&stack[0])) - s.csp = uintptr(unsafe.Pointer(&stack[0])) + uintptr(len(stack))*unsafe.Sizeof(uintptr(0)) - stack[0] = stackCanary + s.asyncifysp = unsafe.Add(stack, unsafe.Sizeof(uintptr(0))) + s.csp = unsafe.Add(stack, stackSize) } //go:linkname runqueuePushBack runtime.runqueuePushBack @@ -85,14 +96,11 @@ func Current() *Task { // Pause suspends the current task and returns to the scheduler. // This function may only be called when running on a goroutine stack, not when running on the system stack. func Pause() { - // This is mildly unsafe but this is also the only place we can do this. - if *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) != stackCanary { + if *currentTask.state.canaryPtr != stackCanary { runtimePanic("stack overflow") } currentTask.state.unwind() - - *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) = stackCanary } //export tinygo_unwind @@ -113,7 +121,7 @@ func (t *Task) Resume() { } currentTask = prevTask t.gcData.swap() - if t.state.asyncifysp > t.state.csp { + if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) { runtimePanic("stack overflow") } } diff --git a/src/internal/task/task_stack.go b/src/internal/task/task_stack.go index f566de041..551612425 100644 --- a/src/internal/task/task_stack.go +++ b/src/internal/task/task_stack.go @@ -104,9 +104,6 @@ var startTask [0]uint8 //go:linkname runqueuePushBack runtime.runqueuePushBack func runqueuePushBack(*Task) -//go:linkname runtime_alloc runtime.alloc -func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer - // start creates and starts a new goroutine with the given function and arguments. // The new goroutine is scheduled to run later. func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) { diff --git a/targets/wasip1.json b/targets/wasip1.json index 409be9ec9..8d1966e78 100644 --- a/targets/wasip1.json +++ b/targets/wasip1.json @@ -8,6 +8,7 @@ "linker": "wasm-ld", "libc": "wasi-libc", "rtlib": "compiler-rt", + "gc": "precise", "scheduler": "asyncify", "default-stack-size": 65536, "cflags": [ diff --git a/targets/wasip2.json b/targets/wasip2.json index 4b0e67591..7c8394c8e 100644 --- a/targets/wasip2.json +++ b/targets/wasip2.json @@ -9,6 +9,7 @@ "linker": "wasm-ld", "libc": "wasmbuiltins", "rtlib": "compiler-rt", + "gc": "precise", "scheduler": "asyncify", "default-stack-size": 65536, "cflags": [ diff --git a/targets/wasm.json b/targets/wasm.json index 050ee105e..48a678891 100644 --- a/targets/wasm.json +++ b/targets/wasm.json @@ -8,6 +8,7 @@ "linker": "wasm-ld", "libc": "wasi-libc", "rtlib": "compiler-rt", + "gc": "precise", "scheduler": "asyncify", "default-stack-size": 65536, "cflags": [ |