Home | History | Annotate | Download | only in ssa
      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 	"fmt"
     10 )
     11 
     12 // an edgeMem records a backedge, together with the memory
     13 // phi functions at the target of the backedge that must
     14 // be updated when a rescheduling check replaces the backedge.
     15 type edgeMem struct {
     16 	e Edge
     17 	m *Value // phi for memory at dest of e
     18 }
     19 
     20 // a rewriteTarget is a value-argindex pair indicating
     21 // where a rewrite is applied.  Note that this is for values,
     22 // not for block controls, because block controls are not targets
     23 // for the rewrites performed in inserting rescheduling checks.
     24 type rewriteTarget struct {
     25 	v *Value
     26 	i int
     27 }
     28 
     29 type rewrite struct {
     30 	before, after *Value          // before is the expected value before rewrite, after is the new value installed.
     31 	rewrites      []rewriteTarget // all the targets for this rewrite.
     32 }
     33 
     34 func (r *rewrite) String() string {
     35 	s := "\n\tbefore=" + r.before.String() + ", after=" + r.after.String()
     36 	for _, rw := range r.rewrites {
     37 		s += ", (i=" + fmt.Sprint(rw.i) + ", v=" + rw.v.LongString() + ")"
     38 	}
     39 	s += "\n"
     40 	return s
     41 }
     42 
     43 // insertLoopReschedChecks inserts rescheduling checks on loop backedges.
     44 func insertLoopReschedChecks(f *Func) {
     45 	// TODO: when split information is recorded in export data, insert checks only on backedges that can be reached on a split-call-free path.
     46 
     47 	// Loop reschedule checks compare the stack pointer with
     48 	// the per-g stack bound.  If the pointer appears invalid,
     49 	// that means a reschedule check is needed.
     50 	//
     51 	// Steps:
     52 	// 1. locate backedges.
     53 	// 2. Record memory definitions at block end so that
     54 	//    the SSA graph for mem can be properly modified.
     55 	// 3. Ensure that phi functions that will-be-needed for mem
     56 	//    are present in the graph, initially with trivial inputs.
     57 	// 4. Record all to-be-modified uses of mem;
     58 	//    apply modifications (split into two steps to simplify and
     59 	//    avoided nagging order-dependences).
     60 	// 5. Rewrite backedges to include reschedule check,
     61 	//    and modify destination phi function appropriately with new
     62 	//    definitions for mem.
     63 
     64 	if f.NoSplit { // nosplit functions don't reschedule.
     65 		return
     66 	}
     67 
     68 	backedges := backedges(f)
     69 	if len(backedges) == 0 { // no backedges means no rescheduling checks.
     70 		return
     71 	}
     72 
     73 	lastMems := findLastMems(f)
     74 
     75 	idom := f.Idom()
     76 	po := f.postorder()
     77 	// The ordering in the dominator tree matters; it's important that
     78 	// the walk of the dominator tree also be a preorder (i.e., a node is
     79 	// visited only after all its non-backedge predecessors have been visited).
     80 	sdom := newSparseOrderedTree(f, idom, po)
     81 
     82 	if f.pass.debug > 1 {
     83 		fmt.Printf("before %s = %s\n", f.Name, sdom.treestructure(f.Entry))
     84 	}
     85 
     86 	tofixBackedges := []edgeMem{}
     87 
     88 	for _, e := range backedges { // TODO: could filter here by calls in loops, if declared and inferred nosplit are recorded in export data.
     89 		tofixBackedges = append(tofixBackedges, edgeMem{e, nil})
     90 	}
     91 
     92 	// It's possible that there is no memory state (no global/pointer loads/stores or calls)
     93 	if lastMems[f.Entry.ID] == nil {
     94 		lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Pos, OpInitMem, types.TypeMem)
     95 	}
     96 
     97 	memDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, the mem def seen at its bottom. Could be from earlier block.
     98 
     99 	// Propagate last mem definitions forward through successor blocks.
    100 	for i := len(po) - 1; i >= 0; i-- {
    101 		b := po[i]
    102 		mem := lastMems[b.ID]
    103 		for j := 0; mem == nil; j++ { // if there's no def, then there's no phi, so the visible mem is identical in all predecessors.
    104 			// loop because there might be backedges that haven't been visited yet.
    105 			mem = memDefsAtBlockEnds[b.Preds[j].b.ID]
    106 		}
    107 		memDefsAtBlockEnds[b.ID] = mem
    108 		if f.pass.debug > 2 {
    109 			fmt.Printf("memDefsAtBlockEnds[%s] = %s\n", b, mem)
    110 		}
    111 	}
    112 
    113 	// Maps from block to newly-inserted phi function in block.
    114 	newmemphis := make(map[*Block]rewrite)
    115 
    116 	// Insert phi functions as necessary for future changes to flow graph.
    117 	for i, emc := range tofixBackedges {
    118 		e := emc.e
    119 		h := e.b
    120 
    121 		// find the phi function for the memory input at "h", if there is one.
    122 		var headerMemPhi *Value // look for header mem phi
    123 
    124 		for _, v := range h.Values {
    125 			if v.Op == OpPhi && v.Type.IsMemory() {
    126 				headerMemPhi = v
    127 			}
    128 		}
    129 
    130 		if headerMemPhi == nil {
    131 			// if the header is nil, make a trivial phi from the dominator
    132 			mem0 := memDefsAtBlockEnds[idom[h.ID].ID]
    133 			headerMemPhi = newPhiFor(h, mem0)
    134 			newmemphis[h] = rewrite{before: mem0, after: headerMemPhi}
    135 			addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis, sdom)
    136 
    137 		}
    138 		tofixBackedges[i].m = headerMemPhi
    139 
    140 	}
    141 	if f.pass.debug > 0 {
    142 		for b, r := range newmemphis {
    143 			fmt.Printf("before b=%s, rewrite=%s\n", b, r.String())
    144 		}
    145 	}
    146 
    147 	// dfPhiTargets notes inputs to phis in dominance frontiers that should not
    148 	// be rewritten as part of the dominated children of some outer rewrite.
    149 	dfPhiTargets := make(map[rewriteTarget]bool)
    150 
    151 	rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis, dfPhiTargets, sdom)
    152 
    153 	if f.pass.debug > 0 {
    154 		for b, r := range newmemphis {
    155 			fmt.Printf("after b=%s, rewrite=%s\n", b, r.String())
    156 		}
    157 	}
    158 
    159 	// Apply collected rewrites.
    160 	for _, r := range newmemphis {
    161 		for _, rw := range r.rewrites {
    162 			rw.v.SetArg(rw.i, r.after)
    163 		}
    164 	}
    165 
    166 	// Rewrite backedges to include reschedule checks.
    167 	for _, emc := range tofixBackedges {
    168 		e := emc.e
    169 		headerMemPhi := emc.m
    170 		h := e.b
    171 		i := e.i
    172 		p := h.Preds[i]
    173 		bb := p.b
    174 		mem0 := headerMemPhi.Args[i]
    175 		// bb e->p h,
    176 		// Because we're going to insert a rare-call, make sure the
    177 		// looping edge still looks likely.
    178 		likely := BranchLikely
    179 		if p.i != 0 {
    180 			likely = BranchUnlikely
    181 		}
    182 		bb.Likely = likely
    183 
    184 		// rewrite edge to include reschedule check
    185 		// existing edges:
    186 		//
    187 		// bb.Succs[p.i] == Edge{h, i}
    188 		// h.Preds[i] == p == Edge{bb,p.i}
    189 		//
    190 		// new block(s):
    191 		// test:
    192 		//    if sp < g.limit { goto sched }
    193 		//    goto join
    194 		// sched:
    195 		//    mem1 := call resched (mem0)
    196 		//    goto join
    197 		// join:
    198 		//    mem2 := phi(mem0, mem1)
    199 		//    goto h
    200 		//
    201 		// and correct arg i of headerMemPhi and headerCtrPhi
    202 		//
    203 		// EXCEPT: join block containing only phi functions is bad
    204 		// for the register allocator.  Therefore, there is no
    205 		// join, and branches targeting join must instead target
    206 		// the header, and the other phi functions within header are
    207 		// adjusted for the additional input.
    208 
    209 		test := f.NewBlock(BlockIf)
    210 		sched := f.NewBlock(BlockPlain)
    211 
    212 		test.Pos = bb.Pos
    213 		sched.Pos = bb.Pos
    214 
    215 		// if sp < g.limit { goto sched }
    216 		// goto header
    217 
    218 		cfgtypes := &f.Config.Types
    219 		pt := cfgtypes.Uintptr
    220 		g := test.NewValue1(bb.Pos, OpGetG, pt, mem0)
    221 		sp := test.NewValue0(bb.Pos, OpSP, pt)
    222 		cmpOp := OpLess64U
    223 		if pt.Size() == 4 {
    224 			cmpOp = OpLess32U
    225 		}
    226 		limaddr := test.NewValue1I(bb.Pos, OpOffPtr, pt, 2*pt.Size(), g)
    227 		lim := test.NewValue2(bb.Pos, OpLoad, pt, limaddr, mem0)
    228 		cmp := test.NewValue2(bb.Pos, cmpOp, cfgtypes.Bool, sp, lim)
    229 		test.SetControl(cmp)
    230 
    231 		// if true, goto sched
    232 		test.AddEdgeTo(sched)
    233 
    234 		// if false, rewrite edge to header.
    235 		// do NOT remove+add, because that will perturb all the other phi functions
    236 		// as well as messing up other edges to the header.
    237 		test.Succs = append(test.Succs, Edge{h, i})
    238 		h.Preds[i] = Edge{test, 1}
    239 		headerMemPhi.SetArg(i, mem0)
    240 
    241 		test.Likely = BranchUnlikely
    242 
    243 		// sched:
    244 		//    mem1 := call resched (mem0)
    245 		//    goto header
    246 		resched := f.fe.Syslook("goschedguarded")
    247 		mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, resched, mem0)
    248 		sched.AddEdgeTo(h)
    249 		headerMemPhi.AddArg(mem1)
    250 
    251 		bb.Succs[p.i] = Edge{test, 0}
    252 		test.Preds = append(test.Preds, Edge{bb, p.i})
    253 
    254 		// Must correct all the other phi functions in the header for new incoming edge.
    255 		// Except for mem phis, it will be the same value seen on the original
    256 		// backedge at index i.
    257 		for _, v := range h.Values {
    258 			if v.Op == OpPhi && v != headerMemPhi {
    259 				v.AddArg(v.Args[i])
    260 			}
    261 		}
    262 	}
    263 
    264 	f.invalidateCFG()
    265 
    266 	if f.pass.debug > 1 {
    267 		sdom = newSparseTree(f, f.Idom())
    268 		fmt.Printf("after %s = %s\n", f.Name, sdom.treestructure(f.Entry))
    269 	}
    270 }
    271 
    272 // newPhiFor inserts a new Phi function into b,
    273 // with all inputs set to v.
    274 func newPhiFor(b *Block, v *Value) *Value {
    275 	phiV := b.NewValue0(b.Pos, OpPhi, v.Type)
    276 
    277 	for range b.Preds {
    278 		phiV.AddArg(v)
    279 	}
    280 	return phiV
    281 }
    282 
    283 // rewriteNewPhis updates newphis[h] to record all places where the new phi function inserted
    284 // in block h will replace a previous definition.  Block b is the block currently being processed;
    285 // if b has its own phi definition then it takes the place of h.
    286 // defsForUses provides information about other definitions of the variable that are present
    287 // (and if nil, indicates that the variable is no longer live)
    288 // sdom must yield a preorder of the flow graph if recursively walked, root-to-children.
    289 // The result of newSparseOrderedTree with order supplied by a dfs-postorder satisfies this
    290 // requirement.
    291 func rewriteNewPhis(h, b *Block, f *Func, defsForUses []*Value, newphis map[*Block]rewrite, dfPhiTargets map[rewriteTarget]bool, sdom SparseTree) {
    292 	// If b is a block with a new phi, then a new rewrite applies below it in the dominator tree.
    293 	if _, ok := newphis[b]; ok {
    294 		h = b
    295 	}
    296 	change := newphis[h]
    297 	x := change.before
    298 	y := change.after
    299 
    300 	// Apply rewrites to this block
    301 	if x != nil { // don't waste time on the common case of no definition.
    302 		p := &change.rewrites
    303 		for _, v := range b.Values {
    304 			if v == y { // don't rewrite self -- phi inputs are handled below.
    305 				continue
    306 			}
    307 			for i, w := range v.Args {
    308 				if w != x {
    309 					continue
    310 				}
    311 				tgt := rewriteTarget{v, i}
    312 
    313 				// It's possible dominated control flow will rewrite this instead.
    314 				// Visiting in preorder (a property of how sdom was constructed)
    315 				// ensures that these are seen in the proper order.
    316 				if dfPhiTargets[tgt] {
    317 					continue
    318 				}
    319 				*p = append(*p, tgt)
    320 				if f.pass.debug > 1 {
    321 					fmt.Printf("added block target for h=%v, b=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n",
    322 						h, b, x, y, v, i)
    323 				}
    324 			}
    325 		}
    326 
    327 		// Rewrite appropriate inputs of phis reached in successors
    328 		// in dominance frontier, self, and dominated.
    329 		// If the variable def reaching uses in b is itself defined in b, then the new phi function
    330 		// does not reach the successors of b.  (This assumes a bit about the structure of the
    331 		// phi use-def graph, but it's true for memory.)
    332 		if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b {
    333 			for _, e := range b.Succs {
    334 				s := e.b
    335 
    336 				for _, v := range s.Values {
    337 					if v.Op == OpPhi && v.Args[e.i] == x {
    338 						tgt := rewriteTarget{v, e.i}
    339 						*p = append(*p, tgt)
    340 						dfPhiTargets[tgt] = true
    341 						if f.pass.debug > 1 {
    342 							fmt.Printf("added phi target for h=%v, b=%v, s=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n",
    343 								h, b, s, x, y, v.LongString(), e.i)
    344 						}
    345 						break
    346 					}
    347 				}
    348 			}
    349 		}
    350 		newphis[h] = change
    351 	}
    352 
    353 	for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
    354 		rewriteNewPhis(h, c, f, defsForUses, newphis, dfPhiTargets, sdom) // TODO: convert to explicit stack from recursion.
    355 	}
    356 }
    357 
    358 // addDFphis creates new trivial phis that are necessary to correctly reflect (within SSA)
    359 // a new definition for variable "x" inserted at h (usually but not necessarily a phi).
    360 // These new phis can only occur at the dominance frontier of h; block s is in the dominance
    361 // frontier of h if h does not strictly dominate s and if s is a successor of a block b where
    362 // either b = h or h strictly dominates b.
    363 // These newly created phis are themselves new definitions that may require addition of their
    364 // own trivial phi functions in their own dominance frontier, and this is handled recursively.
    365 func addDFphis(x *Value, h, b *Block, f *Func, defForUses []*Value, newphis map[*Block]rewrite, sdom SparseTree) {
    366 	oldv := defForUses[b.ID]
    367 	if oldv != x { // either a new definition replacing x, or nil if it is proven that there are no uses reachable from b
    368 		return
    369 	}
    370 	idom := f.Idom()
    371 outer:
    372 	for _, e := range b.Succs {
    373 		s := e.b
    374 		// check phi functions in the dominance frontier
    375 		if sdom.isAncestor(h, s) {
    376 			continue // h dominates s, successor of b, therefore s is not in the frontier.
    377 		}
    378 		if _, ok := newphis[s]; ok {
    379 			continue // successor s of b already has a new phi function, so there is no need to add another.
    380 		}
    381 		if x != nil {
    382 			for _, v := range s.Values {
    383 				if v.Op == OpPhi && v.Args[e.i] == x {
    384 					continue outer // successor s of b has an old phi function, so there is no need to add another.
    385 				}
    386 			}
    387 		}
    388 
    389 		old := defForUses[idom[s.ID].ID] // new phi function is correct-but-redundant, combining value "old" on all inputs.
    390 		headerPhi := newPhiFor(s, old)
    391 		// the new phi will replace "old" in block s and all blocks dominated by s.
    392 		newphis[s] = rewrite{before: old, after: headerPhi} // record new phi, to have inputs labeled "old" rewritten to "headerPhi"
    393 		addDFphis(old, s, s, f, defForUses, newphis, sdom)  // the new definition may also create new phi functions.
    394 	}
    395 	for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling {
    396 		addDFphis(x, h, c, f, defForUses, newphis, sdom) // TODO: convert to explicit stack from recursion.
    397 	}
    398 }
    399 
    400 // findLastMems maps block ids to last memory-output op in a block, if any
    401 func findLastMems(f *Func) []*Value {
    402 
    403 	var stores []*Value
    404 	lastMems := make([]*Value, f.NumBlocks())
    405 	storeUse := f.newSparseSet(f.NumValues())
    406 	defer f.retSparseSet(storeUse)
    407 	for _, b := range f.Blocks {
    408 		// Find all the stores in this block. Categorize their uses:
    409 		//  storeUse contains stores which are used by a subsequent store.
    410 		storeUse.clear()
    411 		stores = stores[:0]
    412 		var memPhi *Value
    413 		for _, v := range b.Values {
    414 			if v.Op == OpPhi {
    415 				if v.Type.IsMemory() {
    416 					memPhi = v
    417 				}
    418 				continue
    419 			}
    420 			if v.Type.IsMemory() {
    421 				stores = append(stores, v)
    422 				for _, a := range v.Args {
    423 					if a.Block == b && a.Type.IsMemory() {
    424 						storeUse.add(a.ID)
    425 					}
    426 				}
    427 			}
    428 		}
    429 		if len(stores) == 0 {
    430 			lastMems[b.ID] = memPhi
    431 			continue
    432 		}
    433 
    434 		// find last store in the block
    435 		var last *Value
    436 		for _, v := range stores {
    437 			if storeUse.contains(v.ID) {
    438 				continue
    439 			}
    440 			if last != nil {
    441 				b.Fatalf("two final stores - simultaneous live stores %s %s", last, v)
    442 			}
    443 			last = v
    444 		}
    445 		if last == nil {
    446 			b.Fatalf("no last store found - cycle?")
    447 		}
    448 		lastMems[b.ID] = last
    449 	}
    450 	return lastMems
    451 }
    452 
    453 type backedgesState struct {
    454 	b *Block
    455 	i int
    456 }
    457 
    458 // backedges returns a slice of successor edges that are back
    459 // edges.  For reducible loops, edge.b is the header.
    460 func backedges(f *Func) []Edge {
    461 	edges := []Edge{}
    462 	mark := make([]markKind, f.NumBlocks())
    463 	stack := []backedgesState{}
    464 
    465 	mark[f.Entry.ID] = notExplored
    466 	stack = append(stack, backedgesState{f.Entry, 0})
    467 
    468 	for len(stack) > 0 {
    469 		l := len(stack)
    470 		x := stack[l-1]
    471 		if x.i < len(x.b.Succs) {
    472 			e := x.b.Succs[x.i]
    473 			stack[l-1].i++
    474 			s := e.b
    475 			if mark[s.ID] == notFound {
    476 				mark[s.ID] = notExplored
    477 				stack = append(stack, backedgesState{s, 0})
    478 			} else if mark[s.ID] == notExplored {
    479 				edges = append(edges, e)
    480 			}
    481 		} else {
    482 			mark[x.b.ID] = done
    483 			stack = stack[0 : l-1]
    484 		}
    485 	}
    486 	return edges
    487 }
    488