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  }