github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/src/cmd/compile/internal/ssa/loopreschedchecks.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 "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 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 sdom := f.sdom() 77 78 if f.pass.debug > 2 { 79 fmt.Printf("before %s = %s\n", f.Name, sdom.treestructure(f.Entry)) 80 } 81 82 tofixBackedges := []edgeMem{} 83 84 for _, e := range backedges { // TODO: could filter here by calls in loops, if declared and inferred nosplit are recorded in export data. 85 tofixBackedges = append(tofixBackedges, edgeMem{e, nil}) 86 } 87 88 // It's possible that there is no memory state (no global/pointer loads/stores or calls) 89 if lastMems[f.Entry.ID] == nil { 90 lastMems[f.Entry.ID] = f.Entry.NewValue0(f.Entry.Pos, OpInitMem, types.TypeMem) 91 } 92 93 memDefsAtBlockEnds := make([]*Value, f.NumBlocks()) // For each block, the mem def seen at its bottom. Could be from earlier block. 94 95 // Propagate last mem definitions forward through successor blocks. 96 po := f.postorder() 97 for i := len(po) - 1; i >= 0; i-- { 98 b := po[i] 99 mem := lastMems[b.ID] 100 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. 101 // loop because there might be backedges that haven't been visited yet. 102 mem = memDefsAtBlockEnds[b.Preds[j].b.ID] 103 } 104 memDefsAtBlockEnds[b.ID] = mem 105 } 106 107 // Maps from block to newly-inserted phi function in block. 108 newmemphis := make(map[*Block]rewrite) 109 110 // Insert phi functions as necessary for future changes to flow graph. 111 for i, emc := range tofixBackedges { 112 e := emc.e 113 h := e.b 114 115 // find the phi function for the memory input at "h", if there is one. 116 var headerMemPhi *Value // look for header mem phi 117 118 for _, v := range h.Values { 119 if v.Op == OpPhi && v.Type.IsMemory() { 120 headerMemPhi = v 121 } 122 } 123 124 if headerMemPhi == nil { 125 // if the header is nil, make a trivial phi from the dominator 126 mem0 := memDefsAtBlockEnds[idom[h.ID].ID] 127 headerMemPhi = newPhiFor(h, mem0) 128 newmemphis[h] = rewrite{before: mem0, after: headerMemPhi} 129 addDFphis(mem0, h, h, f, memDefsAtBlockEnds, newmemphis) 130 131 } 132 tofixBackedges[i].m = headerMemPhi 133 134 } 135 136 rewriteNewPhis(f.Entry, f.Entry, f, memDefsAtBlockEnds, newmemphis) 137 138 if f.pass.debug > 0 { 139 for b, r := range newmemphis { 140 fmt.Printf("b=%s, rewrite=%s\n", b, r.String()) 141 } 142 } 143 144 // Apply collected rewrites. 145 for _, r := range newmemphis { 146 for _, rw := range r.rewrites { 147 rw.v.SetArg(rw.i, r.after) 148 } 149 } 150 151 // Rewrite backedges to include reschedule checks. 152 for _, emc := range tofixBackedges { 153 e := emc.e 154 headerMemPhi := emc.m 155 h := e.b 156 i := e.i 157 p := h.Preds[i] 158 bb := p.b 159 mem0 := headerMemPhi.Args[i] 160 // bb e->p h, 161 // Because we're going to insert a rare-call, make sure the 162 // looping edge still looks likely. 163 likely := BranchLikely 164 if p.i != 0 { 165 likely = BranchUnlikely 166 } 167 bb.Likely = likely 168 169 // rewrite edge to include reschedule check 170 // existing edges: 171 // 172 // bb.Succs[p.i] == Edge{h, i} 173 // h.Preds[i] == p == Edge{bb,p.i} 174 // 175 // new block(s): 176 // test: 177 // if sp < g.limit { goto sched } 178 // goto join 179 // sched: 180 // mem1 := call resched (mem0) 181 // goto join 182 // join: 183 // mem2 := phi(mem0, mem1) 184 // goto h 185 // 186 // and correct arg i of headerMemPhi and headerCtrPhi 187 // 188 // EXCEPT: join block containing only phi functions is bad 189 // for the register allocator. Therefore, there is no 190 // join, and branches targeting join must instead target 191 // the header, and the other phi functions within header are 192 // adjusted for the additional input. 193 194 test := f.NewBlock(BlockIf) 195 sched := f.NewBlock(BlockPlain) 196 197 test.Pos = bb.Pos 198 sched.Pos = bb.Pos 199 200 // if sp < g.limit { goto sched } 201 // goto header 202 203 cfgtypes := &f.Config.Types 204 pt := cfgtypes.Uintptr 205 g := test.NewValue1(bb.Pos, OpGetG, pt, mem0) 206 sp := test.NewValue0(bb.Pos, OpSP, pt) 207 cmpOp := OpLess64U 208 if pt.Size() == 4 { 209 cmpOp = OpLess32U 210 } 211 limaddr := test.NewValue1I(bb.Pos, OpOffPtr, pt, 2*pt.Size(), g) 212 lim := test.NewValue2(bb.Pos, OpLoad, pt, limaddr, mem0) 213 cmp := test.NewValue2(bb.Pos, cmpOp, cfgtypes.Bool, sp, lim) 214 test.SetControl(cmp) 215 216 // if true, goto sched 217 test.AddEdgeTo(sched) 218 219 // if false, rewrite edge to header. 220 // do NOT remove+add, because that will perturb all the other phi functions 221 // as well as messing up other edges to the header. 222 test.Succs = append(test.Succs, Edge{h, i}) 223 h.Preds[i] = Edge{test, 1} 224 headerMemPhi.SetArg(i, mem0) 225 226 test.Likely = BranchUnlikely 227 228 // sched: 229 // mem1 := call resched (mem0) 230 // goto header 231 resched := f.fe.Syslook("goschedguarded") 232 mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, resched, mem0) 233 sched.AddEdgeTo(h) 234 headerMemPhi.AddArg(mem1) 235 236 bb.Succs[p.i] = Edge{test, 0} 237 test.Preds = append(test.Preds, Edge{bb, p.i}) 238 239 // Must correct all the other phi functions in the header for new incoming edge. 240 // Except for mem phis, it will be the same value seen on the original 241 // backedge at index i. 242 for _, v := range h.Values { 243 if v.Op == OpPhi && v != headerMemPhi { 244 v.AddArg(v.Args[i]) 245 } 246 } 247 } 248 249 f.invalidateCFG() 250 251 if f.pass.debug > 2 { 252 sdom = newSparseTree(f, f.Idom()) 253 fmt.Printf("after %s = %s\n", f.Name, sdom.treestructure(f.Entry)) 254 } 255 256 return 257 } 258 259 // newPhiFor inserts a new Phi function into b, 260 // with all inputs set to v. 261 func newPhiFor(b *Block, v *Value) *Value { 262 phiV := b.NewValue0(b.Pos, OpPhi, v.Type) 263 264 for range b.Preds { 265 phiV.AddArg(v) 266 } 267 return phiV 268 } 269 270 // rewriteNewPhis updates newphis[h] to record all places where the new phi function inserted 271 // in block h will replace a previous definition. Block b is the block currently being processed; 272 // if b has its own phi definition then it takes the place of h. 273 // defsForUses provides information about other definitions of the variable that are present 274 // (and if nil, indicates that the variable is no longer live) 275 func rewriteNewPhis(h, b *Block, f *Func, defsForUses []*Value, newphis map[*Block]rewrite) { 276 // If b is a block with a new phi, then a new rewrite applies below it in the dominator tree. 277 if _, ok := newphis[b]; ok { 278 h = b 279 } 280 change := newphis[h] 281 x := change.before 282 y := change.after 283 284 // Apply rewrites to this block 285 if x != nil { // don't waste time on the common case of no definition. 286 p := &change.rewrites 287 for _, v := range b.Values { 288 if v == y { // don't rewrite self -- phi inputs are handled below. 289 continue 290 } 291 for i, w := range v.Args { 292 if w != x { 293 continue 294 } 295 *p = append(*p, rewriteTarget{v, i}) 296 } 297 } 298 299 // Rewrite appropriate inputs of phis reached in successors 300 // in dominance frontier, self, and dominated. 301 // If the variable def reaching uses in b is itself defined in b, then the new phi function 302 // does not reach the successors of b. (This assumes a bit about the structure of the 303 // phi use-def graph, but it's true for memory.) 304 if dfu := defsForUses[b.ID]; dfu != nil && dfu.Block != b { 305 for _, e := range b.Succs { 306 s := e.b 307 if sphi, ok := newphis[s]; ok { // saves time to find the phi this way. 308 *p = append(*p, rewriteTarget{sphi.after, e.i}) 309 continue 310 } 311 for _, v := range s.Values { 312 if v.Op == OpPhi && v.Args[e.i] == x { 313 *p = append(*p, rewriteTarget{v, e.i}) 314 break 315 } 316 } 317 } 318 } 319 newphis[h] = change 320 } 321 322 sdom := f.sdom() 323 324 for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling { 325 rewriteNewPhis(h, c, f, defsForUses, newphis) // TODO: convert to explicit stack from recursion. 326 } 327 } 328 329 // addDFphis creates new trivial phis that are necessary to correctly reflect (within SSA) 330 // a new definition for variable "x" inserted at h (usually but not necessarily a phi). 331 // These new phis can only occur at the dominance frontier of h; block s is in the dominance 332 // frontier of h if h does not strictly dominate s and if s is a successor of a block b where 333 // either b = h or h strictly dominates b. 334 // These newly created phis are themselves new definitions that may require addition of their 335 // own trivial phi functions in their own dominance frontier, and this is handled recursively. 336 func addDFphis(x *Value, h, b *Block, f *Func, defForUses []*Value, newphis map[*Block]rewrite) { 337 oldv := defForUses[b.ID] 338 if oldv != x { // either a new definition replacing x, or nil if it is proven that there are no uses reachable from b 339 return 340 } 341 sdom := f.sdom() 342 idom := f.Idom() 343 outer: 344 for _, e := range b.Succs { 345 s := e.b 346 // check phi functions in the dominance frontier 347 if sdom.isAncestor(h, s) { 348 continue // h dominates s, successor of b, therefore s is not in the frontier. 349 } 350 if _, ok := newphis[s]; ok { 351 continue // successor s of b already has a new phi function, so there is no need to add another. 352 } 353 if x != nil { 354 for _, v := range s.Values { 355 if v.Op == OpPhi && v.Args[e.i] == x { 356 continue outer // successor s of b has an old phi function, so there is no need to add another. 357 } 358 } 359 } 360 361 old := defForUses[idom[s.ID].ID] // new phi function is correct-but-redundant, combining value "old" on all inputs. 362 headerPhi := newPhiFor(s, old) 363 // the new phi will replace "old" in block s and all blocks dominated by s. 364 newphis[s] = rewrite{before: old, after: headerPhi} // record new phi, to have inputs labeled "old" rewritten to "headerPhi" 365 addDFphis(old, s, s, f, defForUses, newphis) // the new definition may also create new phi functions. 366 } 367 for c := sdom[b.ID].child; c != nil; c = sdom[c.ID].sibling { 368 addDFphis(x, h, c, f, defForUses, newphis) // TODO: convert to explicit stack from recursion. 369 } 370 } 371 372 // findLastMems maps block ids to last memory-output op in a block, if any 373 func findLastMems(f *Func) []*Value { 374 375 var stores []*Value 376 lastMems := make([]*Value, f.NumBlocks()) 377 storeUse := f.newSparseSet(f.NumValues()) 378 defer f.retSparseSet(storeUse) 379 for _, b := range f.Blocks { 380 // Find all the stores in this block. Categorize their uses: 381 // storeUse contains stores which are used by a subsequent store. 382 storeUse.clear() 383 stores = stores[:0] 384 var memPhi *Value 385 for _, v := range b.Values { 386 if v.Op == OpPhi { 387 if v.Type.IsMemory() { 388 memPhi = v 389 } 390 continue 391 } 392 if v.Type.IsMemory() { 393 stores = append(stores, v) 394 for _, a := range v.Args { 395 if a.Block == b && a.Type.IsMemory() { 396 storeUse.add(a.ID) 397 } 398 } 399 } 400 } 401 if len(stores) == 0 { 402 lastMems[b.ID] = memPhi 403 continue 404 } 405 406 // find last store in the block 407 var last *Value 408 for _, v := range stores { 409 if storeUse.contains(v.ID) { 410 continue 411 } 412 if last != nil { 413 b.Fatalf("two final stores - simultaneous live stores %s %s", last, v) 414 } 415 last = v 416 } 417 if last == nil { 418 b.Fatalf("no last store found - cycle?") 419 } 420 lastMems[b.ID] = last 421 } 422 return lastMems 423 } 424 425 type backedgesState struct { 426 b *Block 427 i int 428 } 429 430 // backedges returns a slice of successor edges that are back 431 // edges. For reducible loops, edge.b is the header. 432 func backedges(f *Func) []Edge { 433 edges := []Edge{} 434 mark := make([]markKind, f.NumBlocks()) 435 stack := []backedgesState{} 436 437 mark[f.Entry.ID] = notExplored 438 stack = append(stack, backedgesState{f.Entry, 0}) 439 440 for len(stack) > 0 { 441 l := len(stack) 442 x := stack[l-1] 443 if x.i < len(x.b.Succs) { 444 e := x.b.Succs[x.i] 445 stack[l-1].i++ 446 s := e.b 447 if mark[s.ID] == notFound { 448 mark[s.ID] = notExplored 449 stack = append(stack, backedgesState{s, 0}) 450 } else if mark[s.ID] == notExplored { 451 edges = append(edges, e) 452 } 453 } else { 454 mark[x.b.ID] = done 455 stack = stack[0 : l-1] 456 } 457 } 458 return edges 459 }