diff --git a/compiler/testdata/channel.ll b/compiler/testdata/channel.ll index 36d7fd96..3f4d5235 100644 --- a/compiler/testdata/channel.ll +++ b/compiler/testdata/channel.ll @@ -5,7 +5,8 @@ target triple = "wasm32-unknown-wasi" %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" } +%"internal/task.gcData" = type { i8* } %"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 } %"internal/task.stackState" = type { i32, i32 } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll index d34a1eb4..6224f256 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll @@ -5,7 +5,8 @@ target triple = "thumbv7m-unknown-unknown-eabi" %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" } +%"internal/task.gcData" = type {} %"internal/task.state" = type { i32, i32* } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/compiler/testdata/goroutine-wasm-asyncify.ll b/compiler/testdata/goroutine-wasm-asyncify.ll index 1ecf0c79..70ec4ef4 100644 --- a/compiler/testdata/goroutine-wasm-asyncify.ll +++ b/compiler/testdata/goroutine-wasm-asyncify.ll @@ -5,7 +5,8 @@ target triple = "wasm32-unknown-wasi" %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" } +%"internal/task.gcData" = type { i8* } %"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 } %"internal/task.stackState" = type { i32, i32 } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/compiler/testdata/goroutine-wasm-coroutines.ll b/compiler/testdata/goroutine-wasm-coroutines.ll index 6f308247..a0c42991 100644 --- a/compiler/testdata/goroutine-wasm-coroutines.ll +++ b/compiler/testdata/goroutine-wasm-coroutines.ll @@ -6,7 +6,8 @@ target triple = "wasm32-unknown-wasi" %runtime.funcValueWithSignature = type { i32, i8* } %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" } +%"internal/task.gcData" = type {} %"internal/task.state" = type { i8* } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/src/internal/task/gc_stack_chain.go b/src/internal/task/gc_stack_chain.go new file mode 100644 index 00000000..d3e400d3 --- /dev/null +++ b/src/internal/task/gc_stack_chain.go @@ -0,0 +1,19 @@ +//go:build (gc.conservative || gc.extalloc) && tinygo.wasm && !scheduler.coroutines +// +build gc.conservative gc.extalloc +// +build tinygo.wasm +// +build !scheduler.coroutines + +package task + +import "unsafe" + +//go:linkname swapStackChain runtime.swapStackChain +func swapStackChain(dst *unsafe.Pointer) + +type gcData struct { + stackChain unsafe.Pointer +} + +func (gcd *gcData) swap() { + swapStackChain(&gcd.stackChain) +} diff --git a/src/internal/task/gc_stack_noop.go b/src/internal/task/gc_stack_noop.go new file mode 100644 index 00000000..63674805 --- /dev/null +++ b/src/internal/task/gc_stack_noop.go @@ -0,0 +1,9 @@ +//go:build (!gc.conservative && !gc.extalloc) || !tinygo.wasm || scheduler.coroutines +// +build !gc.conservative,!gc.extalloc !tinygo.wasm scheduler.coroutines + +package task + +type gcData struct{} + +func (gcd *gcData) swap() { +} diff --git a/src/internal/task/task.go b/src/internal/task/task.go index bad501b6..b490a202 100644 --- a/src/internal/task/task.go +++ b/src/internal/task/task.go @@ -15,6 +15,9 @@ type Task struct { // Data is a field which can be used for storing state information. Data uint64 + // gcData holds data for the GC. + gcData gcData + // state is the underlying running state of the task. state state } diff --git a/src/internal/task/task_asyncify.go b/src/internal/task/task_asyncify.go index d67f0e1c..939008bc 100644 --- a/src/internal/task/task_asyncify.go +++ b/src/internal/task/task_asyncify.go @@ -104,6 +104,7 @@ func (*stackState) unwind() func (t *Task) Resume() { // The current task must be saved and restored because this can nest on WASM with JS. prevTask := currentTask + t.gcData.swap() currentTask = t if !t.state.launched { t.state.launch() @@ -112,6 +113,7 @@ func (t *Task) Resume() { t.state.rewind() } currentTask = prevTask + t.gcData.swap() if t.state.asyncifysp > t.state.csp { runtimePanic("stack overflow") } diff --git a/src/internal/task/task_stack.go b/src/internal/task/task_stack.go index a703d10a..59af6503 100644 --- a/src/internal/task/task_stack.go +++ b/src/internal/task/task_stack.go @@ -1,3 +1,4 @@ +//go:build scheduler.tasks // +build scheduler.tasks package task @@ -54,7 +55,9 @@ func pause() { // This may only be called from the scheduler. func (t *Task) Resume() { currentTask = t + t.gcData.swap() t.state.resume() + t.gcData.swap() currentTask = nil } diff --git a/src/runtime/gc_stack_portable.go b/src/runtime/gc_stack_portable.go index d4a04637..1cdd31f3 100644 --- a/src/runtime/gc_stack_portable.go +++ b/src/runtime/gc_stack_portable.go @@ -1,3 +1,4 @@ +//go:build (gc.conservative || gc.extalloc) && tinygo.wasm // +build gc.conservative gc.extalloc // +build tinygo.wasm @@ -37,3 +38,9 @@ func markStack() { // construction. Calls to it are later replaced with regular stack bookkeeping // code. func trackPointer(ptr unsafe.Pointer) + +// swapStackChain swaps the stack chain. +// This is called from internal/task when switching goroutines. +func swapStackChain(dst **stackChainObject) { + *dst, stackChainStart = stackChainStart, *dst +}