aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/runtime
diff options
context:
space:
mode:
authorJaden Weiss <[email protected]>2020-04-05 20:50:23 -0400
committerRon Evans <[email protected]>2020-04-09 14:13:10 +0200
commit4a6fba7e3a0228b75a6bac2b1e1e08eab056b5d8 (patch)
tree058d7b5a74750a46589689b3059c8620528d563f /src/runtime
parent012c4a02c996ad20671004e1a3a917f7911156c5 (diff)
downloadtinygo-4a6fba7e3a0228b75a6bac2b1e1e08eab056b5d8.tar.gz
tinygo-4a6fba7e3a0228b75a6bac2b1e1e08eab056b5d8.zip
runtime (gc): remove recursion from "conservative" GC
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/gc_conservative.go112
1 files changed, 108 insertions, 4 deletions
diff --git a/src/runtime/gc_conservative.go b/src/runtime/gc_conservative.go
index 319e68b55..d77c1f2a9 100644
--- a/src/runtime/gc_conservative.go
+++ b/src/runtime/gc_conservative.go
@@ -45,6 +45,7 @@ const (
bytesPerBlock = wordsPerBlock * unsafe.Sizeof(heapStart)
stateBits = 2 // how many bits a block state takes (see blockState type)
blocksPerStateByte = 8 / stateBits
+ markStackSize = 4 * unsafe.Sizeof((*int)(nil)) // number of to-be-marked blocks to queue before forcing a rescan
)
var (
@@ -324,6 +325,112 @@ func markRoots(start, end uintptr) {
}
}
+// mark a gcBlock and all of its children.
+// The specified block must have previously been unmarked, and must be a head.
+func mark(root gcBlock) {
+ var stack [markStackSize]gcBlock
+ stack[0] = root
+ root.setState(blockStateMark)
+ stackTop := 1
+ stackOverflow := false
+ rescanBlock := endBlock
+ for {
+ var block gcBlock
+ switch {
+ case stackTop > 0:
+ // Pop a block off of the stack.
+ stackTop--
+ block = stack[stackTop]
+ if gcDebug {
+ println("stack popped, remaining stack:", stackTop)
+ }
+ case rescanBlock < endBlock:
+ // Select the next marked block to rescan.
+ if rescanBlock.state() != blockStateMark {
+ // This block is not the head of a marked block, so skip it.
+ rescanBlock++
+ continue
+ }
+ block = rescanBlock
+ rescanBlock++
+ if gcDebug {
+ println("rescanning block")
+ }
+ case stackOverflow:
+ // Start a new rescan.
+ if gcDebug {
+ println("start rescan")
+ }
+ rescanBlock = 0
+ stackOverflow = false
+ continue
+ default:
+ // Done.
+ if gcDebug {
+ println("mark done")
+ }
+ return
+ }
+ if gcDebug {
+ println("scan block:", block)
+ }
+
+ // Scan all pointers inside the block.
+ start, end := block.address(), block.findNext().address()
+ for addr := start; addr != end; addr += unsafe.Alignof(addr) {
+ // Load the word.
+ word := *(*uintptr)(unsafe.Pointer(addr))
+
+ if !looksLikePointer(word) {
+ // Not a heap pointer.
+ continue
+ }
+
+ // Find the corresponding memory block.
+ block := blockFromAddr(word)
+
+ if block.state() == blockStateFree {
+ // The to-be-marked object doesn't actually exist.
+ // This is probbably a false positive.
+ if gcDebug {
+ println("found reference to free memory:", word, "at:", addr)
+ }
+ continue
+ }
+
+ // Move to the block's head.
+ block = block.findHead()
+
+ if block.state() == blockStateMark {
+ // The block has already been marked by something else.
+ continue
+ }
+
+ if stackTop == len(stack) {
+ // The stack is full.
+ // It is necessary to rescan all marked blocks once we are done.
+ // There is also no point in trying to scan any more pointers in this block, since they will all end up back here.
+ stackOverflow = true
+ if gcDebug {
+ println("gc stack overflowed")
+ }
+ break
+ }
+
+ // Mark block.
+ if gcDebug {
+ println("marking block:", block)
+ }
+ block.setState(blockStateMark)
+
+ // Push the pointer onto the stack to be scanned later.
+ stack[stackTop] = block
+ stackTop++
+ }
+ }
+}
+
+// mark a GC root at the address addr.
func markRoot(addr, root uintptr) {
if looksLikePointer(root) {
block := blockFromAddr(root)
@@ -338,10 +445,7 @@ func markRoot(addr, root uintptr) {
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())
+ mark(head)
}
}
}