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