github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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/internal/obj"
     9  	"cmd/internal/src"
    10  )
    11  
    12  // needwb returns whether we need write barrier for store op v.
    13  // v must be Store/Move/Zero.
    14  func needwb(v *Value) bool {
    15  	t, ok := v.Aux.(Type)
    16  	if !ok {
    17  		v.Fatalf("store aux is not a type: %s", v.LongString())
    18  	}
    19  	if !t.HasPointer() {
    20  		return false
    21  	}
    22  	if IsStackAddr(v.Args[0]) {
    23  		return false // write on stack doesn't need write barrier
    24  	}
    25  	return true
    26  }
    27  
    28  // writebarrier pass inserts write barriers for store ops (Store, Move, Zero)
    29  // when necessary (the condition above). It rewrites store ops to branches
    30  // and runtime calls, like
    31  //
    32  // if writeBarrier.enabled {
    33  //   writebarrierptr(ptr, val)
    34  // } else {
    35  //   *ptr = val
    36  // }
    37  //
    38  // A sequence of WB stores for many pointer fields of a single type will
    39  // be emitted together, with a single branch.
    40  func writebarrier(f *Func) {
    41  	if !f.fe.UseWriteBarrier() {
    42  		return
    43  	}
    44  
    45  	var sb, sp, wbaddr, const0 *Value
    46  	var writebarrierptr, typedmemmove, typedmemclr *obj.LSym
    47  	var stores, after []*Value
    48  	var sset *sparseSet
    49  	var storeNumber []int32
    50  
    51  	for _, b := range f.Blocks { // range loop is safe since the blocks we added contain no stores to expand
    52  		// first, identify all the stores that need to insert a write barrier.
    53  		// mark them with WB ops temporarily. record presence of WB ops.
    54  		hasStore := false
    55  		for _, v := range b.Values {
    56  			switch v.Op {
    57  			case OpStore, OpMove, OpZero:
    58  				if needwb(v) {
    59  					switch v.Op {
    60  					case OpStore:
    61  						v.Op = OpStoreWB
    62  					case OpMove:
    63  						v.Op = OpMoveWB
    64  					case OpZero:
    65  						v.Op = OpZeroWB
    66  					}
    67  					hasStore = true
    68  				}
    69  			}
    70  		}
    71  		if !hasStore {
    72  			continue
    73  		}
    74  
    75  		if wbaddr == nil {
    76  			// lazily initialize global values for write barrier test and calls
    77  			// find SB and SP values in entry block
    78  			initpos := f.Entry.Pos
    79  			for _, v := range f.Entry.Values {
    80  				if v.Op == OpSB {
    81  					sb = v
    82  				}
    83  				if v.Op == OpSP {
    84  					sp = v
    85  				}
    86  				if sb != nil && sp != nil {
    87  					break
    88  				}
    89  			}
    90  			if sb == nil {
    91  				sb = f.Entry.NewValue0(initpos, OpSB, f.Config.Types.Uintptr)
    92  			}
    93  			if sp == nil {
    94  				sp = f.Entry.NewValue0(initpos, OpSP, f.Config.Types.Uintptr)
    95  			}
    96  			wbsym := &ExternSymbol{Sym: f.fe.Syslook("writeBarrier")}
    97  			wbaddr = f.Entry.NewValue1A(initpos, OpAddr, f.Config.Types.UInt32Ptr, wbsym, sb)
    98  			writebarrierptr = f.fe.Syslook("writebarrierptr")
    99  			typedmemmove = f.fe.Syslook("typedmemmove")
   100  			typedmemclr = f.fe.Syslook("typedmemclr")
   101  			const0 = f.ConstInt32(initpos, f.Config.Types.UInt32, 0)
   102  
   103  			// allocate auxiliary data structures for computing store order
   104  			sset = f.newSparseSet(f.NumValues())
   105  			defer f.retSparseSet(sset)
   106  			storeNumber = make([]int32, f.NumValues())
   107  		}
   108  
   109  		// order values in store order
   110  		b.Values = storeOrder(b.Values, sset, storeNumber)
   111  
   112  	again:
   113  		// find the start and end of the last contiguous WB store sequence.
   114  		// a branch will be inserted there. values after it will be moved
   115  		// to a new block.
   116  		var last *Value
   117  		var start, end int
   118  		values := b.Values
   119  		for i := len(values) - 1; i >= 0; i-- {
   120  			w := values[i]
   121  			if w.Op == OpStoreWB || w.Op == OpMoveWB || w.Op == OpZeroWB {
   122  				if last == nil {
   123  					last = w
   124  					end = i + 1
   125  				}
   126  			} else {
   127  				if last != nil {
   128  					start = i + 1
   129  					break
   130  				}
   131  			}
   132  		}
   133  		stores = append(stores[:0], b.Values[start:end]...) // copy to avoid aliasing
   134  		after = append(after[:0], b.Values[end:]...)
   135  		b.Values = b.Values[:start]
   136  
   137  		// find the memory before the WB stores
   138  		mem := stores[0].MemoryArg()
   139  		pos := stores[0].Pos
   140  		bThen := f.NewBlock(BlockPlain)
   141  		bElse := f.NewBlock(BlockPlain)
   142  		bEnd := f.NewBlock(b.Kind)
   143  		bThen.Pos = pos
   144  		bElse.Pos = pos
   145  		bEnd.Pos = b.Pos
   146  		b.Pos = pos
   147  
   148  		// set up control flow for end block
   149  		bEnd.SetControl(b.Control)
   150  		bEnd.Likely = b.Likely
   151  		for _, e := range b.Succs {
   152  			bEnd.Succs = append(bEnd.Succs, e)
   153  			e.b.Preds[e.i].b = bEnd
   154  		}
   155  
   156  		// set up control flow for write barrier test
   157  		// load word, test word, avoiding partial register write from load byte.
   158  		types := &f.Config.Types
   159  		flag := b.NewValue2(pos, OpLoad, types.UInt32, wbaddr, mem)
   160  		flag = b.NewValue2(pos, OpNeq32, types.Bool, flag, const0)
   161  		b.Kind = BlockIf
   162  		b.SetControl(flag)
   163  		b.Likely = BranchUnlikely
   164  		b.Succs = b.Succs[:0]
   165  		b.AddEdgeTo(bThen)
   166  		b.AddEdgeTo(bElse)
   167  		bThen.AddEdgeTo(bEnd)
   168  		bElse.AddEdgeTo(bEnd)
   169  
   170  		// for each write barrier store, append write barrier version to bThen
   171  		// and simple store version to bElse
   172  		memThen := mem
   173  		memElse := mem
   174  		for _, w := range stores {
   175  			ptr := w.Args[0]
   176  			pos := w.Pos
   177  
   178  			var fn *obj.LSym
   179  			var typ *ExternSymbol
   180  			var val *Value
   181  			switch w.Op {
   182  			case OpStoreWB:
   183  				fn = writebarrierptr
   184  				val = w.Args[1]
   185  			case OpMoveWB:
   186  				fn = typedmemmove
   187  				val = w.Args[1]
   188  				typ = &ExternSymbol{Sym: w.Aux.(Type).Symbol()}
   189  			case OpZeroWB:
   190  				fn = typedmemclr
   191  				typ = &ExternSymbol{Sym: w.Aux.(Type).Symbol()}
   192  			}
   193  
   194  			// then block: emit write barrier call
   195  			volatile := w.Op == OpMoveWB && isVolatile(val)
   196  			memThen = wbcall(pos, bThen, fn, typ, ptr, val, memThen, sp, sb, volatile)
   197  
   198  			// else block: normal store
   199  			switch w.Op {
   200  			case OpStoreWB:
   201  				memElse = bElse.NewValue3A(pos, OpStore, TypeMem, w.Aux, ptr, val, memElse)
   202  			case OpMoveWB:
   203  				memElse = bElse.NewValue3I(pos, OpMove, TypeMem, w.AuxInt, ptr, val, memElse)
   204  				memElse.Aux = w.Aux
   205  			case OpZeroWB:
   206  				memElse = bElse.NewValue2I(pos, OpZero, TypeMem, w.AuxInt, ptr, memElse)
   207  				memElse.Aux = w.Aux
   208  			}
   209  
   210  			if !f.WBPos.IsKnown() {
   211  				f.WBPos = pos
   212  			}
   213  			if f.fe.Debug_wb() {
   214  				f.Warnl(pos, "write barrier")
   215  			}
   216  		}
   217  
   218  		// merge memory
   219  		// Splice memory Phi into the last memory of the original sequence,
   220  		// which may be used in subsequent blocks. Other memories in the
   221  		// sequence must be dead after this block since there can be only
   222  		// one memory live.
   223  		bEnd.Values = append(bEnd.Values, last)
   224  		last.Block = bEnd
   225  		last.reset(OpPhi)
   226  		last.Type = TypeMem
   227  		last.AddArg(memThen)
   228  		last.AddArg(memElse)
   229  		for _, w := range stores {
   230  			if w != last {
   231  				w.resetArgs()
   232  			}
   233  		}
   234  		for _, w := range stores {
   235  			if w != last {
   236  				f.freeValue(w)
   237  			}
   238  		}
   239  
   240  		// put values after the store sequence into the end block
   241  		bEnd.Values = append(bEnd.Values, after...)
   242  		for _, w := range after {
   243  			w.Block = bEnd
   244  		}
   245  
   246  		// if we have more stores in this block, do this block again
   247  		for _, w := range b.Values {
   248  			if w.Op == OpStoreWB || w.Op == OpMoveWB || w.Op == OpZeroWB {
   249  				goto again
   250  			}
   251  		}
   252  	}
   253  }
   254  
   255  // wbcall emits write barrier runtime call in b, returns memory.
   256  // if valIsVolatile, it moves val into temp space before making the call.
   257  func wbcall(pos src.XPos, b *Block, fn *obj.LSym, typ *ExternSymbol, ptr, val, mem, sp, sb *Value, valIsVolatile bool) *Value {
   258  	config := b.Func.Config
   259  
   260  	var tmp GCNode
   261  	if valIsVolatile {
   262  		// Copy to temp location if the source is volatile (will be clobbered by
   263  		// a function call). Marshaling the args to typedmemmove might clobber the
   264  		// value we're trying to move.
   265  		t := val.Type.ElemType()
   266  		tmp = b.Func.fe.Auto(val.Pos, t)
   267  		aux := &AutoSymbol{Node: tmp}
   268  		mem = b.NewValue1A(pos, OpVarDef, TypeMem, tmp, mem)
   269  		tmpaddr := b.NewValue1A(pos, OpAddr, t.PtrTo(), aux, sp)
   270  		siz := t.Size()
   271  		mem = b.NewValue3I(pos, OpMove, TypeMem, siz, tmpaddr, val, mem)
   272  		mem.Aux = t
   273  		val = tmpaddr
   274  	}
   275  
   276  	// put arguments on stack
   277  	off := config.ctxt.FixedFrameSize()
   278  
   279  	if typ != nil { // for typedmemmove
   280  		taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
   281  		off = round(off, taddr.Type.Alignment())
   282  		arg := b.NewValue1I(pos, OpOffPtr, taddr.Type.PtrTo(), off, sp)
   283  		mem = b.NewValue3A(pos, OpStore, TypeMem, ptr.Type, arg, taddr, mem)
   284  		off += taddr.Type.Size()
   285  	}
   286  
   287  	off = round(off, ptr.Type.Alignment())
   288  	arg := b.NewValue1I(pos, OpOffPtr, ptr.Type.PtrTo(), off, sp)
   289  	mem = b.NewValue3A(pos, OpStore, TypeMem, ptr.Type, arg, ptr, mem)
   290  	off += ptr.Type.Size()
   291  
   292  	if val != nil {
   293  		off = round(off, val.Type.Alignment())
   294  		arg = b.NewValue1I(pos, OpOffPtr, val.Type.PtrTo(), off, sp)
   295  		mem = b.NewValue3A(pos, OpStore, TypeMem, val.Type, arg, val, mem)
   296  		off += val.Type.Size()
   297  	}
   298  	off = round(off, config.PtrSize)
   299  
   300  	// issue call
   301  	mem = b.NewValue1A(pos, OpStaticCall, TypeMem, fn, mem)
   302  	mem.AuxInt = off - config.ctxt.FixedFrameSize()
   303  
   304  	if valIsVolatile {
   305  		mem = b.NewValue1A(pos, OpVarKill, TypeMem, tmp, mem) // mark temp dead
   306  	}
   307  
   308  	return mem
   309  }
   310  
   311  // round to a multiple of r, r is a power of 2
   312  func round(o int64, r int64) int64 {
   313  	return (o + r - 1) &^ (r - 1)
   314  }
   315  
   316  // IsStackAddr returns whether v is known to be an address of a stack slot
   317  func IsStackAddr(v *Value) bool {
   318  	for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
   319  		v = v.Args[0]
   320  	}
   321  	switch v.Op {
   322  	case OpSP:
   323  		return true
   324  	case OpAddr:
   325  		return v.Args[0].Op == OpSP
   326  	}
   327  	return false
   328  }
   329  
   330  // isVolatile returns whether v is a pointer to argument region on stack which
   331  // will be clobbered by a function call.
   332  func isVolatile(v *Value) bool {
   333  	for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
   334  		v = v.Args[0]
   335  	}
   336  	return v.Op == OpSP
   337  }