62 строки
2,3 КиБ
Go
62 строки
2,3 КиБ
Go
//go:build (gc.conservative || gc.custom || gc.precise) && tinygo.wasm
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"internal/task"
|
|
"runtime/volatile"
|
|
"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() {
|
|
// Hack to force LLVM to consider stackChainStart to be live.
|
|
// Without this hack, loads and stores may be considered dead and objects on
|
|
// the stack might not be correctly tracked. With this volatile load, LLVM
|
|
// is forced to consider stackChainStart (and everything it points to) as
|
|
// live.
|
|
volatile.LoadUint32((*uint32)(unsafe.Pointer(&stackChainStart)))
|
|
|
|
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, alloca unsafe.Pointer)
|
|
|
|
// swapStackChain swaps the stack chain.
|
|
// This is called from internal/task when switching goroutines.
|
|
func swapStackChain(dst **stackChainObject) {
|
|
*dst, stackChainStart = stackChainStart, *dst
|
|
}
|