Source file
src/runtime/mgcmark_greenteagc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package runtime
38
39 import (
40 "internal/goarch"
41 "internal/runtime/atomic"
42 "internal/runtime/gc"
43 "internal/runtime/gc/scan"
44 "internal/runtime/sys"
45 "unsafe"
46 )
47
48 const doubleCheckGreenTea = false
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 type spanInlineMarkBits struct {
67 scans [63]uint8
68 owned spanScanOwnership
69 marks [63]uint8
70 class spanClass
71 }
72
73
74
75
76
77
78
79 type spanScanOwnership uint8
80
81 const (
82 spanScanUnowned spanScanOwnership = 0
83 spanScanOneMark = 1 << iota
84 spanScanManyMark
85
86
87
88 )
89
90
91 func (o *spanScanOwnership) load() spanScanOwnership {
92 return spanScanOwnership(atomic.Load8((*uint8)(unsafe.Pointer(o))))
93 }
94
95 func (o *spanScanOwnership) or(v spanScanOwnership) spanScanOwnership {
96
97
98
99
100
101
102
103
104
105 o32 := (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(o)) &^ 0b11))
106 off := (uintptr(unsafe.Pointer(o)) & 0b11) * 8
107 if goarch.BigEndian {
108 off = 32 - off - 8
109 }
110 return spanScanOwnership(atomic.Or32(o32, uint32(v)<<off) >> off)
111 }
112
113 func (imb *spanInlineMarkBits) init(class spanClass, needzero bool) {
114 if imb == nil {
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 throw("runtime: span inline mark bits nil?")
133 }
134 if needzero {
135
136
137
138
139 memclrNoHeapPointers(unsafe.Pointer(imb), unsafe.Sizeof(spanInlineMarkBits{}))
140 }
141 imb.class = class
142 }
143
144
145
146 func (imb *spanInlineMarkBits) tryAcquire() bool {
147 switch imb.owned.load() {
148 case spanScanUnowned:
149
150 if imb.owned.or(spanScanOneMark) == spanScanUnowned {
151 return true
152 }
153
154
155
156 fallthrough
157 case spanScanOneMark:
158
159
160 return imb.owned.or(spanScanManyMark) == spanScanUnowned
161 }
162 return false
163 }
164
165
166
167
168
169
170
171
172
173 func (imb *spanInlineMarkBits) release() spanScanOwnership {
174 return spanScanOwnership(atomic.Xchg8((*uint8)(unsafe.Pointer(&imb.owned)), uint8(spanScanUnowned)))
175 }
176
177
178
179
180 func spanInlineMarkBitsFromBase(base uintptr) *spanInlineMarkBits {
181 return (*spanInlineMarkBits)(unsafe.Pointer(base + gc.PageSize - unsafe.Sizeof(spanInlineMarkBits{})))
182 }
183
184
185 func (s *mspan) initInlineMarkBits() {
186 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
187 throw("expected span with inline mark bits")
188 }
189
190 s.inlineMarkBits().init(s.spanclass, s.needzero != 0)
191 }
192
193
194
195
196 func (s *mspan) moveInlineMarks(dst *gcBits) {
197 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
198 throw("expected span with inline mark bits")
199 }
200 bytes := divRoundUp(uintptr(s.nelems), 8)
201 imb := s.inlineMarkBits()
202 imbMarks := (*gc.ObjMask)(unsafe.Pointer(&imb.marks))
203 for i := uintptr(0); i < bytes; i += goarch.PtrSize {
204 marks := bswapIfBigEndian(imbMarks[i/goarch.PtrSize])
205 if i/goarch.PtrSize == uintptr(len(imb.marks)+1)/goarch.PtrSize-1 {
206 marks &^= 0xff << ((goarch.PtrSize - 1) * 8)
207 }
208 *(*uintptr)(unsafe.Pointer(dst.bytep(i))) |= bswapIfBigEndian(marks)
209 }
210 if doubleCheckGreenTea && !s.spanclass.noscan() && imb.marks != imb.scans {
211 throw("marks don't match scans for span with pointer")
212 }
213
214
215 imb.init(s.spanclass, true )
216 }
217
218
219
220
221 func (s *mspan) inlineMarkBits() *spanInlineMarkBits {
222 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
223 throw("expected span with inline mark bits")
224 }
225 return spanInlineMarkBitsFromBase(s.base())
226 }
227
228 func (s *mspan) markBitsForIndex(objIndex uintptr) (bits markBits) {
229 if gcUsesSpanInlineMarkBits(s.elemsize) {
230 bits.bytep = &s.inlineMarkBits().marks[objIndex/8]
231 } else {
232 bits.bytep = s.gcmarkBits.bytep(objIndex / 8)
233 }
234 bits.mask = uint8(1) << (objIndex % 8)
235 bits.index = objIndex
236 return
237 }
238
239 func (s *mspan) markBitsForBase() markBits {
240 if gcUsesSpanInlineMarkBits(s.elemsize) {
241 return markBits{&s.inlineMarkBits().marks[0], uint8(1), 0}
242 }
243 return markBits{&s.gcmarkBits.x, uint8(1), 0}
244 }
245
246
247
248 func (s *mspan) scannedBitsForIndex(objIndex uintptr) markBits {
249 return markBits{&s.inlineMarkBits().scans[objIndex/8], uint8(1) << (objIndex % 8), objIndex}
250 }
251
252
253
254
255
256
257
258 func gcUsesSpanInlineMarkBits(size uintptr) bool {
259 return heapBitsInSpan(size) && size >= 16
260 }
261
262
263
264 func tryDeferToSpanScan(p uintptr, gcw *gcWork) bool {
265 if useCheckmark {
266 return false
267 }
268
269
270 ha := heapArenaOf(p)
271 if ha == nil {
272 return false
273 }
274 pageIdx := ((p / pageSize) / 8) % uintptr(len(ha.pageInUse))
275 pageMask := byte(1 << ((p / pageSize) % 8))
276 if ha.pageUseSpanInlineMarkBits[pageIdx]&pageMask == 0 {
277 return false
278 }
279
280
281 base := alignDown(p, gc.PageSize)
282 q := spanInlineMarkBitsFromBase(base)
283 objIndex := uint16((uint64(p-base) * uint64(gc.SizeClassToDivMagic[q.class.sizeclass()])) >> 32)
284
285
286 idx, mask := objIndex/8, uint8(1)<<(objIndex%8)
287 if atomic.Load8(&q.marks[idx])&mask != 0 {
288 return true
289 }
290 atomic.Or8(&q.marks[idx], mask)
291
292
293 if q.class.noscan() {
294 gcw.bytesMarked += uint64(gc.SizeClassToSize[q.class.sizeclass()])
295 return true
296 }
297
298
299 if q.tryAcquire() {
300 if gcw.spanq.put(makeObjPtr(base, objIndex)) {
301 if gcphase == _GCmark {
302
303
304
305 if !work.spanqMask.read(uint32(gcw.id)) {
306 work.spanqMask.set(gcw.id)
307 }
308 gcw.mayNeedWorker = true
309 }
310 gcw.flushedWork = true
311 }
312 }
313 return true
314 }
315
316
317 func (w *gcWork) tryGetSpanFast() objptr {
318 return w.spanq.tryGetFast()
319 }
320
321
322 func (w *gcWork) tryGetSpan() objptr {
323 if s := w.spanq.tryGetFast(); s != 0 {
324 return s
325 }
326
327 if s := w.spanq.steal(&w.spanq); s != 0 {
328 return s
329 }
330
331
332 if work.spanqMask.read(uint32(w.id)) {
333 work.spanqMask.clear(w.id)
334 }
335 return 0
336 }
337
338
339 type spanQueue struct {
340
341 head, tail uint32
342 ring [256]objptr
343
344
345 putsSinceDrain int
346
347
348
349
350
351
352
353 chain struct {
354
355
356 head *spanSPMC
357
358
359
360 tail atomic.UnsafePointer
361 }
362 }
363
364
365 func (q *spanQueue) putFast(s objptr) (ok bool) {
366 if q.tail-q.head == uint32(len(q.ring)) {
367 return false
368 }
369 q.ring[q.tail%uint32(len(q.ring))] = s
370 q.tail++
371 return true
372 }
373
374
375
376
377 func (q *spanQueue) put(s objptr) bool {
378
379
380
381
382
383
384
385
386
387
388 const (
389
390
391 spillPeriod = 64
392
393
394
395 spillMax = 16
396 )
397
398 if q.putFast(s) {
399
400 q.putsSinceDrain++
401 if q.putsSinceDrain >= spillPeriod {
402
403 q.putsSinceDrain = 0
404
405
406
407 n := min((q.tail-q.head)/2, spillMax)
408 if n > 4 && q.chainEmpty() {
409 q.drain(n)
410 return true
411 }
412 }
413 return false
414 }
415
416
417 q.drain(uint32(len(q.ring)) / 2)
418 if !q.putFast(s) {
419 throw("failed putFast after drain")
420 }
421 return true
422 }
423
424
425 func (q *spanQueue) flush() {
426 n := q.tail - q.head
427 if n == 0 {
428 return
429 }
430 q.drain(n)
431 }
432
433
434
435
436 func (q *spanQueue) empty() bool {
437
438 if q.tail-q.head > 0 {
439 return false
440 }
441 return q.chainEmpty()
442 }
443
444
445
446
447 func (q *spanQueue) chainEmpty() bool {
448
449 r := (*spanSPMC)(q.chain.tail.Load())
450 for r != nil {
451 if !r.empty() {
452 return false
453 }
454 r = (*spanSPMC)(r.prev.Load())
455 }
456 return true
457 }
458
459
460 func (q *spanQueue) drain(n uint32) {
461 q.putsSinceDrain = 0
462
463 if q.chain.head == nil {
464
465
466
467 r := newSpanSPMC(1024)
468 q.chain.head = r
469 q.chain.tail.StoreNoWB(unsafe.Pointer(r))
470 }
471
472
473 if q.tryDrain(q.chain.head, n) {
474 return
475 }
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 const maxCap = 1 << 20 / goarch.PtrSize
496 newCap := q.chain.head.cap * 2
497 if newCap > maxCap {
498 newCap = maxCap
499 }
500 newHead := newSpanSPMC(newCap)
501 if !q.tryDrain(newHead, n) {
502 throw("failed to put span on newly-allocated spanSPMC")
503 }
504 q.chain.head.prev.StoreNoWB(unsafe.Pointer(newHead))
505 q.chain.head = newHead
506 }
507
508
509
510
511 func (q *spanQueue) tryDrain(r *spanSPMC, n uint32) bool {
512 if q.head+n > q.tail {
513 throw("attempt to drain too many elements")
514 }
515 h := r.head.Load()
516 t := r.tail.Load()
517 rn := t - h
518 if rn+n <= r.cap {
519 for i := uint32(0); i < n; i++ {
520 *r.slot(t + i) = q.ring[(q.head+i)%uint32(len(q.ring))]
521 }
522 r.tail.Store(t + n)
523 q.head += n
524 return true
525 }
526 return false
527 }
528
529
530
531 func (q *spanQueue) tryGetFast() objptr {
532 if q.tail-q.head == 0 {
533 return 0
534 }
535 s := q.ring[q.head%uint32(len(q.ring))]
536 q.head++
537 return s
538 }
539
540
541
542
543 func (q *spanQueue) steal(q2 *spanQueue) objptr {
544 r := (*spanSPMC)(q2.chain.tail.Load())
545 if r == nil {
546 return 0
547 }
548 for {
549
550
551
552
553
554
555 r2 := (*spanSPMC)(r.prev.Load())
556
557
558 if s := q.refill(r); s != 0 {
559 return s
560 }
561
562 if r2 == nil {
563
564
565 return 0
566 }
567
568
569
570
571
572 if q2.chain.tail.CompareAndSwapNoWB(unsafe.Pointer(r), unsafe.Pointer(r2)) {
573 r.dead.Store(true)
574 }
575
576 r = r2
577 }
578 }
579
580
581
582
583
584
585
586 func (q *spanQueue) refill(r *spanSPMC) objptr {
587 if q.tail-q.head != 0 {
588 throw("steal with local work available")
589 }
590
591
592 var n uint32
593 for {
594 h := r.head.Load()
595 t := r.tail.Load()
596 n = t - h
597 n = n - n/2
598 if n == 0 {
599 return 0
600 }
601 if n > r.cap {
602 continue
603 }
604 n = min(n, uint32(len(q.ring)/2))
605 for i := uint32(0); i < n; i++ {
606 q.ring[i] = *r.slot(h + i)
607 }
608 if r.head.CompareAndSwap(h, h+n) {
609 break
610 }
611 }
612
613
614 q.head = 0
615 q.tail = n
616
617
618 return q.tryGetFast()
619 }
620
621
622
623
624
625
626
627 func (q *spanQueue) destroy() {
628 assertWorldStopped()
629 if gcphase != _GCoff {
630 throw("spanQueue.destroy during the mark phase")
631 }
632 if !q.empty() {
633 throw("spanQueue.destroy on non-empty queue")
634 }
635
636 lock(&work.spanSPMCs.lock)
637
638
639 for r := (*spanSPMC)(q.chain.tail.Load()); r != nil; r = (*spanSPMC)(r.prev.Load()) {
640 prev := r.allprev
641 next := r.allnext
642 if prev != nil {
643 prev.allnext = next
644 }
645 if next != nil {
646 next.allprev = prev
647 }
648 if work.spanSPMCs.all == r {
649 work.spanSPMCs.all = next
650 }
651
652 r.deinit()
653 mheap_.spanSPMCAlloc.free(unsafe.Pointer(r))
654 }
655
656 q.chain.head = nil
657 q.chain.tail.Store(nil)
658 q.putsSinceDrain = 0
659
660 unlock(&work.spanSPMCs.lock)
661 }
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688 type spanSPMC struct {
689 _ sys.NotInHeap
690
691
692
693
694 allnext *spanSPMC
695
696
697
698
699 allprev *spanSPMC
700
701
702
703
704
705 dead atomic.Bool
706
707
708
709
710 prev atomic.UnsafePointer
711
712
713
714 head atomic.Uint32
715 tail atomic.Uint32
716 cap uint32
717 ring *objptr
718 }
719
720
721
722
723
724 func newSpanSPMC(cap uint32) *spanSPMC {
725 lock(&work.spanSPMCs.lock)
726 r := (*spanSPMC)(mheap_.spanSPMCAlloc.alloc())
727 next := work.spanSPMCs.all
728 r.allnext = next
729 if next != nil {
730 next.allprev = r
731 }
732 work.spanSPMCs.all = r
733 unlock(&work.spanSPMCs.lock)
734
735
736 pageCap := uint32(physPageSize / goarch.PtrSize)
737 if cap < pageCap {
738 cap = pageCap
739 }
740 if cap&(cap-1) != 0 {
741 throw("spmc capacity must be a power of 2")
742 }
743
744 r.cap = cap
745 ring := sysAlloc(uintptr(cap)*unsafe.Sizeof(objptr(0)), &memstats.gcMiscSys, "GC span queue")
746 atomic.StorepNoWB(unsafe.Pointer(&r.ring), ring)
747 return r
748 }
749
750
751
752
753 func (r *spanSPMC) empty() bool {
754 h := r.head.Load()
755 t := r.tail.Load()
756 return t == h
757 }
758
759
760 func (r *spanSPMC) deinit() {
761 sysFree(unsafe.Pointer(r.ring), uintptr(r.cap)*unsafe.Sizeof(objptr(0)), &memstats.gcMiscSys)
762 r.ring = nil
763 r.dead.Store(false)
764 r.prev.StoreNoWB(nil)
765 r.head.Store(0)
766 r.tail.Store(0)
767 r.cap = 0
768 r.allnext = nil
769 r.allprev = nil
770 }
771
772
773 func (r *spanSPMC) slot(i uint32) *objptr {
774 idx := uintptr(i & (r.cap - 1))
775 return (*objptr)(unsafe.Add(unsafe.Pointer(r.ring), idx*unsafe.Sizeof(objptr(0))))
776 }
777
778
779 func freeDeadSpanSPMCs() {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797 lock(&work.spanSPMCs.lock)
798 if gcphase != _GCoff || work.spanSPMCs.all == nil {
799 unlock(&work.spanSPMCs.lock)
800 return
801 }
802 r := work.spanSPMCs.all
803 for r != nil {
804 next := r.allnext
805 if r.dead.Load() {
806
807 prev := r.allprev
808 if prev != nil {
809 prev.allnext = next
810 }
811 if next != nil {
812 next.allprev = prev
813 }
814 if work.spanSPMCs.all == r {
815 work.spanSPMCs.all = next
816 }
817
818 r.deinit()
819 mheap_.spanSPMCAlloc.free(unsafe.Pointer(r))
820 }
821 r = next
822 }
823 unlock(&work.spanSPMCs.lock)
824 }
825
826
827
828
829 func (w *gcWork) tryStealSpan() objptr {
830 pp := getg().m.p.ptr()
831
832 for enum := stealOrder.start(cheaprand()); !enum.done(); enum.next() {
833 if !work.spanqMask.read(enum.position()) {
834 continue
835 }
836 p2 := allp[enum.position()]
837 if pp == p2 {
838 continue
839 }
840 if s := w.spanq.steal(&p2.gcw.spanq); s != 0 {
841 return s
842 }
843
844
845
846
847
848
849
850 work.spanqMask.clear(int32(enum.position()))
851 }
852 return 0
853 }
854
855
856 type objptr uintptr
857
858
859 func makeObjPtr(spanBase uintptr, objIndex uint16) objptr {
860 if doubleCheckGreenTea && spanBase&((1<<gc.PageShift)-1) != 0 {
861 throw("created objptr with address that is incorrectly aligned")
862 }
863 return objptr(spanBase | uintptr(objIndex))
864 }
865
866 func (p objptr) spanBase() uintptr {
867 return uintptr(p) &^ ((1 << gc.PageShift) - 1)
868 }
869
870 func (p objptr) objIndex() uint16 {
871 return uint16(p) & ((1 << gc.PageShift) - 1)
872 }
873
874
875
876 func scanSpan(p objptr, gcw *gcWork) {
877 spanBase := p.spanBase()
878 imb := spanInlineMarkBitsFromBase(spanBase)
879 spanclass := imb.class
880 if spanclass.noscan() {
881 throw("noscan object in scanSpan")
882 }
883 elemsize := uintptr(gc.SizeClassToSize[spanclass.sizeclass()])
884
885
886 if imb.release() == spanScanOneMark {
887
888
889 objIndex := p.objIndex()
890 bytep := &imb.scans[objIndex/8]
891 mask := uint8(1) << (objIndex % 8)
892 if atomic.Load8(bytep)&mask != 0 {
893 return
894 }
895 atomic.Or8(bytep, mask)
896 gcw.bytesMarked += uint64(elemsize)
897 if debug.gctrace > 1 {
898 gcw.stats[spanclass.sizeclass()].sparseObjsScanned++
899 }
900 b := spanBase + uintptr(objIndex)*elemsize
901 scanObjectSmall(spanBase, b, elemsize, gcw)
902 return
903 }
904
905
906 divMagic := uint64(gc.SizeClassToDivMagic[spanclass.sizeclass()])
907 usableSpanSize := uint64(gc.PageSize - unsafe.Sizeof(spanInlineMarkBits{}))
908 if !spanclass.noscan() {
909 usableSpanSize -= gc.PageSize / goarch.PtrSize / 8
910 }
911 nelems := uint16((usableSpanSize * divMagic) >> 32)
912
913
914 var toScan gc.ObjMask
915 objsMarked := spanSetScans(spanBase, nelems, imb, &toScan)
916 if objsMarked == 0 {
917 return
918 }
919 gcw.bytesMarked += uint64(objsMarked) * uint64(elemsize)
920
921
922
923
924 if !scan.HasFastScanSpanPacked() || objsMarked < int(nelems/8) {
925 if debug.gctrace > 1 {
926 gcw.stats[spanclass.sizeclass()].spansSparseScanned++
927 gcw.stats[spanclass.sizeclass()].spanObjsSparseScanned += uint64(objsMarked)
928 }
929 scanObjectsSmall(spanBase, elemsize, nelems, gcw, &toScan)
930 return
931 }
932
933
934
935
936
937
938 nptrs := scan.ScanSpanPacked(
939 unsafe.Pointer(spanBase),
940 &gcw.ptrBuf[0],
941 &toScan,
942 uintptr(spanclass.sizeclass()),
943 spanPtrMaskUnsafe(spanBase),
944 )
945 gcw.heapScanWork += int64(objsMarked) * int64(elemsize)
946
947 if debug.gctrace > 1 {
948
949 gcw.stats[spanclass.sizeclass()].spansDenseScanned++
950 gcw.stats[spanclass.sizeclass()].spanObjsDenseScanned += uint64(objsMarked)
951 }
952
953
954 for _, p := range gcw.ptrBuf[:nptrs] {
955 if !tryDeferToSpanScan(p, gcw) {
956 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
957 greyobject(obj, 0, 0, span, gcw, objIndex)
958 }
959 }
960 }
961 }
962
963
964
965
966
967
968 func spanSetScans(spanBase uintptr, nelems uint16, imb *spanInlineMarkBits, toScan *gc.ObjMask) int {
969 arena, pageIdx, pageMask := pageIndexOf(spanBase)
970 if arena.pageMarks[pageIdx]&pageMask == 0 {
971 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
972 }
973
974 bytes := divRoundUp(uintptr(nelems), 8)
975 objsMarked := 0
976
977
978
979
980 imbMarks := (*gc.ObjMask)(unsafe.Pointer(&imb.marks))
981 imbScans := (*gc.ObjMask)(unsafe.Pointer(&imb.scans))
982
983
984
985
986 for i := uintptr(0); i < bytes; i += goarch.PtrSize {
987 scans := atomic.Loaduintptr(&imbScans[i/goarch.PtrSize])
988 marks := imbMarks[i/goarch.PtrSize]
989 scans = bswapIfBigEndian(scans)
990 marks = bswapIfBigEndian(marks)
991 if i/goarch.PtrSize == uintptr(len(imb.marks)+1)/goarch.PtrSize-1 {
992 scans &^= 0xff << ((goarch.PtrSize - 1) * 8)
993 marks &^= 0xff << ((goarch.PtrSize - 1) * 8)
994 }
995 toGrey := marks &^ scans
996 toScan[i/goarch.PtrSize] = toGrey
997
998
999 if toGrey != 0 {
1000 toGrey = bswapIfBigEndian(toGrey)
1001 if goarch.PtrSize == 4 {
1002 atomic.Or32((*uint32)(unsafe.Pointer(&imbScans[i/goarch.PtrSize])), uint32(toGrey))
1003 } else {
1004 atomic.Or64((*uint64)(unsafe.Pointer(&imbScans[i/goarch.PtrSize])), uint64(toGrey))
1005 }
1006 }
1007 objsMarked += sys.OnesCount64(uint64(toGrey))
1008 }
1009 return objsMarked
1010 }
1011
1012 func scanObjectSmall(spanBase, b, objSize uintptr, gcw *gcWork) {
1013 ptrBits := heapBitsSmallForAddrInline(spanBase, b, objSize)
1014 gcw.heapScanWork += int64(sys.Len64(uint64(ptrBits)) * goarch.PtrSize)
1015 nptrs := 0
1016 n := sys.OnesCount64(uint64(ptrBits))
1017 for range n {
1018 k := sys.TrailingZeros64(uint64(ptrBits))
1019 ptrBits &^= 1 << k
1020 addr := b + uintptr(k)*goarch.PtrSize
1021
1022
1023
1024 sys.Prefetch(addr)
1025
1026
1027 gcw.ptrBuf[nptrs] = addr
1028 nptrs++
1029 }
1030
1031
1032 for _, p := range gcw.ptrBuf[:nptrs] {
1033 p = *(*uintptr)(unsafe.Pointer(p))
1034 if p == 0 {
1035 continue
1036 }
1037 if !tryDeferToSpanScan(p, gcw) {
1038 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
1039 greyobject(obj, 0, 0, span, gcw, objIndex)
1040 }
1041 }
1042 }
1043 }
1044
1045 func scanObjectsSmall(base, objSize uintptr, elems uint16, gcw *gcWork, scans *gc.ObjMask) {
1046 nptrs := 0
1047 for i, bits := range scans {
1048 if i*(goarch.PtrSize*8) > int(elems) {
1049 break
1050 }
1051 n := sys.OnesCount64(uint64(bits))
1052 for range n {
1053 j := sys.TrailingZeros64(uint64(bits))
1054 bits &^= 1 << j
1055
1056 b := base + uintptr(i*(goarch.PtrSize*8)+j)*objSize
1057 ptrBits := heapBitsSmallForAddrInline(base, b, objSize)
1058 gcw.heapScanWork += int64(sys.Len64(uint64(ptrBits)) * goarch.PtrSize)
1059
1060 n := sys.OnesCount64(uint64(ptrBits))
1061 for range n {
1062 k := sys.TrailingZeros64(uint64(ptrBits))
1063 ptrBits &^= 1 << k
1064 addr := b + uintptr(k)*goarch.PtrSize
1065
1066
1067
1068 sys.Prefetch(addr)
1069
1070
1071 gcw.ptrBuf[nptrs] = addr
1072 nptrs++
1073 }
1074 }
1075 }
1076
1077
1078 for _, p := range gcw.ptrBuf[:nptrs] {
1079 p = *(*uintptr)(unsafe.Pointer(p))
1080 if p == 0 {
1081 continue
1082 }
1083 if !tryDeferToSpanScan(p, gcw) {
1084 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
1085 greyobject(obj, 0, 0, span, gcw, objIndex)
1086 }
1087 }
1088 }
1089 }
1090
1091 func heapBitsSmallForAddrInline(spanBase, addr, elemsize uintptr) uintptr {
1092 hbitsBase, _ := spanHeapBitsRange(spanBase, gc.PageSize, elemsize)
1093 hbits := (*byte)(unsafe.Pointer(hbitsBase))
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103 i := (addr - spanBase) / goarch.PtrSize / ptrBits
1104 j := (addr - spanBase) / goarch.PtrSize % ptrBits
1105 bits := elemsize / goarch.PtrSize
1106 word0 := (*uintptr)(unsafe.Pointer(addb(hbits, goarch.PtrSize*(i+0))))
1107 word1 := (*uintptr)(unsafe.Pointer(addb(hbits, goarch.PtrSize*(i+1))))
1108
1109 var read uintptr
1110 if j+bits > ptrBits {
1111
1112 bits0 := ptrBits - j
1113 bits1 := bits - bits0
1114 read = *word0 >> j
1115 read |= (*word1 & ((1 << bits1) - 1)) << bits0
1116 } else {
1117
1118 read = (*word0 >> j) & ((1 << bits) - 1)
1119 }
1120 return read
1121 }
1122
1123
1124
1125
1126
1127
1128
1129 func spanPtrMaskUnsafe(spanBase uintptr) *gc.PtrMask {
1130 base := spanBase + gc.PageSize - unsafe.Sizeof(gc.PtrMask{}) - unsafe.Sizeof(spanInlineMarkBits{})
1131 return (*gc.PtrMask)(unsafe.Pointer(base))
1132 }
1133
1134 type sizeClassScanStats struct {
1135 spansDenseScanned uint64
1136 spanObjsDenseScanned uint64
1137 spansSparseScanned uint64
1138 spanObjsSparseScanned uint64
1139 sparseObjsScanned uint64
1140
1141
1142
1143
1144 }
1145
1146 func dumpScanStats() {
1147 var (
1148 spansDenseScanned uint64
1149 spanObjsDenseScanned uint64
1150 spansSparseScanned uint64
1151 spanObjsSparseScanned uint64
1152 sparseObjsScanned uint64
1153 )
1154 for _, stats := range memstats.lastScanStats {
1155 spansDenseScanned += stats.spansDenseScanned
1156 spanObjsDenseScanned += stats.spanObjsDenseScanned
1157 spansSparseScanned += stats.spansSparseScanned
1158 spanObjsSparseScanned += stats.spanObjsSparseScanned
1159 sparseObjsScanned += stats.sparseObjsScanned
1160 }
1161 totalObjs := sparseObjsScanned + spanObjsSparseScanned + spanObjsDenseScanned
1162 totalSpans := spansSparseScanned + spansDenseScanned
1163 print("scan: total ", sparseObjsScanned, "+", spanObjsSparseScanned, "+", spanObjsDenseScanned, "=", totalObjs, " objs")
1164 print(", ", spansSparseScanned, "+", spansDenseScanned, "=", totalSpans, " spans\n")
1165 for i, stats := range memstats.lastScanStats {
1166 if stats == (sizeClassScanStats{}) {
1167 continue
1168 }
1169 totalObjs := stats.sparseObjsScanned + stats.spanObjsSparseScanned + stats.spanObjsDenseScanned
1170 totalSpans := stats.spansSparseScanned + stats.spansDenseScanned
1171 if i == 0 {
1172 print("scan: class L ")
1173 } else {
1174 print("scan: class ", gc.SizeClassToSize[i], "B ")
1175 }
1176 print(stats.sparseObjsScanned, "+", stats.spanObjsSparseScanned, "+", stats.spanObjsDenseScanned, "=", totalObjs, " objs")
1177 print(", ", stats.spansSparseScanned, "+", stats.spansDenseScanned, "=", totalSpans, " spans\n")
1178 }
1179 }
1180
1181 func (w *gcWork) flushScanStats(dst *[gc.NumSizeClasses]sizeClassScanStats) {
1182 for i := range w.stats {
1183 dst[i].spansDenseScanned += w.stats[i].spansDenseScanned
1184 dst[i].spanObjsDenseScanned += w.stats[i].spanObjsDenseScanned
1185 dst[i].spansSparseScanned += w.stats[i].spansSparseScanned
1186 dst[i].spanObjsSparseScanned += w.stats[i].spanObjsSparseScanned
1187 dst[i].sparseObjsScanned += w.stats[i].sparseObjsScanned
1188 }
1189 clear(w.stats[:])
1190 }
1191
1192
1193
1194
1195
1196 func gcMarkWorkAvailable() bool {
1197 if !work.full.empty() {
1198 return true
1199 }
1200 if work.markrootNext.Load() < work.markrootJobs.Load() {
1201 return true
1202 }
1203 if work.spanqMask.any() {
1204 return true
1205 }
1206 return false
1207 }
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218 func scanObject(b uintptr, gcw *gcWork) {
1219
1220
1221
1222
1223 sys.Prefetch(b)
1224
1225
1226
1227
1228
1229
1230 s := spanOfUnchecked(b)
1231 n := s.elemsize
1232 if n == 0 {
1233 throw("scanObject n == 0")
1234 }
1235 if s.spanclass.noscan() {
1236
1237
1238 throw("scanObject of a noscan object")
1239 }
1240
1241 var tp typePointers
1242 if n > maxObletBytes {
1243
1244
1245 if b == s.base() {
1246
1247
1248
1249
1250
1251 for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes {
1252 if !gcw.putObjFast(oblet) {
1253 gcw.putObj(oblet)
1254 }
1255 }
1256 }
1257
1258
1259
1260
1261 n = s.base() + s.elemsize - b
1262 n = min(n, maxObletBytes)
1263 tp = s.typePointersOfUnchecked(s.base())
1264 tp = tp.fastForward(b-tp.addr, b+n)
1265 } else {
1266 tp = s.typePointersOfUnchecked(b)
1267 }
1268
1269 var scanSize uintptr
1270 for {
1271 var addr uintptr
1272 if tp, addr = tp.nextFast(); addr == 0 {
1273 if tp, addr = tp.next(b + n); addr == 0 {
1274 break
1275 }
1276 }
1277
1278
1279
1280
1281 scanSize = addr - b + goarch.PtrSize
1282
1283
1284
1285 obj := *(*uintptr)(unsafe.Pointer(addr))
1286
1287
1288
1289 if obj != 0 && obj-b >= n {
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299 if !tryDeferToSpanScan(obj, gcw) {
1300 if obj, span, objIndex := findObject(obj, b, addr-b); obj != 0 {
1301 greyobject(obj, b, addr-b, span, gcw, objIndex)
1302 }
1303 }
1304 }
1305 }
1306 gcw.bytesMarked += uint64(n)
1307 gcw.heapScanWork += int64(scanSize)
1308 if debug.gctrace > 1 {
1309 gcw.stats[s.spanclass.sizeclass()].sparseObjsScanned++
1310 }
1311 }
1312
View as plain text