1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:14:45 -07:00

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 (
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 Ptr", &bssPtr, infoPtr)
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct())
@ -26,6 +26,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss eface", &bssEface, infoEface)
verifyGCInfo(t, "bss iface", &bssIface, infoIface)
verifyGCInfo(t, "data Ptr", &dataPtr, infoPtr)
verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct())
@ -34,6 +35,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "data eface", &dataEface, infoEface)
verifyGCInfo(t, "data iface", &dataIface, infoIface)
verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
@ -43,6 +45,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "stack iface", new(Iface), infoIface)
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 ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr))
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 iface", escape(new(Iface)), trimDead(infoIface))
}
}
func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
mask := runtime.GCMask(p)
if len(mask) > len(mask0) {
mask0 = append(mask0, typeDead)
mask = mask[:len(mask0)]
}
if bytes.Compare(mask, mask0) != 0 {
t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask)
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 {
for len(mask) > 2 && mask[len(mask)-1] == typeScalar {
mask = mask[:len(mask)-1]
@ -81,6 +91,12 @@ func escape(p interface{}) interface{} {
return p
}
var infoPtr = []byte{typePointer}
type Ptr struct {
*byte
}
var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
type ScalarPtr struct {
@ -160,6 +176,7 @@ func (IfaceImpl) f() {
var (
// BSS
bssPtr Ptr
bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar
bssBigStruct BigStruct
@ -169,6 +186,7 @@ var (
bssIface Iface
// DATA
dataPtr = Ptr{new(byte)}
dataScalarPtr = ScalarPtr{q: 1}
dataPtrScalar = PtrScalar{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
// 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.
// In general we'd need an atomic update here if the
// 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).
if size == 2*ptrSize {
if typ.size == ptrSize {
// 2-element slice of pointer.
if gcphase == _GCoff {
*h.bitp |= (bitPointer | bitPointer<<heapBitsShift) << h.shift
// We're allocating a block big enough to hold two pointers.
// On 64-bit, that means the actual object must be two pointers,
// 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 {
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
}