|  | // Copyright 2016 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package ssa | 
|  |  | 
|  | import ( | 
|  | "cmd/compile/internal/types" | 
|  | "fmt" | 
|  | ) | 
|  |  | 
|  | // an edgeMem records a backedge, together with the memory | 
|  | // phi functions at the target of the backedge that must | 
|  | // be updated when a rescheduling check replaces the backedge. | 
|  | type edgeMem struct { | 
|  | e Edge | 
|  | m *Value // phi for memory at dest of e | 
|  | } | 
|  |  | 
|  | // a rewriteTarget is a value-argindex pair indicating | 
|  | // where a rewrite is applied.  Note that this is for values, | 
|  | // not for block controls, because block controls are not targets | 
|  | // for the rewrites performed in inserting rescheduling checks. | 
|  | type rewriteTarget struct { | 
|  | v *Value | 
|  | i int | 
|  | } | 
|  |  | 
|  | type rewrite struct { | 
|  | before, after *Value          // before is the expected value before rewrite, after is the new value installed. | 
|  | rewrites      []rewriteTarget // all the targets for this rewrite. | 
|  | } | 
|  |  | 
|  | func (r *rewrite) String() string { | 
|  | s := "\n\tbefore=" + r.before.String() + ", after=" + r.after.String() | 
|  | for _, rw := range r.rewrites { | 
|  | s += ", (i=" + fmt.Sprint(rw.i) + ", v=" + rw.v.LongString() + ")" | 
|  | } | 
|  | s += "\n" | 
|  | return s | 
|  | } | 
|  |  | 
|  | // insertLoopReschedChecks inserts rescheduling checks on loop backedges. | 
|  | func insertLoopReschedChecks(f *Func) { | 
|  | // TODO: when split information is recorded in export data, insert checks only on backedges that can be reached on a split-call-free path. | 
|  |  | 
|  | // Loop reschedule checks compare the stack pointer with | 
|  | // the per-g stack bound.  If the pointer appears invalid, | 
|  | // that means a reschedule check is needed. | 
|  | // | 
|  | // Steps: | 
|  | // 1. locate backedges. | 
|  | // 2. Record memory definitions at block end so that | 
|  | //    the SSA graph for mem can be properly modified. | 
|  | // 3. Ensure that phi functions that will-be-needed for mem | 
|  | //    are present in the graph, initially with trivial inputs. | 
|  | // 4. Record all to-be-modified uses of mem; | 
|  | //    apply modifications (split into two steps to simplify and | 
|  | //    avoided nagging order-dependencies). | 
|  | // 5. Rewrite backedges to include reschedule check, | 
|  | //    and modify destination phi function appropriately with new | 
|  | //    definitions for mem. | 
|  |  | 
|  | if f.NoSplit { // nosplit functions don't reschedule. | 
|  | return | 
|  | } | 
|  |  | 
|  | backedges := backedges(f) | 
|  | if len(backedges) == 0 { // no backedges means no rescheduling checks. | 
|  | return | 
|  | } | 
|  |  | 
|  | lastMems := findLastMems(f) | 
|  |  | 
|  | idom := f.Idom() | 
|  | po := f.postorder() | 
|  | // The ordering in the dominator tree matters; it's important that | 
|  | // the walk of the dominator tree also be a preorder (i.e., a node is | 
|  | // visited only after all its non-backedge predecessors have been visited). | 
|  | sdom := newSparseOrderedTree(f, idom, po) | 
|  |  | 
|  | if f.pass.debug > 1 { | 
|  | fmt.Printf("before %s = %s\n", f.Name, sdom.treestructure(f.Entry)) | 
|  | } | 
|  |  | 
|  | tofixBackedges := []edgeMem{} | 
|  |  | 
|  | for _, e := range backedges { // TODO: could filter here by calls in loops, if declared and inferred nosplit are recorded in export data. | 
|  | tofixBackedges = append(tofixBackedges, edgeMem{e, nil}) | 
|  | } | 
|  |  | 
|  | // It's possible that there is no memory state (no global/pointer loads/stores or calls) | 
|  | if lastMems[f.Entry.ID] == nil { | 
|  | lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Pos, OpInitMem, types.TypeMem) | 
|  | } | 
|  |  | 
|  | memDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, the mem def seen at its bottom. Could be from earlier block. | 
|  |  | 
|  | // Propagate last mem definitions forward through successor blocks. | 
|  | for i := len(po) - 1; i >= 0; i-- { | 
|  | b := po[i] | 
|  | mem := lastMems[b.ID] | 
|  | 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. | 
|  | // loop because there might be backedges that haven't been visited yet. | 
|  | mem = memDefsAtBlockEnds[b.Preds[j].b.ID] | 
|  | } | 
|  | memDefsAtBlockEnds[b.ID] = mem | 
|  | if f.pass.debug > 2 { | 
|  | fmt.Printf("memDefsAtBlockEnds[%s] = %s\n", b, mem) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Maps from block to newly-inserted phi function in block. | 
|  | newmemphis := make(map[*Block]rewrite) | 
|  |  | 
|  | // Insert phi functions as necessary for future changes to flow graph. | 
|  | for i, emc := range tofixBackedges { | 
|  | e := emc.e | 
|  | h := e.b | 
|  |  | 
|  | // find the phi function for the memory input at "h", if there is one. | 
|  | var headerMemPhi *Value // look for header mem phi | 
|  |  | 
|  | for _, v := range h.Values { | 
|  | if v.Op == OpPhi && v.Type.IsMemory() { | 
|  | headerMemPhi = v | 
|  | } | 
|  | } | 
|  |  | 
|  | if headerMemPhi == nil { | 
|  | // if the header is nil, make a trivial phi from the dominator | 
|  | mem0 := memDefsAtBlockEnds[idom[h.ID].ID] | 
|  | headerMemPhi = newPhiFor(h, mem0) | 
|  | newmemphis[h] = rewrite{before: mem0, after: headerMemPhi} | 
|  | addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis, sdom) | 
|  |  | 
|  | } | 
|  | tofixBackedges[i].m = headerMemPhi | 
|  |  | 
|  | } | 
|  | if f.pass.debug > 0 { | 
|  | for b, r := range newmemphis { | 
|  | fmt.Printf("before b=%s, rewrite=%s\n", b, r.String()) | 
|  | } | 
|  | } | 
|  |  | 
|  | // dfPhiTargets notes inputs to phis in dominance frontiers that should not | 
|  | // be rewritten as part of the dominated children of some outer rewrite. | 
|  | dfPhiTargets := make(map[rewriteTarget]bool) | 
|  |  | 
|  | rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis, dfPhiTargets, sdom) | 
|  |  | 
|  | if f.pass.debug > 0 { | 
|  | for b, r := range newmemphis { | 
|  | fmt.Printf("after b=%s, rewrite=%s\n", b, r.String()) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Apply collected rewrites. | 
|  | for _, r := range newmemphis { | 
|  | for _, rw := range r.rewrites { | 
|  | rw.v.SetArg(rw.i, r.after) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Rewrite backedges to include reschedule checks. | 
|  | for _, emc := range tofixBackedges { | 
|  | e := emc.e | 
|  | headerMemPhi := emc.m | 
|  | h := e.b | 
|  | i := e.i | 
|  | p := h.Preds[i] | 
|  | bb := p.b | 
|  | mem0 := headerMemPhi.Args[i] | 
|  | // bb e->p h, | 
|  | // Because we're going to insert a rare-call, make sure the | 
|  | // looping edge still looks likely. | 
|  | likely := BranchLikely | 
|  | if p.i != 0 { | 
|  | likely = BranchUnlikely | 
|  | } | 
|  | if bb.Kind != BlockPlain { // backedges can be unconditional. e.g., if x { something; continue } | 
|  | bb.Likely = likely | 
|  | } | 
|  |  | 
|  | // rewrite edge to include reschedule check | 
|  | // existing edges: | 
|  | // | 
|  | // bb.Succs[p.i] == Edge{h, i} | 
|  | // h.Preds[i] == p == Edge{bb,p.i} | 
|  | // | 
|  | // new block(s): | 
|  | // test: | 
|  | //    if sp < g.limit { goto sched } | 
|  | //    goto join | 
|  | // sched: | 
|  | //    mem1 := call resched (mem0) | 
|  | //    goto join | 
|  | // join: | 
|  | //    mem2 := phi(mem0, mem1) | 
|  | //    goto h | 
|  | // | 
|  | // and correct arg i of headerMemPhi and headerCtrPhi | 
|  | // | 
|  | // EXCEPT: join block containing only phi functions is bad | 
|  | // for the register allocator.  Therefore, there is no | 
|  | // join, and branches targeting join must instead target | 
|  | // the header, and the other phi functions within header are | 
|  | // adjusted for the additional input. | 
|  |  | 
|  | test := f.NewBlock(BlockIf) | 
|  | sched := f.NewBlock(BlockPlain) | 
|  |  | 
|  | test.Pos = bb.Pos | 
|  | sched.Pos = bb.Pos | 
|  |  | 
|  | // if sp < g.limit { goto sched } | 
|  | // goto header | 
|  |  | 
|  | cfgtypes := &f.Config.Types | 
|  | pt := cfgtypes.Uintptr | 
|  | g := test.NewValue1(bb.Pos, OpGetG, pt, mem0) | 
|  | sp := test.NewValue0(bb.Pos, OpSP, pt) | 
|  | cmpOp := OpLess64U | 
|  | if pt.Size() == 4 { | 
|  | cmpOp = OpLess32U | 
|  | } | 
|  | limaddr := test.NewValue1I(bb.Pos, OpOffPtr, pt, 2*pt.Size(), g) | 
|  | lim := test.NewValue2(bb.Pos, OpLoad, pt, limaddr, mem0) | 
|  | cmp := test.NewValue2(bb.Pos, cmpOp, cfgtypes.Bool, sp, lim) | 
|  | test.SetControl(cmp) | 
|  |  | 
|  | // if true, goto sched | 
|  | test.AddEdgeTo(sched) | 
|  |  | 
|  | // if false, rewrite edge to header. | 
|  | // do NOT remove+add, because that will perturb all the other phi functions | 
|  | // as well as messing up other edges to the header. | 
|  | test.Succs = append(test.Succs, Edge{h, i}) | 
|  | h.Preds[i] = Edge{test, 1} | 
|  | headerMemPhi.SetArg(i, mem0) | 
|  |  | 
|  | test.Likely = BranchUnlikely | 
|  |  | 
|  | // sched: | 
|  | //    mem1 := call resched (mem0) | 
|  | //    goto header | 
|  | resched := f.fe.Syslook("goschedguarded") | 
|  | mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, resched, mem0) | 
|  | sched.AddEdgeTo(h) | 
|  | headerMemPhi.AddArg(mem1) | 
|  |  | 
|  | bb.Succs[p.i] = Edge{test, 0} | 
|  | test.Preds = append(test.Preds, Edge{bb, p.i}) | 
|  |  | 
|  | // Must correct all the other phi functions in the header for new incoming edge. | 
|  | // Except for mem phis, it will be the same value seen on the original | 
|  | // backedge at index i. | 
|  | for _, v := range h.Values { | 
|  | if v.Op == OpPhi && v != headerMemPhi { | 
|  | v.AddArg(v.Args[i]) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | f.invalidateCFG() | 
|  |  | 
|  | if f.pass.debug > 1 { | 
|  | sdom = newSparseTree(f, f.Idom()) | 
|  | fmt.Printf("after %s = %s\n", f.Name, sdom.treestructure(f.Entry)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // newPhiFor inserts a new Phi function into b, | 
|  | // with all inputs set to v. | 
|  | func newPhiFor(b *Block, v *Value) *Value { | 
|  | phiV := b.NewValue0(b.Pos, OpPhi, v.Type) | 
|  |  | 
|  | for range b.Preds { | 
|  | phiV.AddArg(v) | 
|  | } | 
|  | return phiV | 
|  | } | 
|  |  | 
|  | // rewriteNewPhis updates newphis[h] to record all places where the new phi function inserted | 
|  | // in block h will replace a previous definition.  Block b is the block currently being processed; | 
|  | // if b has its own phi definition then it takes the place of h. | 
|  | // defsForUses provides information about other definitions of the variable that are present | 
|  | // (and if nil, indicates that the variable is no longer live) | 
|  | // sdom must yield a preorder of the flow graph if recursively walked, root-to-children. | 
|  | // The result of newSparseOrderedTree with order supplied by a dfs-postorder satisfies this | 
|  | // requirement. | 
|  | func rewriteNewPhis(h, b *Block, f *Func, defsForUses []*Value, newphis map[*Block]rewrite, dfPhiTargets map[rewriteTarget]bool, sdom SparseTree) { | 
|  | // If b is a block with a new phi, then a new rewrite applies below it in the dominator tree. | 
|  | if _, ok := newphis[b]; ok { | 
|  | h = b | 
|  | } | 
|  | change := newphis[h] | 
|  | x := change.before | 
|  | y := change.after | 
|  |  | 
|  | // Apply rewrites to this block | 
|  | if x != nil { // don't waste time on the common case of no definition. | 
|  | p := &change.rewrites | 
|  | for _, v := range b.Values { | 
|  | if v == y { // don't rewrite self -- phi inputs are handled below. | 
|  | continue | 
|  | } | 
|  | for i, w := range v.Args { | 
|  | if w != x { | 
|  | continue | 
|  | } | 
|  | tgt := rewriteTarget{v, i} | 
|  |  | 
|  | // It's possible dominated control flow will rewrite this instead. | 
|  | // Visiting in preorder (a property of how sdom was constructed) | 
|  | // ensures that these are seen in the proper order. | 
|  | if dfPhiTargets[tgt] { | 
|  | continue | 
|  | } | 
|  | *p = append(*p, tgt) | 
|  | if f.pass.debug > 1 { | 
|  | fmt.Printf("added block target for h=%v, b=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n", | 
|  | h, b, x, y, v, i) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Rewrite appropriate inputs of phis reached in successors | 
|  | // in dominance frontier, self, and dominated. | 
|  | // If the variable def reaching uses in b is itself defined in b, then the new phi function | 
|  | // does not reach the successors of b.  (This assumes a bit about the structure of the | 
|  | // phi use-def graph, but it's true for memory.) | 
|  | if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b { | 
|  | for _, e := range b.Succs { | 
|  | s := e.b | 
|  |  | 
|  | for _, v := range s.Values { | 
|  | if v.Op == OpPhi && v.Args[e.i] == x { | 
|  | tgt := rewriteTarget{v, e.i} | 
|  | *p = append(*p, tgt) | 
|  | dfPhiTargets[tgt] = true | 
|  | if f.pass.debug > 1 { | 
|  | fmt.Printf("added phi target for h=%v, b=%v, s=%v, x=%v, y=%v, tgt.v=%s, tgt.i=%d\n", | 
|  | h, b, s, x, y, v.LongString(), e.i) | 
|  | } | 
|  | break | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | newphis[h] = change | 
|  | } | 
|  |  | 
|  | for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling { | 
|  | rewriteNewPhis(h, c, f, defsForUses, newphis, dfPhiTargets, sdom) // TODO: convert to explicit stack from recursion. | 
|  | } | 
|  | } | 
|  |  | 
|  | // addDFphis creates new trivial phis that are necessary to correctly reflect (within SSA) | 
|  | // a new definition for variable "x" inserted at h (usually but not necessarily a phi). | 
|  | // These new phis can only occur at the dominance frontier of h; block s is in the dominance | 
|  | // frontier of h if h does not strictly dominate s and if s is a successor of a block b where | 
|  | // either b = h or h strictly dominates b. | 
|  | // These newly created phis are themselves new definitions that may require addition of their | 
|  | // own trivial phi functions in their own dominance frontier, and this is handled recursively. | 
|  | func addDFphis(x *Value, h, b *Block, f *Func, defForUses []*Value, newphis map[*Block]rewrite, sdom SparseTree) { | 
|  | oldv := defForUses[b.ID] | 
|  | if oldv != x { // either a new definition replacing x, or nil if it is proven that there are no uses reachable from b | 
|  | return | 
|  | } | 
|  | idom := f.Idom() | 
|  | outer: | 
|  | for _, e := range b.Succs { | 
|  | s := e.b | 
|  | // check phi functions in the dominance frontier | 
|  | if sdom.isAncestor(h, s) { | 
|  | continue // h dominates s, successor of b, therefore s is not in the frontier. | 
|  | } | 
|  | if _, ok := newphis[s]; ok { | 
|  | continue // successor s of b already has a new phi function, so there is no need to add another. | 
|  | } | 
|  | if x != nil { | 
|  | for _, v := range s.Values { | 
|  | if v.Op == OpPhi && v.Args[e.i] == x { | 
|  | continue outer // successor s of b has an old phi function, so there is no need to add another. | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | old := defForUses[idom[s.ID].ID] // new phi function is correct-but-redundant, combining value "old" on all inputs. | 
|  | headerPhi := newPhiFor(s, old) | 
|  | // the new phi will replace "old" in block s and all blocks dominated by s. | 
|  | newphis[s] = rewrite{before: old, after: headerPhi} // record new phi, to have inputs labeled "old" rewritten to "headerPhi" | 
|  | addDFphis(old, s, s, f, defForUses, newphis, sdom)  // the new definition may also create new phi functions. | 
|  | } | 
|  | for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling { | 
|  | addDFphis(x, h, c, f, defForUses, newphis, sdom) // TODO: convert to explicit stack from recursion. | 
|  | } | 
|  | } | 
|  |  | 
|  | // findLastMems maps block ids to last memory-output op in a block, if any | 
|  | func findLastMems(f *Func) []*Value { | 
|  |  | 
|  | var stores []*Value | 
|  | lastMems := make([]*Value, f.NumBlocks()) | 
|  | storeUse := f.newSparseSet(f.NumValues()) | 
|  | defer f.retSparseSet(storeUse) | 
|  | for _, b := range f.Blocks { | 
|  | // Find all the stores in this block. Categorize their uses: | 
|  | //  storeUse contains stores which are used by a subsequent store. | 
|  | storeUse.clear() | 
|  | stores = stores[:0] | 
|  | var memPhi *Value | 
|  | for _, v := range b.Values { | 
|  | if v.Op == OpPhi { | 
|  | if v.Type.IsMemory() { | 
|  | memPhi = v | 
|  | } | 
|  | continue | 
|  | } | 
|  | if v.Type.IsMemory() { | 
|  | stores = append(stores, v) | 
|  | for _, a := range v.Args { | 
|  | if a.Block == b && a.Type.IsMemory() { | 
|  | storeUse.add(a.ID) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if len(stores) == 0 { | 
|  | lastMems[b.ID] = memPhi | 
|  | continue | 
|  | } | 
|  |  | 
|  | // find last store in the block | 
|  | var last *Value | 
|  | for _, v := range stores { | 
|  | if storeUse.contains(v.ID) { | 
|  | continue | 
|  | } | 
|  | if last != nil { | 
|  | b.Fatalf("two final stores - simultaneous live stores %s %s", last, v) | 
|  | } | 
|  | last = v | 
|  | } | 
|  | if last == nil { | 
|  | b.Fatalf("no last store found - cycle?") | 
|  | } | 
|  | lastMems[b.ID] = last | 
|  | } | 
|  | return lastMems | 
|  | } | 
|  |  | 
|  | type backedgesState struct { | 
|  | b *Block | 
|  | i int | 
|  | } | 
|  |  | 
|  | // backedges returns a slice of successor edges that are back | 
|  | // edges.  For reducible loops, edge.b is the header. | 
|  | func backedges(f *Func) []Edge { | 
|  | edges := []Edge{} | 
|  | mark := make([]markKind, f.NumBlocks()) | 
|  | stack := []backedgesState{} | 
|  |  | 
|  | mark[f.Entry.ID] = notExplored | 
|  | stack = append(stack, backedgesState{f.Entry, 0}) | 
|  |  | 
|  | for len(stack) > 0 { | 
|  | l := len(stack) | 
|  | x := stack[l-1] | 
|  | if x.i < len(x.b.Succs) { | 
|  | e := x.b.Succs[x.i] | 
|  | stack[l-1].i++ | 
|  | s := e.b | 
|  | if mark[s.ID] == notFound { | 
|  | mark[s.ID] = notExplored | 
|  | stack = append(stack, backedgesState{s, 0}) | 
|  | } else if mark[s.ID] == notExplored { | 
|  | edges = append(edges, e) | 
|  | } | 
|  | } else { | 
|  | mark[x.b.ID] = done | 
|  | stack = stack[0 : l-1] | 
|  | } | 
|  | } | 
|  | return edges | 
|  | } |