Commit c3c047a6 authored by Russ Cox's avatar Russ Cox

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: default avatarAustin Clements <austin@google.com>
parent 8fa14ea8
...@@ -150,3 +150,5 @@ func BenchSetType(n int, x interface{}) { ...@@ -150,3 +150,5 @@ func BenchSetType(n int, x interface{}) {
} }
}) })
} }
const PtrSize = ptrSize
...@@ -13,11 +13,11 @@ import ( ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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{} { ...@@ -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() { ...@@ -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 ( ...@@ -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}
......
...@@ -583,7 +583,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { ...@@ -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) { ...@@ -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
} }
......
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