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