Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 108 additions & 4 deletions src/runtime/gc_conservative.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@aykevl aykevl Apr 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This number seems arbitrary but I guess it's reasonable (without data it's hard to say anything about it).

Copy link
Member Author

@niaow niaow Apr 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically had decided that it would be reasonable to spend 64 bytes on ARM, and then decided to reduce that to 16 on AVR. I just went with 4*(wordWidth^2) bytes spent on the stack, since 256 bytes also seemed fine for AMD64 where there is plenty of memory.

)

var (
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
}
}
Expand Down