mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 02:57:46 +00:00
65d65c1313
Scanning of allocas was entirely broken on WebAssembly. The code intended to do this was never run. There were also no tests. Looking into this further, I found that it is actually not really necessary to do that: the C stack can be scanned conservatively and in fact this was already done for goroutine stacks (because they live on the heap and are always referenced). It wasn't done for the system stack however. With these fixes, I believe code should be both faster *and* more correct. I found this in my work to get opaque pointers supported in LLVM 15, because the code that was never reached now finally got run and was actually quite buggy.
56 lines
1.9 KiB
Go
56 lines
1.9 KiB
Go
//go:build gc.conservative && tinygo.wasm
|
|
// +build gc.conservative,tinygo.wasm
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"internal/task"
|
|
"unsafe"
|
|
)
|
|
|
|
//go:extern runtime.stackChainStart
|
|
var stackChainStart *stackChainObject
|
|
|
|
type stackChainObject struct {
|
|
parent *stackChainObject
|
|
numSlots uintptr
|
|
}
|
|
|
|
// markStack marks all root pointers found on the stack.
|
|
//
|
|
// - Goroutine stacks are heap allocated and always reachable in some way
|
|
// (for example through internal/task.currentTask) so they will always be
|
|
// scanned.
|
|
// - The system stack (aka startup stack) is not heap allocated, so even
|
|
// though it may be referenced it will not be scanned by default.
|
|
//
|
|
// Therefore, we only need to scan the system stack.
|
|
// It is relatively easy to scan the system stack while we're on it: we can
|
|
// simply read __stack_pointer and __global_base and scan the area inbetween.
|
|
// Unfortunately, it's hard to get the system stack pointer while we're on a
|
|
// goroutine stack. But when we're on a goroutine stack, the system stack is in
|
|
// the scheduler which means there shouldn't be anything on the system stack
|
|
// anyway.
|
|
// ...I hope this assumption holds, otherwise we will need to store the system
|
|
// stack in a global or something.
|
|
//
|
|
// The compiler also inserts code to store all globals in a chain via
|
|
// stackChainStart. Luckily we don't need to scan these, as these globals are
|
|
// stored on the goroutine stack and are therefore already getting scanned.
|
|
func markStack() {
|
|
if task.OnSystemStack() {
|
|
markRoots(getCurrentStackPointer(), stackTop)
|
|
}
|
|
}
|
|
|
|
// trackPointer is a stub function call inserted by the compiler during IR
|
|
// 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
|
|
}
|