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  }