Skip to content

Commit 6a9c674

Browse files
committed
runtime: redo heap bitmap
[this is a retry of CL 407035 + its revert CL 422395. The content is unchanged] Use just 1 bit per word to record the ptr/nonptr bitmap. Use word-sized operations to manipulate the bitmap, so we can operate on up to 64 ptr/nonptr bits at a time. Use a separate bitmap, one bit per word of the ptr/nonptr bitmap, to encode a no-more-pointers signal. Since we can check 64 ptr/nonptr bits at once, knowing the exact last pointer location is not necessary. As a followon CL, we should make the gcdata bitmap an array of uintptr instead of an array of byte, so we can load 64 bits of it at once. Similarly for the processing of gc programs. Change-Id: Ica5eb622f5b87e647be64f471d67b02732ef8be6 Reviewed-on: https://go-review.googlesource.com/c/go/+/422634 Reviewed-by: Michael Knyszek <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Keith Randall <[email protected]>
1 parent a557938 commit 6a9c674

File tree

12 files changed

+520
-1116
lines changed

12 files changed

+520
-1116
lines changed

src/cmd/compile/internal/test/inl_test.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,7 @@ func TestIntendedInlining(t *testing.T) {
7272
"cgoInRange",
7373
"gclinkptr.ptr",
7474
"guintptr.ptr",
75-
"heapBits.bits",
76-
"heapBits.isPointer",
77-
"heapBits.morePointers",
78-
"heapBits.next",
79-
"heapBitsForAddr",
75+
"writeHeapBitsForAddr",
8076
"markBits.isMarked",
8177
"muintptr.ptr",
8278
"puintptr.ptr",
@@ -224,6 +220,8 @@ func TestIntendedInlining(t *testing.T) {
224220
// On loong64, mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive
225221
// to inline (Issue 22239).
226222
want["runtime"] = append(want["runtime"], "nextFreeFast")
223+
// Same behavior for heapBits.nextFast.
224+
want["runtime"] = append(want["runtime"], "heapBits.nextFast")
227225
}
228226
if runtime.GOARCH != "386" {
229227
// As explained above, Ctz64 and Ctz32 are not Go code on 386.

src/reflect/all_test.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6989,8 +6989,21 @@ func TestFuncLayout(t *testing.T) {
69896989
}
69906990
}
69916991

6992+
// trimBitmap removes trailing 0 elements from b and returns the result.
6993+
func trimBitmap(b []byte) []byte {
6994+
for len(b) > 0 && b[len(b)-1] == 0 {
6995+
b = b[:len(b)-1]
6996+
}
6997+
return b
6998+
}
6999+
69927000
func verifyGCBits(t *testing.T, typ Type, bits []byte) {
69937001
heapBits := GCBits(New(typ).Interface())
7002+
7003+
// Trim scalars at the end, as bits might end in zero,
7004+
// e.g. with rep(2, lit(1, 0)).
7005+
bits = trimBitmap(bits)
7006+
69947007
if !bytes.Equal(heapBits, bits) {
69957008
_, _, line, _ := runtime.Caller(1)
69967009
t.Errorf("line %d: heapBits incorrect for %v\nhave %v\nwant %v", line, typ, heapBits, bits)
@@ -7007,12 +7020,10 @@ func verifyGCBitsSlice(t *testing.T, typ Type, cap int, bits []byte) {
70077020
heapBits := GCBits(data.Interface())
70087021
// Repeat the bitmap for the slice size, trimming scalars in
70097022
// the last element.
7010-
bits = rep(cap, bits)
7011-
for len(bits) > 0 && bits[len(bits)-1] == 0 {
7012-
bits = bits[:len(bits)-1]
7013-
}
7023+
bits = trimBitmap(rep(cap, bits))
70147024
if !bytes.Equal(heapBits, bits) {
7015-
t.Errorf("heapBits incorrect for make(%v, 0, %v)\nhave %v\nwant %v", typ, cap, heapBits, bits)
7025+
_, _, line, _ := runtime.Caller(1)
7026+
t.Errorf("line %d: heapBits incorrect for make(%v, 0, %v)\nhave %v\nwant %v", line, typ, cap, heapBits, bits)
70167027
}
70177028
}
70187029

src/runtime/cgocall.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -568,17 +568,16 @@ func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
568568
if base == 0 {
569569
return
570570
}
571-
hbits := heapBitsForAddr(base)
572571
n := span.elemsize
573-
for i = uintptr(0); i < n; i += goarch.PtrSize {
574-
if !hbits.morePointers() {
575-
// No more possible pointers.
572+
hbits := heapBitsForAddr(base, n)
573+
for {
574+
var addr uintptr
575+
if hbits, addr = hbits.next(); addr == 0 {
576576
break
577577
}
578-
if hbits.isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) {
578+
if cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(addr))) {
579579
panic(errorString(msg))
580580
}
581-
hbits = hbits.next()
582581
}
583582

584583
return

src/runtime/cgocheck.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,16 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
153153

154154
// src must be in the regular heap.
155155

156-
hbits := heapBitsForAddr(uintptr(src))
157-
for i := uintptr(0); i < off+size; i += goarch.PtrSize {
158-
bits := hbits.bits()
159-
if i >= off && bits&bitPointer != 0 {
160-
v := *(*unsafe.Pointer)(add(src, i))
161-
if cgoIsGoPointer(v) {
162-
throw(cgoWriteBarrierFail)
163-
}
156+
hbits := heapBitsForAddr(uintptr(src), size)
157+
for {
158+
var addr uintptr
159+
if hbits, addr = hbits.next(); addr == 0 {
160+
break
161+
}
162+
v := *(*unsafe.Pointer)(unsafe.Pointer(addr))
163+
if cgoIsGoPointer(v) {
164+
throw(cgoWriteBarrierFail)
164165
}
165-
hbits = hbits.next()
166166
}
167167
}
168168

src/runtime/heapdump.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -737,16 +737,16 @@ func makeheapobjbv(p uintptr, size uintptr) bitvector {
737737
for i := uintptr(0); i < nptr/8+1; i++ {
738738
tmpbuf[i] = 0
739739
}
740-
i := uintptr(0)
741-
hbits := heapBitsForAddr(p)
742-
for ; i < nptr; i++ {
743-
if !hbits.morePointers() {
744-
break // end of object
745-
}
746-
if hbits.isPointer() {
747-
tmpbuf[i/8] |= 1 << (i % 8)
740+
741+
hbits := heapBitsForAddr(p, size)
742+
for {
743+
var addr uintptr
744+
hbits, addr = hbits.next()
745+
if addr == 0 {
746+
break
748747
}
749-
hbits = hbits.next()
748+
i := (addr - p) / goarch.PtrSize
749+
tmpbuf[i/8] |= 1 << (i % 8)
750750
}
751-
return bitvector{int32(i), &tmpbuf[0]}
751+
return bitvector{int32(nptr), &tmpbuf[0]}
752752
}

src/runtime/malloc.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,15 @@ const (
247247
// memory.
248248
heapArenaBytes = 1 << logHeapArenaBytes
249249

250+
heapArenaWords = heapArenaBytes / goarch.PtrSize
251+
250252
// logHeapArenaBytes is log_2 of heapArenaBytes. For clarity,
251253
// prefer using heapArenaBytes where possible (we need the
252254
// constant to compute some other constants).
253255
logHeapArenaBytes = (6+20)*(_64bit*(1-goos.IsWindows)*(1-goarch.IsWasm)*(1-goos.IsIos*goarch.IsArm64)) + (2+20)*(_64bit*goos.IsWindows) + (2+20)*(1-_64bit) + (2+20)*goarch.IsWasm + (2+20)*goos.IsIos*goarch.IsArm64
254256

255-
// heapArenaBitmapBytes is the size of each heap arena's bitmap.
256-
heapArenaBitmapBytes = heapArenaBytes / (goarch.PtrSize * 8 / 2)
257+
// heapArenaBitmapWords is the size of each heap arena's bitmap in uintptrs.
258+
heapArenaBitmapWords = heapArenaWords / (8 * goarch.PtrSize)
257259

258260
pagesPerArena = heapArenaBytes / pageSize
259261

@@ -353,10 +355,10 @@ func mallocinit() {
353355
throw("bad TinySizeClass")
354356
}
355357

356-
if heapArenaBitmapBytes&(heapArenaBitmapBytes-1) != 0 {
358+
if heapArenaBitmapWords&(heapArenaBitmapWords-1) != 0 {
357359
// heapBits expects modular arithmetic on bitmap
358360
// addresses to work.
359-
throw("heapArenaBitmapBytes not a power of 2")
361+
throw("heapArenaBitmapWords not a power of 2")
360362
}
361363

362364
// Check physPageSize.

0 commit comments

Comments
 (0)