Home | History | Annotate | Download | only in runtime
      1 // Copyright 2015 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: write barriers.
      6 //
      7 // For the concurrent garbage collector, the Go compiler implements
      8 // updates to pointer-valued fields that may be in heap objects by
      9 // emitting calls to write barriers. This file contains the actual write barrier
     10 // implementation, markwb, and the various wrappers called by the
     11 // compiler to implement pointer assignment, slice assignment,
     12 // typed memmove, and so on.
     13 
     14 package runtime
     15 
     16 import "unsafe"
     17 
     18 // markwb is the mark-phase write barrier, the only barrier we have.
     19 // The rest of this file exists only to make calls to this function.
     20 //
     21 // This is the Dijkstra barrier coarsened to always shade the ptr (dst) object.
     22 // The original Dijkstra barrier only shaded ptrs being placed in black slots.
     23 //
     24 // Shade indicates that it has seen a white pointer by adding the referent
     25 // to wbuf as well as marking it.
     26 //
     27 // slot is the destination (dst) in go code
     28 // ptr is the value that goes into the slot (src) in the go code
     29 //
     30 //
     31 // Dealing with memory ordering:
     32 //
     33 // Dijkstra pointed out that maintaining the no black to white
     34 // pointers means that white to white pointers not need
     35 // to be noted by the write barrier. Furthermore if either
     36 // white object dies before it is reached by the
     37 // GC then the object can be collected during this GC cycle
     38 // instead of waiting for the next cycle. Unfortunately the cost of
     39 // ensure that the object holding the slot doesn't concurrently
     40 // change to black without the mutator noticing seems prohibitive.
     41 //
     42 // Consider the following example where the mutator writes into
     43 // a slot and then loads the slot's mark bit while the GC thread
     44 // writes to the slot's mark bit and then as part of scanning reads
     45 // the slot.
     46 //
     47 // Initially both [slot] and [slotmark] are 0 (nil)
     48 // Mutator thread          GC thread
     49 // st [slot], ptr          st [slotmark], 1
     50 //
     51 // ld r1, [slotmark]       ld r2, [slot]
     52 //
     53 // Without an expensive memory barrier between the st and the ld, the final
     54 // result on most HW (including 386/amd64) can be r1==r2==0. This is a classic
     55 // example of what can happen when loads are allowed to be reordered with older
     56 // stores (avoiding such reorderings lies at the heart of the classic
     57 // Peterson/Dekker algorithms for mutual exclusion). Rather than require memory
     58 // barriers, which will slow down both the mutator and the GC, we always grey
     59 // the ptr object regardless of the slot's color.
     60 //
     61 // Another place where we intentionally omit memory barriers is when
     62 // accessing mheap_.arena_used to check if a pointer points into the
     63 // heap. On relaxed memory machines, it's possible for a mutator to
     64 // extend the size of the heap by updating arena_used, allocate an
     65 // object from this new region, and publish a pointer to that object,
     66 // but for tracing running on another processor to observe the pointer
     67 // but use the old value of arena_used. In this case, tracing will not
     68 // mark the object, even though it's reachable. However, the mutator
     69 // is guaranteed to execute a write barrier when it publishes the
     70 // pointer, so it will take care of marking the object. A general
     71 // consequence of this is that the garbage collector may cache the
     72 // value of mheap_.arena_used. (See issue #9984.)
     73 //
     74 //
     75 // Stack writes:
     76 //
     77 // The compiler omits write barriers for writes to the current frame,
     78 // but if a stack pointer has been passed down the call stack, the
     79 // compiler will generate a write barrier for writes through that
     80 // pointer (because it doesn't know it's not a heap pointer).
     81 //
     82 // One might be tempted to ignore the write barrier if slot points
     83 // into to the stack. Don't do it! Mark termination only re-scans
     84 // frames that have potentially been active since the concurrent scan,
     85 // so it depends on write barriers to track changes to pointers in
     86 // stack frames that have not been active.
     87 //go:nowritebarrier
     88 func gcmarkwb_m(slot *uintptr, ptr uintptr) {
     89 	if writeBarrierEnabled {
     90 		if ptr != 0 && inheap(ptr) {
     91 			shade(ptr)
     92 		}
     93 	}
     94 }
     95 
     96 // Write barrier calls must not happen during critical GC and scheduler
     97 // related operations. In particular there are times when the GC assumes
     98 // that the world is stopped but scheduler related code is still being
     99 // executed, dealing with syscalls, dealing with putting gs on runnable
    100 // queues and so forth. This code can not execute write barriers because
    101 // the GC might drop them on the floor. Stopping the world involves removing
    102 // the p associated with an m. We use the fact that m.p == nil to indicate
    103 // that we are in one these critical section and throw if the write is of
    104 // a pointer to a heap object.
    105 //go:nosplit
    106 func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
    107 	mp := acquirem()
    108 	if mp.inwb || mp.dying > 0 {
    109 		releasem(mp)
    110 		return
    111 	}
    112 	systemstack(func() {
    113 		if mp.p == 0 && memstats.enablegc && !mp.inwb && inheap(src) {
    114 			throw("writebarrierptr_nostore1 called with mp.p == nil")
    115 		}
    116 		mp.inwb = true
    117 		gcmarkwb_m(dst, src)
    118 	})
    119 	mp.inwb = false
    120 	releasem(mp)
    121 }
    122 
    123 // NOTE: Really dst *unsafe.Pointer, src unsafe.Pointer,
    124 // but if we do that, Go inserts a write barrier on *dst = src.
    125 //go:nosplit
    126 func writebarrierptr(dst *uintptr, src uintptr) {
    127 	*dst = src
    128 	if !writeBarrierEnabled {
    129 		return
    130 	}
    131 	if src != 0 && (src < _PhysPageSize || src == poisonStack) {
    132 		systemstack(func() {
    133 			print("runtime: writebarrierptr *", dst, " = ", hex(src), "\n")
    134 			throw("bad pointer in write barrier")
    135 		})
    136 	}
    137 	writebarrierptr_nostore1(dst, src)
    138 }
    139 
    140 // Like writebarrierptr, but the store has already been applied.
    141 // Do not reapply.
    142 //go:nosplit
    143 func writebarrierptr_nostore(dst *uintptr, src uintptr) {
    144 	if !writeBarrierEnabled {
    145 		return
    146 	}
    147 	if src != 0 && (src < _PhysPageSize || src == poisonStack) {
    148 		systemstack(func() { throw("bad pointer in write barrier") })
    149 	}
    150 	writebarrierptr_nostore1(dst, src)
    151 }
    152 
    153 //go:nosplit
    154 func writebarrierstring(dst *[2]uintptr, src [2]uintptr) {
    155 	writebarrierptr(&dst[0], src[0])
    156 	dst[1] = src[1]
    157 }
    158 
    159 //go:nosplit
    160 func writebarrierslice(dst *[3]uintptr, src [3]uintptr) {
    161 	writebarrierptr(&dst[0], src[0])
    162 	dst[1] = src[1]
    163 	dst[2] = src[2]
    164 }
    165 
    166 //go:nosplit
    167 func writebarrieriface(dst *[2]uintptr, src [2]uintptr) {
    168 	writebarrierptr(&dst[0], src[0])
    169 	writebarrierptr(&dst[1], src[1])
    170 }
    171 
    172 //go:generate go run wbfat_gen.go -- wbfat.go
    173 //
    174 // The above line generates multiword write barriers for
    175 // all the combinations of ptr+scalar up to four words.
    176 // The implementations are written to wbfat.go.
    177 
    178 // typedmemmove copies a value of type t to dst from src.
    179 //go:nosplit
    180 func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
    181 	memmove(dst, src, typ.size)
    182 	if typ.kind&kindNoPointers != 0 {
    183 		return
    184 	}
    185 	heapBitsBulkBarrier(uintptr(dst), typ.size)
    186 }
    187 
    188 //go:linkname reflect_typedmemmove reflect.typedmemmove
    189 func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
    190 	typedmemmove(typ, dst, src)
    191 }
    192 
    193 // typedmemmovepartial is like typedmemmove but assumes that
    194 // dst and src point off bytes into the value and only copies size bytes.
    195 //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial
    196 func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
    197 	memmove(dst, src, size)
    198 	if !writeBarrierEnabled || typ.kind&kindNoPointers != 0 || size < ptrSize || !inheap(uintptr(dst)) {
    199 		return
    200 	}
    201 
    202 	if frag := -off & (ptrSize - 1); frag != 0 {
    203 		dst = add(dst, frag)
    204 		size -= frag
    205 	}
    206 	heapBitsBulkBarrier(uintptr(dst), size&^(ptrSize-1))
    207 }
    208 
    209 // callwritebarrier is invoked at the end of reflectcall, to execute
    210 // write barrier operations to record the fact that a call's return
    211 // values have just been copied to frame, starting at retoffset
    212 // and continuing to framesize. The entire frame (not just the return
    213 // values) is described by typ. Because the copy has already
    214 // happened, we call writebarrierptr_nostore, and we must be careful
    215 // not to be preempted before the write barriers have been run.
    216 //go:nosplit
    217 func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) {
    218 	if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < ptrSize || !inheap(uintptr(frame)) {
    219 		return
    220 	}
    221 	heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
    222 }
    223 
    224 //go:nosplit
    225 func typedslicecopy(typ *_type, dst, src slice) int {
    226 	// TODO(rsc): If typedslicecopy becomes faster than calling
    227 	// typedmemmove repeatedly, consider using during func growslice.
    228 	n := dst.len
    229 	if n > src.len {
    230 		n = src.len
    231 	}
    232 	if n == 0 {
    233 		return 0
    234 	}
    235 	dstp := unsafe.Pointer(dst.array)
    236 	srcp := unsafe.Pointer(src.array)
    237 
    238 	if raceenabled {
    239 		callerpc := getcallerpc(unsafe.Pointer(&typ))
    240 		pc := funcPC(slicecopy)
    241 		racewriterangepc(dstp, uintptr(n)*typ.size, callerpc, pc)
    242 		racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc)
    243 	}
    244 
    245 	// Note: No point in checking typ.kind&kindNoPointers here:
    246 	// compiler only emits calls to typedslicecopy for types with pointers,
    247 	// and growslice and reflect_typedslicecopy check for pointers
    248 	// before calling typedslicecopy.
    249 	if !writeBarrierEnabled {
    250 		memmove(dstp, srcp, uintptr(n)*typ.size)
    251 		return n
    252 	}
    253 
    254 	systemstack(func() {
    255 		if uintptr(srcp) < uintptr(dstp) && uintptr(srcp)+uintptr(n)*typ.size > uintptr(dstp) {
    256 			// Overlap with src before dst.
    257 			// Copy backward, being careful not to move dstp/srcp
    258 			// out of the array they point into.
    259 			dstp = add(dstp, uintptr(n-1)*typ.size)
    260 			srcp = add(srcp, uintptr(n-1)*typ.size)
    261 			i := 0
    262 			for {
    263 				typedmemmove(typ, dstp, srcp)
    264 				if i++; i >= n {
    265 					break
    266 				}
    267 				dstp = add(dstp, -typ.size)
    268 				srcp = add(srcp, -typ.size)
    269 			}
    270 		} else {
    271 			// Copy forward, being careful not to move dstp/srcp
    272 			// out of the array they point into.
    273 			i := 0
    274 			for {
    275 				typedmemmove(typ, dstp, srcp)
    276 				if i++; i >= n {
    277 					break
    278 				}
    279 				dstp = add(dstp, typ.size)
    280 				srcp = add(srcp, typ.size)
    281 			}
    282 		}
    283 	})
    284 	return int(n)
    285 }
    286 
    287 //go:linkname reflect_typedslicecopy reflect.typedslicecopy
    288 func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
    289 	if elemType.kind&kindNoPointers != 0 {
    290 		n := dst.len
    291 		if n > src.len {
    292 			n = src.len
    293 		}
    294 		memmove(dst.array, src.array, uintptr(n)*elemType.size)
    295 		return n
    296 	}
    297 	return typedslicecopy(elemType, dst, src)
    298 }
    299