Skip to content

Commit 0f95b41

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 24c11d4 commit 0f95b41

File tree

6 files changed

+25
-14
lines changed

6 files changed

+25
-14
lines changed

src/internal/task/task.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ type Task struct {
3030
// the given function and falls back to the default stack size. It is replaced
3131
// with a load from a special section just before codegen.
3232
func getGoroutineStackSize(fn uintptr) uintptr
33+
34+
//go:linkname runtime_alloc runtime.alloc
35+
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer

src/internal/task/task_asyncify.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,16 @@ type state struct {
3535
type stackState struct {
3636
// asyncify is the stack pointer of the asyncify stack.
3737
// This starts from the bottom and grows upwards.
38-
asyncifysp uintptr
38+
asyncifysp unsafe.Pointer
3939

4040
// asyncify is stack pointer of the C stack.
4141
// This starts from the top and grows downwards.
42-
csp uintptr
42+
csp unsafe.Pointer
43+
44+
// Pointer to the first (lowest address) of the stack. It must never be
45+
// overwritten. It can be checked from time to time to see whether a stack
46+
// overflow happened in the past.
47+
canaryPtr *uintptr
4348
}
4449

4550
// 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) {
6368
s.args = args
6469

6570
// Create a stack.
66-
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
71+
stack := runtime_alloc(stackSize, nil)
72+
73+
// Set up the stack canary, a random number that should be checked when
74+
// switching from the task back to the scheduler. The stack canary pointer
75+
// points to the first word of the stack. If it has changed between now and
76+
// the next stack switch, there was a stack overflow.
77+
s.canaryPtr = (*uintptr)(stack)
78+
*s.canaryPtr = stackCanary
6779

6880
// Calculate stack base addresses.
69-
s.asyncifysp = uintptr(unsafe.Pointer(&stack[0]))
70-
s.csp = uintptr(unsafe.Pointer(&stack[0])) + uintptr(len(stack))*unsafe.Sizeof(uintptr(0))
71-
stack[0] = stackCanary
81+
s.asyncifysp = unsafe.Add(stack, unsafe.Sizeof(uintptr(0)))
82+
s.csp = unsafe.Add(stack, stackSize)
7283
}
7384

7485
//go:linkname runqueuePushBack runtime.runqueuePushBack
@@ -85,14 +96,11 @@ func Current() *Task {
8596
// Pause suspends the current task and returns to the scheduler.
8697
// This function may only be called when running on a goroutine stack, not when running on the system stack.
8798
func Pause() {
88-
// This is mildly unsafe but this is also the only place we can do this.
89-
if *(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) != stackCanary {
99+
if *currentTask.state.canaryPtr != stackCanary {
90100
runtimePanic("stack overflow")
91101
}
92102

93103
currentTask.state.unwind()
94-
95-
*(*uintptr)(unsafe.Pointer(currentTask.state.asyncifysp)) = stackCanary
96104
}
97105

98106
//export tinygo_unwind
@@ -113,7 +121,7 @@ func (t *Task) Resume() {
113121
}
114122
currentTask = prevTask
115123
t.gcData.swap()
116-
if t.state.asyncifysp > t.state.csp {
124+
if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) {
117125
runtimePanic("stack overflow")
118126
}
119127
}

src/internal/task/task_stack.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,6 @@ var startTask [0]uint8
104104
//go:linkname runqueuePushBack runtime.runqueuePushBack
105105
func runqueuePushBack(*Task)
106106

107-
//go:linkname runtime_alloc runtime.alloc
108-
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
109-
110107
// start creates and starts a new goroutine with the given function and arguments.
111108
// The new goroutine is scheduled to run later.
112109
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {

targets/wasip1.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"linker": "wasm-ld",
99
"libc": "wasi-libc",
1010
"rtlib": "compiler-rt",
11+
"gc": "precise",
1112
"scheduler": "asyncify",
1213
"default-stack-size": 65536,
1314
"cflags": [

targets/wasip2.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"linker": "wasm-ld",
1010
"libc": "wasmbuiltins",
1111
"rtlib": "compiler-rt",
12+
"gc": "precise",
1213
"scheduler": "asyncify",
1314
"default-stack-size": 65536,
1415
"cflags": [

targets/wasm.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"linker": "wasm-ld",
99
"libc": "wasi-libc",
1010
"rtlib": "compiler-rt",
11+
"gc": "precise",
1112
"scheduler": "asyncify",
1213
"default-stack-size": 65536,
1314
"cflags": [

0 commit comments

Comments
 (0)