tinygo/src/runtime/gc_stack_portable.go
Ayke van Laethem 655075e5e0 runtime: implement precise GC
This implements the block-based GC as a partially precise GC. This means
that for most heap allocations it is known which words contain a pointer
and which don't. This should in theory make the GC faster (because it
can skip non-pointer object) and have fewer false positives in a GC
cycle. It does however use a bit more RAM to store the layout of each
object.

Right now this GC seems to be slower than the conservative GC, but
should be less likely to run out of memory as a result of false
positives.
2023-01-17 19:32:18 +01:00

62 строки
2,3 КиБ
Go

//go:build (gc.conservative || 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 unsafe.Pointer)
// swapStackChain swaps the stack chain.
// This is called from internal/task when switching goroutines.
func swapStackChain(dst **stackChainObject) {
*dst, stackChainStart = stackChainStart, *dst
}