github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/gc_stack_portable.go (about) 1 //go:build (gc.conservative || gc.custom || gc.precise) && tinygo.wasm 2 3 package runtime 4 5 import ( 6 "internal/task" 7 "runtime/volatile" 8 "unsafe" 9 ) 10 11 //go:extern runtime.stackChainStart 12 var stackChainStart *stackChainObject 13 14 type stackChainObject struct { 15 parent *stackChainObject 16 numSlots uintptr 17 } 18 19 // markStack marks all root pointers found on the stack. 20 // 21 // - Goroutine stacks are heap allocated and always reachable in some way 22 // (for example through internal/task.currentTask) so they will always be 23 // scanned. 24 // - The system stack (aka startup stack) is not heap allocated, so even 25 // though it may be referenced it will not be scanned by default. 26 // 27 // Therefore, we only need to scan the system stack. 28 // It is relatively easy to scan the system stack while we're on it: we can 29 // simply read __stack_pointer and __global_base and scan the area inbetween. 30 // Unfortunately, it's hard to get the system stack pointer while we're on a 31 // goroutine stack. But when we're on a goroutine stack, the system stack is in 32 // the scheduler which means there shouldn't be anything on the system stack 33 // anyway. 34 // ...I hope this assumption holds, otherwise we will need to store the system 35 // stack in a global or something. 36 // 37 // The compiler also inserts code to store all globals in a chain via 38 // stackChainStart. Luckily we don't need to scan these, as these globals are 39 // stored on the goroutine stack and are therefore already getting scanned. 40 func markStack() { 41 // Hack to force LLVM to consider stackChainStart to be live. 42 // Without this hack, loads and stores may be considered dead and objects on 43 // the stack might not be correctly tracked. With this volatile load, LLVM 44 // is forced to consider stackChainStart (and everything it points to) as 45 // live. 46 volatile.LoadUint32((*uint32)(unsafe.Pointer(&stackChainStart))) 47 48 if task.OnSystemStack() { 49 markRoots(getCurrentStackPointer(), stackTop) 50 } 51 } 52 53 // trackPointer is a stub function call inserted by the compiler during IR 54 // construction. Calls to it are later replaced with regular stack bookkeeping 55 // code. 56 func trackPointer(ptr, alloca unsafe.Pointer) 57 58 // swapStackChain swaps the stack chain. 59 // This is called from internal/task when switching goroutines. 60 func swapStackChain(dst **stackChainObject) { 61 *dst, stackChainStart = stackChainStart, *dst 62 }