rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/runtime/mgcmark.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Garbage collector: marking and scanning
     6  
     7  package runtime
     8  
     9  import "unsafe"
    10  
    11  // Scan all of the stacks, greying (or graying if in America) the referents
    12  // but not blackening them since the mark write barrier isn't installed.
    13  //go:nowritebarrier
    14  func gcscan_m() {
    15  	_g_ := getg()
    16  
    17  	// Grab the g that called us and potentially allow rescheduling.
    18  	// This allows it to be scanned like other goroutines.
    19  	mastergp := _g_.m.curg
    20  	casgstatus(mastergp, _Grunning, _Gwaiting)
    21  	mastergp.waitreason = "garbage collection scan"
    22  
    23  	// Span sweeping has been done by finishsweep_m.
    24  	// Long term we will want to make this goroutine runnable
    25  	// by placing it onto a scanenqueue state and then calling
    26  	// runtimeĀ·restartg(mastergp) to make it Grunnable.
    27  	// At the bottom we will want to return this p back to the scheduler.
    28  
    29  	// Prepare flag indicating that the scan has not been completed.
    30  	local_allglen := gcResetGState()
    31  
    32  	work.nwait = 0
    33  	work.ndone = 0
    34  	work.nproc = 1 // For now do not do this in parallel.
    35  	//	ackgcphase is not needed since we are not scanning running goroutines.
    36  	parforsetup(work.markfor, work.nproc, uint32(_RootCount+local_allglen), false, markroot)
    37  	parfordo(work.markfor)
    38  
    39  	lock(&allglock)
    40  	// Check that gc work is done.
    41  	for i := 0; i < local_allglen; i++ {
    42  		gp := allgs[i]
    43  		if !gp.gcworkdone {
    44  			throw("scan missed a g")
    45  		}
    46  	}
    47  	unlock(&allglock)
    48  
    49  	casgstatus(mastergp, _Gwaiting, _Grunning)
    50  	// Let the g that called us continue to run.
    51  }
    52  
    53  // ptrmask for an allocation containing a single pointer.
    54  var oneptr = [...]uint8{typePointer}
    55  
    56  //go:nowritebarrier
    57  func markroot(desc *parfor, i uint32) {
    58  	var gcw gcWork
    59  
    60  	// Note: if you add a case here, please also update heapdump.go:dumproots.
    61  	switch i {
    62  	case _RootData:
    63  		for datap := &firstmoduledata; datap != nil; datap = datap.next {
    64  			scanblock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, &gcw)
    65  		}
    66  
    67  	case _RootBss:
    68  		for datap := &firstmoduledata; datap != nil; datap = datap.next {
    69  			scanblock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, &gcw)
    70  		}
    71  
    72  	case _RootFinalizers:
    73  		for fb := allfin; fb != nil; fb = fb.alllink {
    74  			scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], &gcw)
    75  		}
    76  
    77  	case _RootSpans:
    78  		// mark MSpan.specials
    79  		sg := mheap_.sweepgen
    80  		for spanidx := uint32(0); spanidx < uint32(len(work.spans)); spanidx++ {
    81  			s := work.spans[spanidx]
    82  			if s.state != mSpanInUse {
    83  				continue
    84  			}
    85  			if !useCheckmark && s.sweepgen != sg {
    86  				// sweepgen was updated (+2) during non-checkmark GC pass
    87  				print("sweep ", s.sweepgen, " ", sg, "\n")
    88  				throw("gc: unswept span")
    89  			}
    90  			for sp := s.specials; sp != nil; sp = sp.next {
    91  				if sp.kind != _KindSpecialFinalizer {
    92  					continue
    93  				}
    94  				// don't mark finalized object, but scan it so we
    95  				// retain everything it points to.
    96  				spf := (*specialfinalizer)(unsafe.Pointer(sp))
    97  				// A finalizer can be set for an inner byte of an object, find object beginning.
    98  				p := uintptr(s.start<<_PageShift) + uintptr(spf.special.offset)/s.elemsize*s.elemsize
    99  				if gcphase != _GCscan {
   100  					scanblock(p, s.elemsize, nil, &gcw) // scanned during mark phase
   101  				}
   102  				scanblock(uintptr(unsafe.Pointer(&spf.fn)), ptrSize, &oneptr[0], &gcw)
   103  			}
   104  		}
   105  
   106  	case _RootFlushCaches:
   107  		if gcphase != _GCscan { // Do not flush mcaches during GCscan phase.
   108  			flushallmcaches()
   109  		}
   110  
   111  	default:
   112  		// the rest is scanning goroutine stacks
   113  		if uintptr(i-_RootCount) >= allglen {
   114  			throw("markroot: bad index")
   115  		}
   116  		gp := allgs[i-_RootCount]
   117  
   118  		// remember when we've first observed the G blocked
   119  		// needed only to output in traceback
   120  		status := readgstatus(gp) // We are not in a scan state
   121  		if (status == _Gwaiting || status == _Gsyscall) && gp.waitsince == 0 {
   122  			gp.waitsince = work.tstart
   123  		}
   124  
   125  		// Shrink a stack if not much of it is being used but not in the scan phase.
   126  		if gcphase == _GCmarktermination {
   127  			// Shrink during STW GCmarktermination phase thus avoiding
   128  			// complications introduced by shrinking during
   129  			// non-STW phases.
   130  			shrinkstack(gp)
   131  		}
   132  		if readgstatus(gp) == _Gdead {
   133  			gp.gcworkdone = true
   134  		} else {
   135  			gp.gcworkdone = false
   136  		}
   137  		restart := stopg(gp)
   138  
   139  		// goroutine will scan its own stack when it stops running.
   140  		// Wait until it has.
   141  		for readgstatus(gp) == _Grunning && !gp.gcworkdone {
   142  		}
   143  
   144  		// scanstack(gp) is done as part of gcphasework
   145  		// But to make sure we finished we need to make sure that
   146  		// the stack traps have all responded so drop into
   147  		// this while loop until they respond.
   148  		for !gp.gcworkdone {
   149  			status = readgstatus(gp)
   150  			if status == _Gdead {
   151  				gp.gcworkdone = true // scan is a noop
   152  				break
   153  			}
   154  			if status == _Gwaiting || status == _Grunnable {
   155  				restart = stopg(gp)
   156  			}
   157  		}
   158  		if restart {
   159  			restartg(gp)
   160  		}
   161  	}
   162  
   163  	// Root aren't part of the heap, so don't count them toward
   164  	// marked heap bytes.
   165  	gcw.bytesMarked = 0
   166  	gcw.dispose()
   167  }
   168  
   169  // gchelpwork does a small bounded amount of gc work. The purpose is to
   170  // shorten the time (as measured by allocations) spent doing a concurrent GC.
   171  // The number of mutator calls is roughly propotional to the number of allocations
   172  // made by that mutator. This slows down the allocation while speeding up the GC.
   173  //go:nowritebarrier
   174  func gchelpwork() {
   175  	switch gcphase {
   176  	default:
   177  		throw("gcphasework in bad gcphase")
   178  	case _GCoff, _GCquiesce, _GCstw:
   179  		// No work.
   180  	case _GCsweep:
   181  		// We could help by calling sweepone to sweep a single span.
   182  		// _ = sweepone()
   183  	case _GCscan:
   184  		// scan the stack, mark the objects, put pointers in work buffers
   185  		// hanging off the P where this is being run.
   186  		// scanstack(gp)
   187  	case _GCmark:
   188  		// drain your own currentwbuf first in the hopes that it will
   189  		// be more cache friendly.
   190  		var gcw gcWork
   191  		gcw.initFromCache()
   192  		const n = len(workbuf{}.obj)
   193  		gcDrainN(&gcw, n) // drain upto one buffer's worth of objects
   194  		gcw.dispose()
   195  	case _GCmarktermination:
   196  		// We should never be here since the world is stopped.
   197  		// All available mark work will be emptied before returning.
   198  		throw("gcphasework in bad gcphase")
   199  	}
   200  }
   201  
   202  // The gp has been moved to a GC safepoint. GC phase specific
   203  // work is done here.
   204  //go:nowritebarrier
   205  func gcphasework(gp *g) {
   206  	switch gcphase {
   207  	default:
   208  		throw("gcphasework in bad gcphase")
   209  	case _GCoff, _GCquiesce, _GCstw, _GCsweep:
   210  		// No work.
   211  	case _GCscan:
   212  		// scan the stack, mark the objects, put pointers in work buffers
   213  		// hanging off the P where this is being run.
   214  		// Indicate that the scan is valid until the goroutine runs again
   215  		scanstack(gp)
   216  	case _GCmark:
   217  		// No work.
   218  	case _GCmarktermination:
   219  		scanstack(gp)
   220  		// All available mark work will be emptied before returning.
   221  	}
   222  	gp.gcworkdone = true
   223  }
   224  
   225  //go:nowritebarrier
   226  func scanstack(gp *g) {
   227  	if gp.gcscanvalid {
   228  		return
   229  	}
   230  
   231  	if readgstatus(gp)&_Gscan == 0 {
   232  		print("runtime:scanstack: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", hex(readgstatus(gp)), "\n")
   233  		throw("scanstack - bad status")
   234  	}
   235  
   236  	switch readgstatus(gp) &^ _Gscan {
   237  	default:
   238  		print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
   239  		throw("mark - bad status")
   240  	case _Gdead:
   241  		return
   242  	case _Grunning:
   243  		print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
   244  		throw("scanstack: goroutine not stopped")
   245  	case _Grunnable, _Gsyscall, _Gwaiting:
   246  		// ok
   247  	}
   248  
   249  	if gp == getg() {
   250  		throw("can't scan our own stack")
   251  	}
   252  	mp := gp.m
   253  	if mp != nil && mp.helpgc != 0 {
   254  		throw("can't scan gchelper stack")
   255  	}
   256  
   257  	var gcw gcWork
   258  	gcw.initFromCache()
   259  	scanframe := func(frame *stkframe, unused unsafe.Pointer) bool {
   260  		// Pick up gcw as free variable so gentraceback and friends can
   261  		// keep the same signature.
   262  		scanframeworker(frame, unused, &gcw)
   263  		return true
   264  	}
   265  	gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, scanframe, nil, 0)
   266  	tracebackdefers(gp, scanframe, nil)
   267  	// Stacks aren't part of the heap, so don't count them toward
   268  	// marked heap bytes.
   269  	gcw.bytesMarked = 0
   270  	gcw.disposeToCache()
   271  	gp.gcscanvalid = true
   272  }
   273  
   274  // Scan a stack frame: local variables and function arguments/results.
   275  //go:nowritebarrier
   276  func scanframeworker(frame *stkframe, unused unsafe.Pointer, gcw *gcWork) {
   277  
   278  	f := frame.fn
   279  	targetpc := frame.continpc
   280  	if targetpc == 0 {
   281  		// Frame is dead.
   282  		return
   283  	}
   284  	if _DebugGC > 1 {
   285  		print("scanframe ", funcname(f), "\n")
   286  	}
   287  	if targetpc != f.entry {
   288  		targetpc--
   289  	}
   290  	pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
   291  	if pcdata == -1 {
   292  		// We do not have a valid pcdata value but there might be a
   293  		// stackmap for this function.  It is likely that we are looking
   294  		// at the function prologue, assume so and hope for the best.
   295  		pcdata = 0
   296  	}
   297  
   298  	// Scan local variables if stack frame has been allocated.
   299  	size := frame.varp - frame.sp
   300  	var minsize uintptr
   301  	switch thechar {
   302  	case '6', '8':
   303  		minsize = 0
   304  	case '7':
   305  		minsize = spAlign
   306  	default:
   307  		minsize = ptrSize
   308  	}
   309  	if size > minsize {
   310  		stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
   311  		if stkmap == nil || stkmap.n <= 0 {
   312  			print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
   313  			throw("missing stackmap")
   314  		}
   315  
   316  		// Locals bitmap information, scan just the pointers in locals.
   317  		if pcdata < 0 || pcdata >= stkmap.n {
   318  			// don't know where we are
   319  			print("runtime: pcdata is ", pcdata, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n")
   320  			throw("scanframe: bad symbol table")
   321  		}
   322  		bv := stackmapdata(stkmap, pcdata)
   323  		size = (uintptr(bv.n) / typeBitsWidth) * ptrSize
   324  		scanblock(frame.varp-size, size, bv.bytedata, gcw)
   325  	}
   326  
   327  	// Scan arguments.
   328  	if frame.arglen > 0 {
   329  		var bv bitvector
   330  		if frame.argmap != nil {
   331  			bv = *frame.argmap
   332  		} else {
   333  			stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
   334  			if stkmap == nil || stkmap.n <= 0 {
   335  				print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(frame.arglen), "\n")
   336  				throw("missing stackmap")
   337  			}
   338  			if pcdata < 0 || pcdata >= stkmap.n {
   339  				// don't know where we are
   340  				print("runtime: pcdata is ", pcdata, " and ", stkmap.n, " args stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n")
   341  				throw("scanframe: bad symbol table")
   342  			}
   343  			bv = stackmapdata(stkmap, pcdata)
   344  		}
   345  		scanblock(frame.argp, uintptr(bv.n)/typeBitsWidth*ptrSize, bv.bytedata, gcw)
   346  	}
   347  }
   348  
   349  // gcDrain scans objects in work buffers, blackening grey
   350  // objects until all work buffers have been drained.
   351  //go:nowritebarrier
   352  func gcDrain(gcw *gcWork) {
   353  	if gcphase != _GCmark && gcphase != _GCmarktermination {
   354  		throw("scanblock phase incorrect")
   355  	}
   356  
   357  	for {
   358  		// If another proc wants a pointer, give it some.
   359  		if work.nwait > 0 && work.full == 0 {
   360  			gcw.balance()
   361  		}
   362  
   363  		b := gcw.get()
   364  		if b == 0 {
   365  			// work barrier reached
   366  			break
   367  		}
   368  		// If the current wbuf is filled by the scan a new wbuf might be
   369  		// returned that could possibly hold only a single object. This
   370  		// could result in each iteration draining only a single object
   371  		// out of the wbuf passed in + a single object placed
   372  		// into an empty wbuf in scanobject so there could be
   373  		// a performance hit as we keep fetching fresh wbufs.
   374  		scanobject(b, 0, nil, gcw)
   375  	}
   376  	checknocurrentwbuf()
   377  }
   378  
   379  // gcDrainN scans n objects, blackening grey objects.
   380  //go:nowritebarrier
   381  func gcDrainN(gcw *gcWork, n int) {
   382  	checknocurrentwbuf()
   383  	for i := 0; i < n; i++ {
   384  		// This might be a good place to add prefetch code...
   385  		// if(wbuf.nobj > 4) {
   386  		//         PREFETCH(wbuf->obj[wbuf.nobj - 3];
   387  		//  }
   388  		b := gcw.tryGet()
   389  		if b == 0 {
   390  			return
   391  		}
   392  		scanobject(b, 0, nil, gcw)
   393  	}
   394  }
   395  
   396  // scanblock scans b as scanobject would.
   397  // If the gcphase is GCscan, scanblock performs additional checks.
   398  //go:nowritebarrier
   399  func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
   400  	// Use local copies of original parameters, so that a stack trace
   401  	// due to one of the throws below shows the original block
   402  	// base and extent.
   403  	b := b0
   404  	n := n0
   405  
   406  	// ptrmask can have 2 possible values:
   407  	// 1. nil - obtain pointer mask from GC bitmap.
   408  	// 2. pointer to a compact mask (for stacks and data).
   409  
   410  	scanobject(b, n, ptrmask, gcw)
   411  	if gcphase == _GCscan {
   412  		if inheap(b) && ptrmask == nil {
   413  			// b is in heap, we are in GCscan so there should be a ptrmask.
   414  			throw("scanblock: In GCscan phase and inheap is true.")
   415  		}
   416  	}
   417  }
   418  
   419  // Scan the object b of size n bytes, adding pointers to wbuf.
   420  // If ptrmask != nil, it specifies where pointers are in b.
   421  // If ptrmask == nil, the GC bitmap should be consulted.
   422  // In this case, n may be an overestimate of the size; the GC bitmap
   423  // must also be used to make sure the scan stops at the end of b.
   424  //go:nowritebarrier
   425  func scanobject(b, n uintptr, ptrmask *uint8, gcw *gcWork) {
   426  	arena_start := mheap_.arena_start
   427  	arena_used := mheap_.arena_used
   428  
   429  	// Find bits of the beginning of the object.
   430  	var hbits heapBits
   431  
   432  	if ptrmask == nil {
   433  		var s *mspan
   434  		b, hbits, s = heapBitsForObject(b)
   435  		if b == 0 {
   436  			return
   437  		}
   438  		n = s.elemsize
   439  		if n == 0 {
   440  			throw("scanobject n == 0")
   441  		}
   442  	}
   443  	for i := uintptr(0); i < n; i += ptrSize {
   444  		// Find bits for this word.
   445  		var bits uintptr
   446  		if ptrmask != nil {
   447  			// dense mask (stack or data)
   448  			bits = (uintptr(*(*byte)(add(unsafe.Pointer(ptrmask), (i/ptrSize)/4))) >> (((i / ptrSize) % 4) * typeBitsWidth)) & typeMask
   449  		} else {
   450  			bits = uintptr(hbits.typeBits())
   451  			if bits == typeDead {
   452  				break // no more pointers in this object
   453  			}
   454  			hbits = hbits.next()
   455  		}
   456  
   457  		if bits <= typeScalar { // typeScalar, typeDead, typeScalarMarked
   458  			continue
   459  		}
   460  
   461  		if bits&typePointer != typePointer {
   462  			print("gc useCheckmark=", useCheckmark, " b=", hex(b), " ptrmask=", ptrmask, "\n")
   463  			throw("unexpected garbage collection bits")
   464  		}
   465  
   466  		obj := *(*uintptr)(unsafe.Pointer(b + i))
   467  
   468  		// At this point we have extracted the next potential pointer.
   469  		// Check if it points into heap.
   470  		if obj == 0 || obj < arena_start || obj >= arena_used {
   471  			continue
   472  		}
   473  
   474  		if mheap_.shadow_enabled && debug.wbshadow >= 2 && debug.gccheckmark > 0 && useCheckmark {
   475  			checkwbshadow((*uintptr)(unsafe.Pointer(b + i)))
   476  		}
   477  
   478  		// Mark the object.
   479  		if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
   480  			greyobject(obj, b, i, hbits, span, gcw)
   481  		}
   482  	}
   483  	gcw.bytesMarked += uint64(n)
   484  }
   485  
   486  // Shade the object if it isn't already.
   487  // The object is not nil and known to be in the heap.
   488  //go:nowritebarrier
   489  func shade(b uintptr) {
   490  	if !inheap(b) {
   491  		throw("shade: passed an address not in the heap")
   492  	}
   493  	if obj, hbits, span := heapBitsForObject(b); obj != 0 {
   494  		// TODO: this would be a great place to put a check to see
   495  		// if we are harvesting and if we are then we should
   496  		// figure out why there is a call to shade when the
   497  		// harvester thinks we are in a STW.
   498  		// if atomicload(&harvestingwbufs) == uint32(1) {
   499  		//	// Throw here to discover write barriers
   500  		//	// being executed during a STW.
   501  		//	throw("shade during harvest")
   502  		// }
   503  
   504  		var gcw gcWork
   505  		greyobject(obj, 0, 0, hbits, span, &gcw)
   506  		// This is part of the write barrier so put the wbuf back.
   507  		if gcphase == _GCmarktermination {
   508  			gcw.dispose()
   509  		} else {
   510  			// If we added any pointers to the gcw, then
   511  			// currentwbuf must be nil because 1)
   512  			// greyobject got its wbuf from currentwbuf
   513  			// and 2) shade runs on the systemstack, so
   514  			// we're still on the same M.  If either of
   515  			// these becomes no longer true, we need to
   516  			// rethink this.
   517  			gcw.disposeToCache()
   518  		}
   519  	}
   520  }
   521  
   522  // obj is the start of an object with mark mbits.
   523  // If it isn't already marked, mark it and enqueue into workbuf.
   524  // Return possibly new workbuf to use.
   525  // base and off are for debugging only and could be removed.
   526  //go:nowritebarrier
   527  func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork) {
   528  	// obj should be start of allocation, and so must be at least pointer-aligned.
   529  	if obj&(ptrSize-1) != 0 {
   530  		throw("greyobject: obj not pointer-aligned")
   531  	}
   532  
   533  	if useCheckmark {
   534  		if !hbits.isMarked() {
   535  			printlock()
   536  			print("runtime:greyobject: checkmarks finds unexpected unmarked object obj=", hex(obj), "\n")
   537  			print("runtime: found obj at *(", hex(base), "+", hex(off), ")\n")
   538  
   539  			// Dump the source (base) object
   540  			gcDumpObject("base", base, off)
   541  
   542  			// Dump the object
   543  			gcDumpObject("obj", obj, ^uintptr(0))
   544  
   545  			throw("checkmark found unmarked object")
   546  		}
   547  		if !hbits.isCheckmarked() {
   548  			return
   549  		}
   550  		hbits.setCheckmarked()
   551  		if !hbits.isCheckmarked() {
   552  			throw("setCheckmarked and isCheckmarked disagree")
   553  		}
   554  	} else {
   555  		// If marked we have nothing to do.
   556  		if hbits.isMarked() {
   557  			return
   558  		}
   559  
   560  		hbits.setMarked()
   561  
   562  		// If this is a noscan object, fast-track it to black
   563  		// instead of greying it.
   564  		if hbits.typeBits() == typeDead {
   565  			gcw.bytesMarked += uint64(span.elemsize)
   566  			return
   567  		}
   568  	}
   569  
   570  	// Queue the obj for scanning. The PREFETCH(obj) logic has been removed but
   571  	// seems like a nice optimization that can be added back in.
   572  	// There needs to be time between the PREFETCH and the use.
   573  	// Previously we put the obj in an 8 element buffer that is drained at a rate
   574  	// to give the PREFETCH time to do its work.
   575  	// Use of PREFETCHNTA might be more appropriate than PREFETCH
   576  
   577  	gcw.put(obj)
   578  }
   579  
   580  // gcDumpObject dumps the contents of obj for debugging and marks the
   581  // field at byte offset off in obj.
   582  func gcDumpObject(label string, obj, off uintptr) {
   583  	k := obj >> _PageShift
   584  	x := k
   585  	x -= mheap_.arena_start >> _PageShift
   586  	s := h_spans[x]
   587  	print(label, "=", hex(obj), " k=", hex(k))
   588  	if s == nil {
   589  		print(" s=nil\n")
   590  		return
   591  	}
   592  	print(" s.start*_PageSize=", hex(s.start*_PageSize), " s.limit=", hex(s.limit), " s.sizeclass=", s.sizeclass, " s.elemsize=", s.elemsize, "\n")
   593  	for i := uintptr(0); i < s.elemsize; i += ptrSize {
   594  		print(" *(", label, "+", i, ") = ", hex(*(*uintptr)(unsafe.Pointer(obj + uintptr(i)))))
   595  		if i == off {
   596  			print(" <==")
   597  		}
   598  		print("\n")
   599  	}
   600  }
   601  
   602  // When in GCmarkterminate phase we allocate black.
   603  //go:nowritebarrier
   604  func gcmarknewobject_m(obj, size uintptr) {
   605  	if gcphase != _GCmarktermination {
   606  		throw("marking new object while not in mark termination phase")
   607  	}
   608  	if useCheckmark { // The world should be stopped so this should not happen.
   609  		throw("gcmarknewobject called while doing checkmark")
   610  	}
   611  
   612  	heapBitsForAddr(obj).setMarked()
   613  	xadd64(&work.bytesMarked, int64(size))
   614  }
   615  
   616  // Checkmarking
   617  
   618  // To help debug the concurrent GC we remark with the world
   619  // stopped ensuring that any object encountered has their normal
   620  // mark bit set. To do this we use an orthogonal bit
   621  // pattern to indicate the object is marked. The following pattern
   622  // uses the upper two bits in the object's bounday nibble.
   623  // 01: scalar  not marked
   624  // 10: pointer not marked
   625  // 11: pointer     marked
   626  // 00: scalar      marked
   627  // Xoring with 01 will flip the pattern from marked to unmarked and vica versa.
   628  // The higher bit is 1 for pointers and 0 for scalars, whether the object
   629  // is marked or not.
   630  // The first nibble no longer holds the typeDead pattern indicating that the
   631  // there are no more pointers in the object. This information is held
   632  // in the second nibble.
   633  
   634  // If useCheckmark is true, marking of an object uses the
   635  // checkmark bits (encoding above) instead of the standard
   636  // mark bits.
   637  var useCheckmark = false
   638  
   639  //go:nowritebarrier
   640  func initCheckmarks() {
   641  	useCheckmark = true
   642  	for _, s := range work.spans {
   643  		if s.state == _MSpanInUse {
   644  			heapBitsForSpan(s.base()).initCheckmarkSpan(s.layout())
   645  		}
   646  	}
   647  }
   648  
   649  func clearCheckmarks() {
   650  	useCheckmark = false
   651  	for _, s := range work.spans {
   652  		if s.state == _MSpanInUse {
   653  			heapBitsForSpan(s.base()).clearCheckmarkSpan(s.layout())
   654  		}
   655  	}
   656  }