Commit 0234dfd4 authored by Russ Cox's avatar Russ Cox

runtime: use 2-bit heap bitmap (in place of 4-bit)

Previous CLs changed the representation of the non-heap type bitmaps
to be 1-bit bitmaps (pointer or not). Before this CL, the heap bitmap
stored a 2-bit type for each word and a mark bit and checkmark bit
for the first word of the object. (There used to be additional per-word bits.)

Reduce heap bitmap to 2-bit, with 1 dedicated to pointer or not,
and the other used for mark, checkmark, and "keep scanning forward
to find pointers in this object." See comments for details.

This CL replaces heapBitsSetType with very slow but obviously correct code.
A followup CL will optimize it. (Spoiler: the new code is faster than Go 1.4 was.)

Change-Id: I999577a133f3cfecacebdec9cdc3573c235c7fb9
Reviewed-on: https://go-review.googlesource.com/9703Reviewed-by: 's avatarRick Hudson <rlh@golang.org>
Reviewed-by: 's avatarAustin Clements <austin@google.com>
parent 6d8a147b
......@@ -10,6 +10,12 @@ import (
"testing"
)
const (
typeScalar = 0
typePointer = 1
typeDead = 255
)
// TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
......@@ -37,7 +43,9 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "stack iface", new(Iface), nonStackInfo(infoIface))
for i := 0; i < 10; i++ {
verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), infoPtr10)
verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr)
verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), infoScalarPtr4)
verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), infoPtrScalar)
verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), infoBigStruct())
verifyGCInfo(t, "heap string", escape(new(string)), infoString)
......@@ -78,18 +86,7 @@ func escape(p interface{}) interface{} {
return p
}
const (
typeDead = iota
typeScalar
typePointer
)
const (
BitsString = iota // unused
BitsSlice // unused
BitsIface
BitsEface
)
var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
type ScalarPtr struct {
q int
......@@ -102,6 +99,8 @@ type ScalarPtr struct {
var infoScalarPtr = []byte{typeScalar, typePointer, typeScalar, typePointer, typeScalar, typePointer}
var infoScalarPtr4 = append(append(append(append([]byte(nil), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...)
type PtrScalar struct {
q *int
w int
......
......@@ -730,14 +730,13 @@ func makeheapobjbv(p uintptr, size uintptr) bitvector {
i := uintptr(0)
hbits := heapBitsForAddr(p)
for ; i < nptr; i++ {
bits := hbits.typeBits()
if bits == typeDead {
if i >= 2 && !hbits.isMarked() {
break // end of object
}
hbits = hbits.next()
if bits == typePointer {
if hbits.isPointer() {
tmpbuf[i/8] |= 1 << (i % 8)
}
hbits = hbits.next()
}
return bitvector{int32(i), &tmpbuf[0]}
}
This diff is collapsed.
......@@ -597,20 +597,19 @@ func scanobject(b uintptr, gcw *gcWork) {
// Avoid needless hbits.next() on last iteration.
hbits = hbits.next()
}
bits := uintptr(hbits.typeBits())
if bits == typeDead {
break // no more pointers in this object
}
if bits <= typeScalar { // typeScalar, typeDead, typeScalarMarked
continue
}
if bits&typePointer != typePointer {
print("gc useCheckmark=", useCheckmark, " b=", hex(b), "\n")
throw("unexpected garbage collection bits")
// During checkmarking, 1-word objects store the checkmark
// in the type bit for the one word. The only one-word objects
// are pointers, or else they'd be merged with other non-pointer
// data into larger allocations.
if n != 1 {
b := hbits.bits()
if i >= 2*ptrSize && b&bitMarked == 0 {
break // no more pointers in this object
}
if b&bitPointer == 0 {
continue // not a pointer
}
}
// Work here is duplicated in scanblock.
// If you make changes here, make changes there too.
......@@ -673,11 +672,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
throw("checkmark found unmarked object")
}
if hbits.isCheckmarked() {
if hbits.isCheckmarked(span.elemsize) {
return
}
hbits.setCheckmarked()
if !hbits.isCheckmarked() {
hbits.setCheckmarked(span.elemsize)
if !hbits.isCheckmarked(span.elemsize) {
throw("setCheckmarked and isCheckmarked disagree")
}
} else {
......@@ -685,12 +684,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
if hbits.isMarked() {
return
}
hbits.setMarked()
// If this is a noscan object, fast-track it to black
// instead of greying it.
if hbits.typeBits() == typeDead {
if !hbits.hasPointers(span.elemsize) {
gcw.bytesMarked += uint64(span.elemsize)
return
}
......
......@@ -352,6 +352,12 @@ func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) {
}
}
// Information from the compiler about the layout of stack frames.
type bitvector struct {
n int32 // # of bits
bytedata *uint8
}
type gobitvector struct {
n uintptr
bytedata []uint8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment