runtime: test and fix heap bitmap for 1-pointer allocation on 32-bit system

Change-Id: Ic064fe7c6bd3304dcc8c3f7b3b5393870b5387c2
Reviewed-on: https://go-review.googlesource.com/10119
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
Russ Cox 2015-05-15 14:23:23 -04:00
parent 8fa14ea8b4
commit c3c047a6a3
3 changed files with 48 additions and 11 deletions

View File

@ -150,3 +150,5 @@ func BenchSetType(n int, x interface{}) {
} }
}) })
} }
const PtrSize = ptrSize

View File

@ -13,11 +13,11 @@ import (
const ( const (
typeScalar = 0 typeScalar = 0
typePointer = 1 typePointer = 1
typeDead = 255
) )
// TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info. // TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
func TestGCInfo(t *testing.T) { func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss Ptr", &bssPtr, infoPtr)
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr) verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar) verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct()) verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct())
@ -26,6 +26,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss eface", &bssEface, infoEface) verifyGCInfo(t, "bss eface", &bssEface, infoEface)
verifyGCInfo(t, "bss iface", &bssIface, infoIface) verifyGCInfo(t, "bss iface", &bssIface, infoIface)
verifyGCInfo(t, "data Ptr", &dataPtr, infoPtr)
verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr) verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar) verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct()) verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct())
@ -34,6 +35,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "data eface", &dataEface, infoEface) verifyGCInfo(t, "data eface", &dataEface, infoEface)
verifyGCInfo(t, "data iface", &dataIface, infoIface) verifyGCInfo(t, "data iface", &dataIface, infoIface)
verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr) verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar) verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct()) verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
@ -43,6 +45,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "stack iface", new(Iface), infoIface) verifyGCInfo(t, "stack iface", new(Iface), infoIface)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10)) verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr)) verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr))
verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4)) verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
@ -52,21 +55,28 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface)) verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface))
verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface)) verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface))
} }
} }
func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) { func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
mask := runtime.GCMask(p) mask := runtime.GCMask(p)
if len(mask) > len(mask0) {
mask0 = append(mask0, typeDead)
mask = mask[:len(mask0)]
}
if bytes.Compare(mask, mask0) != 0 { if bytes.Compare(mask, mask0) != 0 {
t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask) t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask)
return return
} }
} }
func padDead(mask []byte) []byte {
// Because the dead bit isn't encoded until the third word,
// and because on 32-bit systems a one-word allocation
// uses a two-word block, the pointer info for a one-word
// object needs to be expanded to include an extra scalar
// on 32-bit systems to match the heap bitmap.
if runtime.PtrSize == 4 && len(mask) == 1 {
return []byte{mask[0], 0}
}
return mask
}
func trimDead(mask []byte) []byte { func trimDead(mask []byte) []byte {
for len(mask) > 2 && mask[len(mask)-1] == typeScalar { for len(mask) > 2 && mask[len(mask)-1] == typeScalar {
mask = mask[:len(mask)-1] mask = mask[:len(mask)-1]
@ -81,6 +91,12 @@ func escape(p interface{}) interface{} {
return p return p
} }
var infoPtr = []byte{typePointer}
type Ptr struct {
*byte
}
var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer} var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
type ScalarPtr struct { type ScalarPtr struct {
@ -160,6 +176,7 @@ func (IfaceImpl) f() {
var ( var (
// BSS // BSS
bssPtr Ptr
bssScalarPtr ScalarPtr bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar bssPtrScalar PtrScalar
bssBigStruct BigStruct bssBigStruct BigStruct
@ -169,6 +186,7 @@ var (
bssIface Iface bssIface Iface
// DATA // DATA
dataPtr = Ptr{new(byte)}
dataScalarPtr = ScalarPtr{q: 1} dataScalarPtr = ScalarPtr{q: 1}
dataPtrScalar = PtrScalar{w: 1} dataPtrScalar = PtrScalar{w: 1}
dataBigStruct = BigStruct{w: 1} dataBigStruct = BigStruct{w: 1}

View File

@ -583,7 +583,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
// The checks for size == ptrSize and size == 2*ptrSize can therefore // The checks for size == ptrSize and size == 2*ptrSize can therefore
// assume that dataSize == size without checking it explicitly. // assume that dataSize == size without checking it explicitly.
if size == ptrSize { if ptrSize == 8 && size == ptrSize {
// It's one word and it has pointers, it must be a pointer. // It's one word and it has pointers, it must be a pointer.
// In general we'd need an atomic update here if the // In general we'd need an atomic update here if the
// concurrent GC were marking objects in this span, // concurrent GC were marking objects in this span,
@ -635,11 +635,28 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
// are 4-word aligned (because they're all 16-byte aligned). // are 4-word aligned (because they're all 16-byte aligned).
if size == 2*ptrSize { if size == 2*ptrSize {
if typ.size == ptrSize { if typ.size == ptrSize {
// 2-element slice of pointer. // We're allocating a block big enough to hold two pointers.
if gcphase == _GCoff { // On 64-bit, that means the actual object must be two pointers,
*h.bitp |= (bitPointer | bitPointer<<heapBitsShift) << h.shift // or else we'd have used the one-pointer-sized block.
// On 32-bit, however, this is the 8-byte block, the smallest one.
// So it could be that we're allocating one pointer and this was
// just the smallest block available. Distinguish by checking dataSize.
// (In general the number of instances of typ being allocated is
// dataSize/typ.size.)
if ptrSize == 4 && dataSize == ptrSize {
// 1 pointer.
if gcphase == _GCoff {
*h.bitp |= bitPointer << h.shift
} else {
atomicor8(h.bitp, bitPointer<<h.shift)
}
} else { } else {
atomicor8(h.bitp, (bitPointer|bitPointer<<heapBitsShift)<<h.shift) // 2-element slice of pointer.
if gcphase == _GCoff {
*h.bitp |= (bitPointer | bitPointer<<heapBitsShift) << h.shift
} else {
atomicor8(h.bitp, (bitPointer|bitPointer<<heapBitsShift)<<h.shift)
}
} }
return return
} }