6
6
//
7
7
// Stack, data, and bss bitmaps
8
8
//
9
- // Stack frames and global variables in the data and bss sections are described
10
- // by 1-bit bitmaps in which 0 means uninteresting and 1 means live pointer
11
- // to be visited during GC. The bits in each byte are consumed starting with
12
- // the low bit: 1<<0, 1<<1, and so on.
9
+ // Stack frames and global variables in the data and bss sections are
10
+ // described by bitmaps with 1 bit per pointer-sized word. A "1" bit
11
+ // means the word is a live pointer to be visited by the GC (referred to
12
+ // as "pointer"). A "0" bit means the word should be ignored by GC
13
+ // (referred to as "scalar", though it could be a dead pointer value).
13
14
//
14
15
// Heap bitmap
15
16
//
20
21
// through start+3*ptrSize, ha.bitmap[1] holds the entries for
21
22
// start+4*ptrSize through start+7*ptrSize, and so on.
22
23
//
23
- // In each 2-bit entry, the lower bit holds the same information as in the 1-bit
24
- // bitmaps: 0 means uninteresting and 1 means live pointer to be visited during GC.
25
- // The meaning of the high bit depends on the position of the word being described
26
- // in its allocated object. In all words *except* the second word, the
27
- // high bit indicates that the object is still being described. In
28
- // these words, if a bit pair with a high bit 0 is encountered, the
29
- // low bit can also be assumed to be 0, and the object description is
30
- // over. This 00 is called the ``dead'' encoding: it signals that the
31
- // rest of the words in the object are uninteresting to the garbage
32
- // collector.
33
- //
34
- // In the second word, the high bit is the GC ``checkmarked'' bit (see below).
24
+ // In each 2-bit entry, the lower bit is a pointer/scalar bit, just
25
+ // like in the stack/data bitmaps described above. The upper bit
26
+ // indicates scan/dead: a "1" value ("scan") indicates that there may
27
+ // be pointers in later words of the allocation, and a "0" value
28
+ // ("dead") indicates there are no more pointers in the allocation. If
29
+ // the upper bit is 0, the lower bit must also be 0, and this
30
+ // indicates scanning can ignore the rest of the allocation.
35
31
//
36
32
// The 2-bit entries are split when written into the byte, so that the top half
37
33
// of the byte contains 4 high bits and the bottom half contains 4 low (pointer)
38
34
// bits.
39
35
// This form allows a copy from the 1-bit to the 4-bit form to keep the
40
36
// pointer bits contiguous, instead of having to space them out.
41
37
//
42
- // The code makes use of the fact that the zero value for a heap bitmap
43
- // has no live pointer bit set and is (depending on position), not used,
44
- // not checkmarked, and is the dead encoding.
45
- // These properties must be preserved when modifying the encoding.
38
+ // The code makes use of the fact that the zero value for a heap
39
+ // bitmap means scalar/dead. This property must be preserved when
40
+ // modifying the encoding.
46
41
//
47
42
// The bitmap for noscan spans is not maintained. Code must ensure
48
43
// that an object is scannable before consulting its bitmap by
49
44
// checking either the noscan bit in the span or by consulting its
50
45
// type's information.
51
- //
52
- // Checkmarks
53
- //
54
- // In a concurrent garbage collector, one worries about failing to mark
55
- // a live object due to mutations without write barriers or bugs in the
56
- // collector implementation. As a sanity check, the GC has a 'checkmark'
57
- // mode that retraverses the object graph with the world stopped, to make
58
- // sure that everything that should be marked is marked.
59
- // In checkmark mode, in the heap bitmap, the high bit of the 2-bit entry
60
- // for the second word of the object holds the checkmark bit.
61
- // When not in checkmark mode, this bit is set to 1.
62
- //
63
- // The smallest possible allocation is 8 bytes. On a 32-bit machine, that
64
- // means every allocated object has two words, so there is room for the
65
- // checkmark bit. On a 64-bit machine, however, the 8-byte allocation is
66
- // just one word, so the second bit pair is not available for encoding the
67
- // checkmark. However, because non-pointer allocations are combined
68
- // into larger 16-byte (maxTinySize) allocations, a plain 8-byte allocation
69
- // must be a pointer, so the type bit in the first word is not actually needed.
70
- // It is still used in general, except in checkmark the type bit is repurposed
71
- // as the checkmark bit and then reinitialized (to 1) as the type bit when
72
- // finished.
73
- //
74
46
75
47
package runtime
76
48
@@ -551,33 +523,6 @@ func (h heapBits) isPointer() bool {
551
523
return h .bits ()& bitPointer != 0
552
524
}
553
525
554
- // isCheckmarked reports whether the heap bits have the checkmarked bit set.
555
- // It must be told how large the object at h is, because the encoding of the
556
- // checkmark bit varies by size.
557
- // h must describe the initial word of the object.
558
- func (h heapBits ) isCheckmarked (size uintptr ) bool {
559
- if size == sys .PtrSize {
560
- return (* h .bitp >> h .shift )& bitPointer != 0
561
- }
562
- // All multiword objects are 2-word aligned,
563
- // so we know that the initial word's 2-bit pair
564
- // and the second word's 2-bit pair are in the
565
- // same heap bitmap byte, *h.bitp.
566
- return (* h .bitp >> (heapBitsShift + h .shift ))& bitScan != 0
567
- }
568
-
569
- // setCheckmarked sets the checkmarked bit.
570
- // It must be told how large the object at h is, because the encoding of the
571
- // checkmark bit varies by size.
572
- // h must describe the initial word of the object.
573
- func (h heapBits ) setCheckmarked (size uintptr ) {
574
- if size == sys .PtrSize {
575
- atomic .Or8 (h .bitp , bitPointer << h .shift )
576
- return
577
- }
578
- atomic .Or8 (h .bitp , bitScan << (heapBitsShift + h .shift ))
579
- }
580
-
581
526
// bulkBarrierPreWrite executes a write barrier
582
527
// for every pointer slot in the memory range [src, src+size),
583
528
// using pointer/scalar information from [dst, dst+size).
@@ -795,7 +740,6 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) {
795
740
// TODO(rsc): Perhaps introduce a different heapBitsSpan type.
796
741
797
742
// initSpan initializes the heap bitmap for a span.
798
- // It clears all checkmark bits.
799
743
// If this is a span of pointer-sized objects, it initializes all
800
744
// words to pointer/scan.
801
745
// Otherwise, it initializes all words to scalar/dead.
@@ -826,45 +770,6 @@ func (h heapBits) initSpan(s *mspan) {
826
770
}
827
771
}
828
772
829
- // initCheckmarkSpan initializes a span for being checkmarked.
830
- // It clears the checkmark bits, which are set to 1 in normal operation.
831
- func (h heapBits ) initCheckmarkSpan (size , n , total uintptr ) {
832
- // The ptrSize == 8 is a compile-time constant false on 32-bit and eliminates this code entirely.
833
- if sys .PtrSize == 8 && size == sys .PtrSize {
834
- // Checkmark bit is type bit, bottom bit of every 2-bit entry.
835
- // Only possible on 64-bit system, since minimum size is 8.
836
- // Must clear type bit (checkmark bit) of every word.
837
- // The type bit is the lower of every two-bit pair.
838
- for i := uintptr (0 ); i < n ; i += wordsPerBitmapByte {
839
- * h .bitp &^= bitPointerAll
840
- h = h .forward (wordsPerBitmapByte )
841
- }
842
- return
843
- }
844
- for i := uintptr (0 ); i < n ; i ++ {
845
- * h .bitp &^= bitScan << (heapBitsShift + h .shift )
846
- h = h .forward (size / sys .PtrSize )
847
- }
848
- }
849
-
850
- // clearCheckmarkSpan undoes all the checkmarking in a span.
851
- // The actual checkmark bits are ignored, so the only work to do
852
- // is to fix the pointer bits. (Pointer bits are ignored by scanobject
853
- // but consulted by typedmemmove.)
854
- func (h heapBits ) clearCheckmarkSpan (size , n , total uintptr ) {
855
- // The ptrSize == 8 is a compile-time constant false on 32-bit and eliminates this code entirely.
856
- if sys .PtrSize == 8 && size == sys .PtrSize {
857
- // Checkmark bit is type bit, bottom bit of every 2-bit entry.
858
- // Only possible on 64-bit system, since minimum size is 8.
859
- // Must clear type bit (checkmark bit) of every word.
860
- // The type bit is the lower of every two-bit pair.
861
- for i := uintptr (0 ); i < n ; i += wordsPerBitmapByte {
862
- * h .bitp |= bitPointerAll
863
- h = h .forward (wordsPerBitmapByte )
864
- }
865
- }
866
- }
867
-
868
773
// countAlloc returns the number of objects allocated in span s by
869
774
// scanning the allocation bitmap.
870
775
func (s * mspan ) countAlloc () int {
@@ -957,11 +862,11 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
957
862
if sys .PtrSize == 4 && dataSize == sys .PtrSize {
958
863
// 1 pointer object. On 32-bit machines clear the bit for the
959
864
// unused second word.
960
- * h .bitp &^= (bitPointer | bitScan | (( bitPointer | bitScan ) << heapBitsShift ) ) << h .shift
865
+ * h .bitp &^= (bitPointer | bitScan | (bitPointer | bitScan )<< heapBitsShift ) << h .shift
961
866
* h .bitp |= (bitPointer | bitScan ) << h .shift
962
867
} else {
963
868
// 2-element slice of pointer.
964
- * h .bitp |= (bitPointer | bitScan | bitPointer << heapBitsShift ) << h .shift
869
+ * h .bitp |= (bitPointer | bitScan | ( bitPointer | bitScan ) << heapBitsShift ) << h .shift
965
870
}
966
871
return
967
872
}
@@ -974,11 +879,10 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
974
879
}
975
880
}
976
881
b := uint32 (* ptrmask )
977
- hb := (b & 3 ) | bitScan
978
- // bitPointer == 1, bitScan is 1 << 4, heapBitsShift is 1.
979
- // 110011 is shifted h.shift and complemented.
980
- // This clears out the bits that are about to be
981
- // ored into *h.hbitp in the next instructions.
882
+ hb := b & 3
883
+ hb |= bitScanAll & ((bitScan << (typ .ptrdata / sys .PtrSize )) - 1 )
884
+ // Clear the bits for this object so we can set the
885
+ // appropriate ones.
982
886
* h .bitp &^= (bitPointer | bitScan | ((bitPointer | bitScan ) << heapBitsShift )) << h .shift
983
887
* h .bitp |= uint8 (hb << h .shift )
984
888
return
@@ -1155,11 +1059,6 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
1155
1059
throw ("heapBitsSetType: called with non-pointer type" )
1156
1060
return
1157
1061
}
1158
- if nw < 2 {
1159
- // Must write at least 2 words, because the "no scan"
1160
- // encoding doesn't take effect until the third word.
1161
- nw = 2
1162
- }
1163
1062
1164
1063
// Phase 1: Special case for leading byte (shift==0) or half-byte (shift==2).
1165
1064
// The leading byte is special because it contains the bits for word 1,
@@ -1172,21 +1071,22 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
1172
1071
1173
1072
case h .shift == 0 :
1174
1073
// Ptrmask and heap bitmap are aligned.
1175
- // Handle first byte of bitmap specially.
1074
+ //
1075
+ // This is a fast path for small objects.
1176
1076
//
1177
1077
// The first byte we write out covers the first four
1178
1078
// words of the object. The scan/dead bit on the first
1179
1079
// word must be set to scan since there are pointers
1180
- // somewhere in the object. The scan/dead bit on the
1181
- // second word is the checkmark, so we don't set it.
1080
+ // somewhere in the object.
1182
1081
// In all following words, we set the scan/dead
1183
1082
// appropriately to indicate that the object contains
1184
1083
// to the next 2-bit entry in the bitmap.
1185
1084
//
1186
- // TODO: It doesn't matter if we set the checkmark, so
1187
- // maybe this case isn't needed any more.
1085
+ // We set four bits at a time here, but if the object
1086
+ // is fewer than four words, phase 3 will clear
1087
+ // unnecessary bits.
1188
1088
hb = b & bitPointerAll
1189
- hb |= bitScan | bitScan << ( 2 * heapBitsShift ) | bitScan << ( 3 * heapBitsShift )
1089
+ hb |= bitScanAll
1190
1090
if w += 4 ; w >= nw {
1191
1091
goto Phase3
1192
1092
}
@@ -1203,14 +1103,13 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
1203
1103
// We took care of 1-word and 2-word objects above,
1204
1104
// so this is at least a 6-word object.
1205
1105
hb = (b & (bitPointer | bitPointer << heapBitsShift )) << (2 * heapBitsShift )
1206
- // This is not noscan, so set the scan bit in the
1207
- // first word.
1208
1106
hb |= bitScan << (2 * heapBitsShift )
1107
+ if nw > 1 {
1108
+ hb |= bitScan << (3 * heapBitsShift )
1109
+ }
1209
1110
b >>= 2
1210
1111
nb -= 2
1211
- // Note: no bitScan for second word because that's
1212
- // the checkmark.
1213
- * hbitp &^= uint8 ((bitPointer | bitScan | (bitPointer << heapBitsShift )) << (2 * heapBitsShift ))
1112
+ * hbitp &^= uint8 ((bitPointer | bitScan | ((bitPointer | bitScan ) << heapBitsShift )) << (2 * heapBitsShift ))
1214
1113
* hbitp |= uint8 (hb )
1215
1114
hbitp = add1 (hbitp )
1216
1115
if w += 2 ; w >= nw {
@@ -1449,11 +1348,7 @@ Phase4:
1449
1348
if j < nptr && (* addb (ptrmask , j / 8 )>> (j % 8 ))& 1 != 0 {
1450
1349
want |= bitPointer
1451
1350
}
1452
- if i != 1 {
1453
- want |= bitScan
1454
- } else {
1455
- have &^= bitScan
1456
- }
1351
+ want |= bitScan
1457
1352
}
1458
1353
if have != want {
1459
1354
println ("mismatch writing bits for" , typ .string (), "x" , dataSize / typ .size )
@@ -2013,7 +1908,7 @@ func getgcmask(ep interface{}) (mask []byte) {
2013
1908
if hbits .isPointer () {
2014
1909
mask [i / sys .PtrSize ] = 1
2015
1910
}
2016
- if i != 1 * sys . PtrSize && ! hbits .morePointers () {
1911
+ if ! hbits .morePointers () {
2017
1912
mask = mask [:i / sys .PtrSize ]
2018
1913
break
2019
1914
}
0 commit comments