blob: d35e16e30c3112c1b7e3ab751d21241ec967679e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
//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 in between.
// 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
}
|