mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
compiler,runtime: implement a portable conservative GC
This commit is contained in:
@@ -282,8 +282,8 @@ func GC() {
|
||||
}
|
||||
|
||||
// Mark phase: mark all reachable objects, recursively.
|
||||
markRoots(globalsStart, globalsEnd)
|
||||
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
||||
markGlobals()
|
||||
markStack()
|
||||
|
||||
// Sweep phase: free all non-marked objects and unmark marked objects for
|
||||
// the next collection cycle.
|
||||
@@ -311,18 +311,22 @@ func markRoots(start, end uintptr) {
|
||||
|
||||
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||
if looksLikePointer(root) {
|
||||
block := blockFromAddr(root)
|
||||
head := block.findHead()
|
||||
if head.state() != blockStateMark {
|
||||
if gcDebug {
|
||||
println("found unmarked pointer", root, "at address", addr)
|
||||
}
|
||||
head.setState(blockStateMark)
|
||||
next := block.findNext()
|
||||
// TODO: avoid recursion as much as possible
|
||||
markRoots(head.address(), next.address())
|
||||
markRoot(addr, root)
|
||||
}
|
||||
}
|
||||
|
||||
func markRoot(addr, root uintptr) {
|
||||
if looksLikePointer(root) {
|
||||
block := blockFromAddr(root)
|
||||
head := block.findHead()
|
||||
if head.state() != blockStateMark {
|
||||
if gcDebug {
|
||||
println("found unmarked pointer", root, "at address", addr)
|
||||
}
|
||||
head.setState(blockStateMark)
|
||||
next := block.findNext()
|
||||
// TODO: avoid recursion as much as possible
|
||||
markRoots(head.address(), next.address())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// +build gc.conservative
|
||||
// +build cortexm
|
||||
|
||||
package runtime
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation marks all globals conservatively and assumes it can use
|
||||
// linker-defined symbols for the start and end of the .data section.
|
||||
func markGlobals() {
|
||||
markRoots(globalsStart, globalsEnd)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// +build gc.conservative
|
||||
// +build !cortexm
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:extern runtime.trackedGlobalsStart
|
||||
var trackedGlobalsStart uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsLength
|
||||
var trackedGlobalsLength uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsBitmap
|
||||
var trackedGlobalsBitmap [0]uint8
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation relies on a compiler pass that stores all globals in a
|
||||
// single global (adjusting all uses of them accordingly) and creates a bit
|
||||
// vector with the locations of each pointer. This implementation then walks the
|
||||
// bit vector and for each pointer it indicates, it marks the root.
|
||||
//
|
||||
//go:nobounds
|
||||
func markGlobals() {
|
||||
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
||||
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||
markRoot(addr, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// +build gc.conservative
|
||||
// +build !cortexm
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:extern runtime.stackChainStart
|
||||
var stackChainStart *stackChainObject
|
||||
|
||||
type stackChainObject struct {
|
||||
parent *stackChainObject
|
||||
numSlots uintptr
|
||||
}
|
||||
|
||||
// markStack marks all root pointers found on the stack.
|
||||
//
|
||||
// This implementation is conservative and relies on the compiler inserting code
|
||||
// to manually push/pop stack objects that are stored in a linked list starting
|
||||
// with stackChainStart. Manually keeping track of stack values is _much_ more
|
||||
// expensive than letting the compiler do it and it inhibits a few important
|
||||
// optimizations, but it has the big advantage of being portable to basically
|
||||
// any ISA, including WebAssembly.
|
||||
func markStack() {
|
||||
stackObject := stackChainStart
|
||||
for stackObject != nil {
|
||||
start := uintptr(unsafe.Pointer(stackObject)) + unsafe.Sizeof(uintptr(0))*2
|
||||
end := start + stackObject.numSlots*unsafe.Alignof(uintptr(0))
|
||||
markRoots(start, end)
|
||||
stackObject = stackObject.parent
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -0,0 +1,13 @@
|
||||
// +build gc.conservative
|
||||
// +build cortexm
|
||||
|
||||
package runtime
|
||||
|
||||
// markStack marks all root pointers found on the stack.
|
||||
//
|
||||
// This implementation is conservative and relies on the stack top (provided by
|
||||
// the linker) and getting the current stack pointer from a register. Also, it
|
||||
// assumes a descending stack. Thus, it is not very portable.
|
||||
func markStack() {
|
||||
markRoots(getCurrentStackPointer(), stackTop)
|
||||
}
|
||||
Reference in New Issue
Block a user