github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/compile/internal/ssa/writebarrier.go (about) 1 // Copyright 2016 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 package ssa 6 7 import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/obj" 10 "cmd/internal/src" 11 "strings" 12 ) 13 14 // needwb returns whether we need write barrier for store op v. 15 // v must be Store/Move/Zero. 16 func needwb(v *Value) bool { 17 t, ok := v.Aux.(*types.Type) 18 if !ok { 19 v.Fatalf("store aux is not a type: %s", v.LongString()) 20 } 21 if !t.HasHeapPointer() { 22 return false 23 } 24 if IsStackAddr(v.Args[0]) { 25 return false // write on stack doesn't need write barrier 26 } 27 return true 28 } 29 30 // writebarrier pass inserts write barriers for store ops (Store, Move, Zero) 31 // when necessary (the condition above). It rewrites store ops to branches 32 // and runtime calls, like 33 // 34 // if writeBarrier.enabled { 35 // gcWriteBarrier(ptr, val) // Not a regular Go call 36 // } else { 37 // *ptr = val 38 // } 39 // 40 // A sequence of WB stores for many pointer fields of a single type will 41 // be emitted together, with a single branch. 42 func writebarrier(f *Func) { 43 if !f.fe.UseWriteBarrier() { 44 return 45 } 46 47 var sb, sp, wbaddr, const0 *Value 48 var typedmemmove, typedmemclr, gcWriteBarrier *obj.LSym 49 var stores, after []*Value 50 var sset *sparseSet 51 var storeNumber []int32 52 53 for _, b := range f.Blocks { // range loop is safe since the blocks we added contain no stores to expand 54 // first, identify all the stores that need to insert a write barrier. 55 // mark them with WB ops temporarily. record presence of WB ops. 56 nWBops := 0 // count of temporarily created WB ops remaining to be rewritten in the current block 57 for _, v := range b.Values { 58 switch v.Op { 59 case OpStore, OpMove, OpZero: 60 if needwb(v) { 61 switch v.Op { 62 case OpStore: 63 v.Op = OpStoreWB 64 case OpMove: 65 v.Op = OpMoveWB 66 case OpZero: 67 v.Op = OpZeroWB 68 } 69 nWBops++ 70 } 71 } 72 } 73 if nWBops == 0 { 74 continue 75 } 76 77 if wbaddr == nil { 78 // lazily initialize global values for write barrier test and calls 79 // find SB and SP values in entry block 80 initpos := f.Entry.Pos 81 for _, v := range f.Entry.Values { 82 if v.Op == OpSB { 83 sb = v 84 } 85 if v.Op == OpSP { 86 sp = v 87 } 88 if sb != nil && sp != nil { 89 break 90 } 91 } 92 if sb == nil { 93 sb = f.Entry.NewValue0(initpos, OpSB, f.Config.Types.Uintptr) 94 } 95 if sp == nil { 96 sp = f.Entry.NewValue0(initpos, OpSP, f.Config.Types.Uintptr) 97 } 98 wbsym := f.fe.Syslook("writeBarrier") 99 wbaddr = f.Entry.NewValue1A(initpos, OpAddr, f.Config.Types.UInt32Ptr, wbsym, sb) 100 gcWriteBarrier = f.fe.Syslook("gcWriteBarrier") 101 typedmemmove = f.fe.Syslook("typedmemmove") 102 typedmemclr = f.fe.Syslook("typedmemclr") 103 const0 = f.ConstInt32(f.Config.Types.UInt32, 0) 104 105 // allocate auxiliary data structures for computing store order 106 sset = f.newSparseSet(f.NumValues()) 107 defer f.retSparseSet(sset) 108 storeNumber = make([]int32, f.NumValues()) 109 } 110 111 // order values in store order 112 b.Values = storeOrder(b.Values, sset, storeNumber) 113 114 firstSplit := true 115 again: 116 // find the start and end of the last contiguous WB store sequence. 117 // a branch will be inserted there. values after it will be moved 118 // to a new block. 119 var last *Value 120 var start, end int 121 values := b.Values 122 FindSeq: 123 for i := len(values) - 1; i >= 0; i-- { 124 w := values[i] 125 switch w.Op { 126 case OpStoreWB, OpMoveWB, OpZeroWB: 127 start = i 128 if last == nil { 129 last = w 130 end = i + 1 131 } 132 case OpVarDef, OpVarLive, OpVarKill: 133 continue 134 default: 135 if last == nil { 136 continue 137 } 138 break FindSeq 139 } 140 } 141 stores = append(stores[:0], b.Values[start:end]...) // copy to avoid aliasing 142 after = append(after[:0], b.Values[end:]...) 143 b.Values = b.Values[:start] 144 145 // find the memory before the WB stores 146 mem := stores[0].MemoryArg() 147 pos := stores[0].Pos 148 bThen := f.NewBlock(BlockPlain) 149 bElse := f.NewBlock(BlockPlain) 150 bEnd := f.NewBlock(b.Kind) 151 bThen.Pos = pos 152 bElse.Pos = pos 153 bEnd.Pos = b.Pos 154 b.Pos = pos 155 156 // set up control flow for end block 157 bEnd.SetControl(b.Control) 158 bEnd.Likely = b.Likely 159 for _, e := range b.Succs { 160 bEnd.Succs = append(bEnd.Succs, e) 161 e.b.Preds[e.i].b = bEnd 162 } 163 164 // set up control flow for write barrier test 165 // load word, test word, avoiding partial register write from load byte. 166 cfgtypes := &f.Config.Types 167 flag := b.NewValue2(pos, OpLoad, cfgtypes.UInt32, wbaddr, mem) 168 flag = b.NewValue2(pos, OpNeq32, cfgtypes.Bool, flag, const0) 169 b.Kind = BlockIf 170 b.SetControl(flag) 171 b.Likely = BranchUnlikely 172 b.Succs = b.Succs[:0] 173 b.AddEdgeTo(bThen) 174 b.AddEdgeTo(bElse) 175 // TODO: For OpStoreWB and the buffered write barrier, 176 // we could move the write out of the write barrier, 177 // which would lead to fewer branches. We could do 178 // something similar to OpZeroWB, since the runtime 179 // could provide just the barrier half and then we 180 // could unconditionally do an OpZero (which could 181 // also generate better zeroing code). OpMoveWB is 182 // trickier and would require changing how 183 // cgoCheckMemmove works. 184 bThen.AddEdgeTo(bEnd) 185 bElse.AddEdgeTo(bEnd) 186 187 // for each write barrier store, append write barrier version to bThen 188 // and simple store version to bElse 189 memThen := mem 190 memElse := mem 191 for _, w := range stores { 192 ptr := w.Args[0] 193 pos := w.Pos 194 195 var fn *obj.LSym 196 var typ *obj.LSym 197 var val *Value 198 switch w.Op { 199 case OpStoreWB: 200 val = w.Args[1] 201 nWBops-- 202 case OpMoveWB: 203 fn = typedmemmove 204 val = w.Args[1] 205 typ = w.Aux.(*types.Type).Symbol() 206 nWBops-- 207 case OpZeroWB: 208 fn = typedmemclr 209 typ = w.Aux.(*types.Type).Symbol() 210 nWBops-- 211 case OpVarDef, OpVarLive, OpVarKill: 212 } 213 214 // then block: emit write barrier call 215 switch w.Op { 216 case OpStoreWB, OpMoveWB, OpZeroWB: 217 volatile := w.Op == OpMoveWB && isVolatile(val) 218 if w.Op == OpStoreWB { 219 memThen = bThen.NewValue3A(pos, OpWB, types.TypeMem, gcWriteBarrier, ptr, val, memThen) 220 } else { 221 memThen = wbcall(pos, bThen, fn, typ, ptr, val, memThen, sp, sb, volatile) 222 } 223 // Note that we set up a writebarrier function call. 224 f.fe.SetWBPos(pos) 225 case OpVarDef, OpVarLive, OpVarKill: 226 memThen = bThen.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, memThen) 227 } 228 229 // else block: normal store 230 switch w.Op { 231 case OpStoreWB: 232 memElse = bElse.NewValue3A(pos, OpStore, types.TypeMem, w.Aux, ptr, val, memElse) 233 case OpMoveWB: 234 memElse = bElse.NewValue3I(pos, OpMove, types.TypeMem, w.AuxInt, ptr, val, memElse) 235 memElse.Aux = w.Aux 236 case OpZeroWB: 237 memElse = bElse.NewValue2I(pos, OpZero, types.TypeMem, w.AuxInt, ptr, memElse) 238 memElse.Aux = w.Aux 239 case OpVarDef, OpVarLive, OpVarKill: 240 memElse = bElse.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, memElse) 241 } 242 } 243 244 // merge memory 245 // Splice memory Phi into the last memory of the original sequence, 246 // which may be used in subsequent blocks. Other memories in the 247 // sequence must be dead after this block since there can be only 248 // one memory live. 249 bEnd.Values = append(bEnd.Values, last) 250 last.Block = bEnd 251 last.reset(OpPhi) 252 last.Type = types.TypeMem 253 last.AddArg(memThen) 254 last.AddArg(memElse) 255 for _, w := range stores { 256 if w != last { 257 w.resetArgs() 258 } 259 } 260 for _, w := range stores { 261 if w != last { 262 f.freeValue(w) 263 } 264 } 265 266 // put values after the store sequence into the end block 267 bEnd.Values = append(bEnd.Values, after...) 268 for _, w := range after { 269 w.Block = bEnd 270 } 271 272 // Preemption is unsafe between loading the write 273 // barrier-enabled flag and performing the write 274 // because that would allow a GC phase transition, 275 // which would invalidate the flag. Remember the 276 // conditional block so liveness analysis can disable 277 // safe-points. This is somewhat subtle because we're 278 // splitting b bottom-up. 279 if firstSplit { 280 // Add b itself. 281 b.Func.WBLoads = append(b.Func.WBLoads, b) 282 firstSplit = false 283 } else { 284 // We've already split b, so we just pushed a 285 // write barrier test into bEnd. 286 b.Func.WBLoads = append(b.Func.WBLoads, bEnd) 287 } 288 289 // if we have more stores in this block, do this block again 290 if nWBops > 0 { 291 goto again 292 } 293 } 294 } 295 296 // wbcall emits write barrier runtime call in b, returns memory. 297 // if valIsVolatile, it moves val into temp space before making the call. 298 func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Value, valIsVolatile bool) *Value { 299 config := b.Func.Config 300 301 var tmp GCNode 302 if valIsVolatile { 303 // Copy to temp location if the source is volatile (will be clobbered by 304 // a function call). Marshaling the args to typedmemmove might clobber the 305 // value we're trying to move. 306 t := val.Type.Elem() 307 tmp = b.Func.fe.Auto(val.Pos, t) 308 mem = b.NewValue1A(pos, OpVarDef, types.TypeMem, tmp, mem) 309 tmpaddr := b.NewValue2A(pos, OpLocalAddr, t.PtrTo(), tmp, sp, mem) 310 siz := t.Size() 311 mem = b.NewValue3I(pos, OpMove, types.TypeMem, siz, tmpaddr, val, mem) 312 mem.Aux = t 313 val = tmpaddr 314 } 315 316 // put arguments on stack 317 off := config.ctxt.FixedFrameSize() 318 319 if typ != nil { // for typedmemmove 320 taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb) 321 off = round(off, taddr.Type.Alignment()) 322 arg := b.NewValue1I(pos, OpOffPtr, taddr.Type.PtrTo(), off, sp) 323 mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, taddr, mem) 324 off += taddr.Type.Size() 325 } 326 327 off = round(off, ptr.Type.Alignment()) 328 arg := b.NewValue1I(pos, OpOffPtr, ptr.Type.PtrTo(), off, sp) 329 mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, ptr, mem) 330 off += ptr.Type.Size() 331 332 if val != nil { 333 off = round(off, val.Type.Alignment()) 334 arg = b.NewValue1I(pos, OpOffPtr, val.Type.PtrTo(), off, sp) 335 mem = b.NewValue3A(pos, OpStore, types.TypeMem, val.Type, arg, val, mem) 336 off += val.Type.Size() 337 } 338 off = round(off, config.PtrSize) 339 340 // issue call 341 mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, fn, mem) 342 mem.AuxInt = off - config.ctxt.FixedFrameSize() 343 344 if valIsVolatile { 345 mem = b.NewValue1A(pos, OpVarKill, types.TypeMem, tmp, mem) // mark temp dead 346 } 347 348 return mem 349 } 350 351 // round to a multiple of r, r is a power of 2 352 func round(o int64, r int64) int64 { 353 return (o + r - 1) &^ (r - 1) 354 } 355 356 // IsStackAddr returns whether v is known to be an address of a stack slot 357 func IsStackAddr(v *Value) bool { 358 for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy { 359 v = v.Args[0] 360 } 361 switch v.Op { 362 case OpSP, OpLocalAddr: 363 return true 364 } 365 return false 366 } 367 368 // IsSanitizerSafeAddr reports whether v is known to be an address 369 // that doesn't need instrumentation. 370 func IsSanitizerSafeAddr(v *Value) bool { 371 for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy { 372 v = v.Args[0] 373 } 374 switch v.Op { 375 case OpSP, OpLocalAddr: 376 // Stack addresses are always safe. 377 return true 378 case OpITab, OpStringPtr, OpGetClosurePtr: 379 // Itabs, string data, and closure fields are 380 // read-only once initialized. 381 return true 382 case OpAddr: 383 sym := v.Aux.(*obj.LSym) 384 // TODO(mdempsky): Find a cleaner way to 385 // detect this. It would be nice if we could 386 // test sym.Type==objabi.SRODATA, but we don't 387 // initialize sym.Type until after function 388 // compilation. 389 if strings.HasPrefix(sym.Name, `"".statictmp_`) { 390 return true 391 } 392 } 393 return false 394 } 395 396 // isVolatile returns whether v is a pointer to argument region on stack which 397 // will be clobbered by a function call. 398 func isVolatile(v *Value) bool { 399 for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy { 400 v = v.Args[0] 401 } 402 return v.Op == OpSP 403 }