github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/ssa/lift.go (about) 1 // Copyright 2013 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 // This file defines the lifting pass which tries to "lift" Alloc 8 // cells (new/local variables) into SSA registers, replacing loads 9 // with the dominating stored value, eliminating loads and stores, and 10 // inserting φ-nodes as needed. 11 12 // Cited papers and resources: 13 // 14 // Ron Cytron et al. 1991. Efficiently computing SSA form... 15 // http://doi.acm.org/10.1145/115372.115320 16 // 17 // Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm. 18 // Software Practice and Experience 2001, 4:1-10. 19 // http://www.hipersoft.rice.edu/grads/publications/dom14.pdf 20 // 21 // Daniel Berlin, llvmdev mailing list, 2012. 22 // http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html 23 // (Be sure to expand the whole thread.) 24 25 // TODO(adonovan): opt: there are many optimizations worth evaluating, and 26 // the conventional wisdom for SSA construction is that a simple 27 // algorithm well engineered often beats those of better asymptotic 28 // complexity on all but the most egregious inputs. 29 // 30 // Danny Berlin suggests that the Cooper et al. algorithm for 31 // computing the dominance frontier is superior to Cytron et al. 32 // Furthermore he recommends that rather than computing the DF for the 33 // whole function then renaming all alloc cells, it may be cheaper to 34 // compute the DF for each alloc cell separately and throw it away. 35 // 36 // Consider exploiting liveness information to avoid creating dead 37 // φ-nodes which we then immediately remove. 38 // 39 // Integrate lifting with scalar replacement of aggregates (SRA) since 40 // the two are synergistic. 41 // 42 // Also see many other "TODO: opt" suggestions in the code. 43 44 import ( 45 "fmt" 46 "go/token" 47 "math/big" 48 "os" 49 50 "llvm.org/llgo/third_party/gotools/go/types" 51 ) 52 53 // If true, perform sanity checking and show diagnostic information at 54 // each step of lifting. Very verbose. 55 const debugLifting = false 56 57 // domFrontier maps each block to the set of blocks in its dominance 58 // frontier. The outer slice is conceptually a map keyed by 59 // Block.Index. The inner slice is conceptually a set, possibly 60 // containing duplicates. 61 // 62 // TODO(adonovan): opt: measure impact of dups; consider a packed bit 63 // representation, e.g. big.Int, and bitwise parallel operations for 64 // the union step in the Children loop. 65 // 66 // domFrontier's methods mutate the slice's elements but not its 67 // length, so their receivers needn't be pointers. 68 // 69 type domFrontier [][]*BasicBlock 70 71 func (df domFrontier) add(u, v *BasicBlock) { 72 p := &df[u.Index] 73 *p = append(*p, v) 74 } 75 76 // build builds the dominance frontier df for the dominator (sub)tree 77 // rooted at u, using the Cytron et al. algorithm. 78 // 79 // TODO(adonovan): opt: consider Berlin approach, computing pruned SSA 80 // by pruning the entire IDF computation, rather than merely pruning 81 // the DF -> IDF step. 82 func (df domFrontier) build(u *BasicBlock) { 83 // Encounter each node u in postorder of dom tree. 84 for _, child := range u.dom.children { 85 df.build(child) 86 } 87 for _, vb := range u.Succs { 88 if v := vb.dom; v.idom != u { 89 df.add(u, vb) 90 } 91 } 92 for _, w := range u.dom.children { 93 for _, vb := range df[w.Index] { 94 // TODO(adonovan): opt: use word-parallel bitwise union. 95 if v := vb.dom; v.idom != u { 96 df.add(u, vb) 97 } 98 } 99 } 100 } 101 102 func buildDomFrontier(fn *Function) domFrontier { 103 df := make(domFrontier, len(fn.Blocks)) 104 df.build(fn.Blocks[0]) 105 if fn.Recover != nil { 106 df.build(fn.Recover) 107 } 108 return df 109 } 110 111 func removeInstr(refs []Instruction, instr Instruction) []Instruction { 112 i := 0 113 for _, ref := range refs { 114 if ref == instr { 115 continue 116 } 117 refs[i] = ref 118 i++ 119 } 120 for j := i; j != len(refs); j++ { 121 refs[j] = nil // aid GC 122 } 123 return refs[:i] 124 } 125 126 // lift attempts to replace local and new Allocs accessed only with 127 // load/store by SSA registers, inserting φ-nodes where necessary. 128 // The result is a program in classical pruned SSA form. 129 // 130 // Preconditions: 131 // - fn has no dead blocks (blockopt has run). 132 // - Def/use info (Operands and Referrers) is up-to-date. 133 // - The dominator tree is up-to-date. 134 // 135 func lift(fn *Function) { 136 // TODO(adonovan): opt: lots of little optimizations may be 137 // worthwhile here, especially if they cause us to avoid 138 // buildDomFrontier. For example: 139 // 140 // - Alloc never loaded? Eliminate. 141 // - Alloc never stored? Replace all loads with a zero constant. 142 // - Alloc stored once? Replace loads with dominating store; 143 // don't forget that an Alloc is itself an effective store 144 // of zero. 145 // - Alloc used only within a single block? 146 // Use degenerate algorithm avoiding φ-nodes. 147 // - Consider synergy with scalar replacement of aggregates (SRA). 148 // e.g. *(&x.f) where x is an Alloc. 149 // Perhaps we'd get better results if we generated this as x.f 150 // i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)). 151 // Unclear. 152 // 153 // But we will start with the simplest correct code. 154 df := buildDomFrontier(fn) 155 156 if debugLifting { 157 title := false 158 for i, blocks := range df { 159 if blocks != nil { 160 if !title { 161 fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn) 162 title = true 163 } 164 fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks) 165 } 166 } 167 } 168 169 newPhis := make(newPhiMap) 170 171 // During this pass we will replace some BasicBlock.Instrs 172 // (allocs, loads and stores) with nil, keeping a count in 173 // BasicBlock.gaps. At the end we will reset Instrs to the 174 // concatenation of all non-dead newPhis and non-nil Instrs 175 // for the block, reusing the original array if space permits. 176 177 // While we're here, we also eliminate 'rundefers' 178 // instructions in functions that contain no 'defer' 179 // instructions. 180 usesDefer := false 181 182 // Determine which allocs we can lift and number them densely. 183 // The renaming phase uses this numbering for compact maps. 184 numAllocs := 0 185 for _, b := range fn.Blocks { 186 b.gaps = 0 187 b.rundefers = 0 188 for _, instr := range b.Instrs { 189 switch instr := instr.(type) { 190 case *Alloc: 191 index := -1 192 if liftAlloc(df, instr, newPhis) { 193 index = numAllocs 194 numAllocs++ 195 } 196 instr.index = index 197 case *Defer: 198 usesDefer = true 199 case *RunDefers: 200 b.rundefers++ 201 } 202 } 203 } 204 205 // renaming maps an alloc (keyed by index) to its replacement 206 // value. Initially the renaming contains nil, signifying the 207 // zero constant of the appropriate type; we construct the 208 // Const lazily at most once on each path through the domtree. 209 // TODO(adonovan): opt: cache per-function not per subtree. 210 renaming := make([]Value, numAllocs) 211 212 // Renaming. 213 rename(fn.Blocks[0], renaming, newPhis) 214 215 // Eliminate dead new phis, then prepend the live ones to each block. 216 for _, b := range fn.Blocks { 217 218 // Compress the newPhis slice to eliminate unused phis. 219 // TODO(adonovan): opt: compute liveness to avoid 220 // placing phis in blocks for which the alloc cell is 221 // not live. 222 nps := newPhis[b] 223 j := 0 224 for _, np := range nps { 225 if !phiIsLive(np.phi) { 226 // discard it, first removing it from referrers 227 for _, newval := range np.phi.Edges { 228 if refs := newval.Referrers(); refs != nil { 229 *refs = removeInstr(*refs, np.phi) 230 } 231 } 232 continue 233 } 234 nps[j] = np 235 j++ 236 } 237 nps = nps[:j] 238 239 rundefersToKill := b.rundefers 240 if usesDefer { 241 rundefersToKill = 0 242 } 243 244 if j+b.gaps+rundefersToKill == 0 { 245 continue // fast path: no new phis or gaps 246 } 247 248 // Compact nps + non-nil Instrs into a new slice. 249 // TODO(adonovan): opt: compact in situ if there is 250 // sufficient space or slack in the slice. 251 dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill) 252 for i, np := range nps { 253 dst[i] = np.phi 254 } 255 for _, instr := range b.Instrs { 256 if instr == nil { 257 continue 258 } 259 if !usesDefer { 260 if _, ok := instr.(*RunDefers); ok { 261 continue 262 } 263 } 264 dst[j] = instr 265 j++ 266 } 267 for i, np := range nps { 268 dst[i] = np.phi 269 } 270 b.Instrs = dst 271 } 272 273 // Remove any fn.Locals that were lifted. 274 j := 0 275 for _, l := range fn.Locals { 276 if l.index < 0 { 277 fn.Locals[j] = l 278 j++ 279 } 280 } 281 // Nil out fn.Locals[j:] to aid GC. 282 for i := j; i < len(fn.Locals); i++ { 283 fn.Locals[i] = nil 284 } 285 fn.Locals = fn.Locals[:j] 286 } 287 288 func phiIsLive(phi *Phi) bool { 289 for _, instr := range *phi.Referrers() { 290 if instr == phi { 291 continue // self-refs don't count 292 } 293 if _, ok := instr.(*DebugRef); ok { 294 continue // debug refs don't count 295 } 296 return true 297 } 298 return false 299 } 300 301 type blockSet struct{ big.Int } // (inherit methods from Int) 302 303 // add adds b to the set and returns true if the set changed. 304 func (s *blockSet) add(b *BasicBlock) bool { 305 i := b.Index 306 if s.Bit(i) != 0 { 307 return false 308 } 309 s.SetBit(&s.Int, i, 1) 310 return true 311 } 312 313 // take removes an arbitrary element from a set s and 314 // returns its index, or returns -1 if empty. 315 func (s *blockSet) take() int { 316 l := s.BitLen() 317 for i := 0; i < l; i++ { 318 if s.Bit(i) == 1 { 319 s.SetBit(&s.Int, i, 0) 320 return i 321 } 322 } 323 return -1 324 } 325 326 // newPhi is a pair of a newly introduced φ-node and the lifted Alloc 327 // it replaces. 328 type newPhi struct { 329 phi *Phi 330 alloc *Alloc 331 } 332 333 // newPhiMap records for each basic block, the set of newPhis that 334 // must be prepended to the block. 335 type newPhiMap map[*BasicBlock][]newPhi 336 337 // liftAlloc determines whether alloc can be lifted into registers, 338 // and if so, it populates newPhis with all the φ-nodes it may require 339 // and returns true. 340 // 341 func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool { 342 // Don't lift aggregates into registers, because we don't have 343 // a way to express their zero-constants. 344 switch deref(alloc.Type()).Underlying().(type) { 345 case *types.Array, *types.Struct: 346 return false 347 } 348 349 // Don't lift named return values in functions that defer 350 // calls that may recover from panic. 351 if fn := alloc.Parent(); fn.Recover != nil { 352 for _, nr := range fn.namedResults { 353 if nr == alloc { 354 return false 355 } 356 } 357 } 358 359 // Compute defblocks, the set of blocks containing a 360 // definition of the alloc cell. 361 var defblocks blockSet 362 for _, instr := range *alloc.Referrers() { 363 // Bail out if we discover the alloc is not liftable; 364 // the only operations permitted to use the alloc are 365 // loads/stores into the cell, and DebugRef. 366 switch instr := instr.(type) { 367 case *Store: 368 if instr.Val == alloc { 369 return false // address used as value 370 } 371 if instr.Addr != alloc { 372 panic("Alloc.Referrers is inconsistent") 373 } 374 defblocks.add(instr.Block()) 375 case *UnOp: 376 if instr.Op != token.MUL { 377 return false // not a load 378 } 379 if instr.X != alloc { 380 panic("Alloc.Referrers is inconsistent") 381 } 382 case *DebugRef: 383 // ok 384 default: 385 return false // some other instruction 386 } 387 } 388 // The Alloc itself counts as a (zero) definition of the cell. 389 defblocks.add(alloc.Block()) 390 391 if debugLifting { 392 fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name()) 393 } 394 395 fn := alloc.Parent() 396 397 // Φ-insertion. 398 // 399 // What follows is the body of the main loop of the insert-φ 400 // function described by Cytron et al, but instead of using 401 // counter tricks, we just reset the 'hasAlready' and 'work' 402 // sets each iteration. These are bitmaps so it's pretty cheap. 403 // 404 // TODO(adonovan): opt: recycle slice storage for W, 405 // hasAlready, defBlocks across liftAlloc calls. 406 var hasAlready blockSet 407 408 // Initialize W and work to defblocks. 409 var work blockSet = defblocks // blocks seen 410 var W blockSet // blocks to do 411 W.Set(&defblocks.Int) 412 413 // Traverse iterated dominance frontier, inserting φ-nodes. 414 for i := W.take(); i != -1; i = W.take() { 415 u := fn.Blocks[i] 416 for _, v := range df[u.Index] { 417 if hasAlready.add(v) { 418 // Create φ-node. 419 // It will be prepended to v.Instrs later, if needed. 420 phi := &Phi{ 421 Edges: make([]Value, len(v.Preds)), 422 Comment: alloc.Comment, 423 } 424 phi.pos = alloc.Pos() 425 phi.setType(deref(alloc.Type())) 426 phi.block = v 427 if debugLifting { 428 fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v) 429 } 430 newPhis[v] = append(newPhis[v], newPhi{phi, alloc}) 431 432 if work.add(v) { 433 W.add(v) 434 } 435 } 436 } 437 } 438 439 return true 440 } 441 442 // replaceAll replaces all intraprocedural uses of x with y, 443 // updating x.Referrers and y.Referrers. 444 // Precondition: x.Referrers() != nil, i.e. x must be local to some function. 445 // 446 func replaceAll(x, y Value) { 447 var rands []*Value 448 pxrefs := x.Referrers() 449 pyrefs := y.Referrers() 450 for _, instr := range *pxrefs { 451 rands = instr.Operands(rands[:0]) // recycle storage 452 for _, rand := range rands { 453 if *rand != nil { 454 if *rand == x { 455 *rand = y 456 } 457 } 458 } 459 if pyrefs != nil { 460 *pyrefs = append(*pyrefs, instr) // dups ok 461 } 462 } 463 *pxrefs = nil // x is now unreferenced 464 } 465 466 // renamed returns the value to which alloc is being renamed, 467 // constructing it lazily if it's the implicit zero initialization. 468 // 469 func renamed(renaming []Value, alloc *Alloc) Value { 470 v := renaming[alloc.index] 471 if v == nil { 472 v = zeroConst(deref(alloc.Type())) 473 renaming[alloc.index] = v 474 } 475 return v 476 } 477 478 // rename implements the (Cytron et al) SSA renaming algorithm, a 479 // preorder traversal of the dominator tree replacing all loads of 480 // Alloc cells with the value stored to that cell by the dominating 481 // store instruction. For lifting, we need only consider loads, 482 // stores and φ-nodes. 483 // 484 // renaming is a map from *Alloc (keyed by index number) to its 485 // dominating stored value; newPhis[x] is the set of new φ-nodes to be 486 // prepended to block x. 487 // 488 func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) { 489 // Each φ-node becomes the new name for its associated Alloc. 490 for _, np := range newPhis[u] { 491 phi := np.phi 492 alloc := np.alloc 493 renaming[alloc.index] = phi 494 } 495 496 // Rename loads and stores of allocs. 497 for i, instr := range u.Instrs { 498 switch instr := instr.(type) { 499 case *Alloc: 500 if instr.index >= 0 { // store of zero to Alloc cell 501 // Replace dominated loads by the zero value. 502 renaming[instr.index] = nil 503 if debugLifting { 504 fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr) 505 } 506 // Delete the Alloc. 507 u.Instrs[i] = nil 508 u.gaps++ 509 } 510 511 case *Store: 512 if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell 513 // Replace dominated loads by the stored value. 514 renaming[alloc.index] = instr.Val 515 if debugLifting { 516 fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n", 517 instr, instr.Val.Name()) 518 } 519 // Remove the store from the referrer list of the stored value. 520 if refs := instr.Val.Referrers(); refs != nil { 521 *refs = removeInstr(*refs, instr) 522 } 523 // Delete the Store. 524 u.Instrs[i] = nil 525 u.gaps++ 526 } 527 528 case *UnOp: 529 if instr.Op == token.MUL { 530 if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell 531 newval := renamed(renaming, alloc) 532 if debugLifting { 533 fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n", 534 instr.Name(), instr, newval.Name()) 535 } 536 // Replace all references to 537 // the loaded value by the 538 // dominating stored value. 539 replaceAll(instr, newval) 540 // Delete the Load. 541 u.Instrs[i] = nil 542 u.gaps++ 543 } 544 } 545 546 case *DebugRef: 547 if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell 548 if instr.IsAddr { 549 instr.X = renamed(renaming, alloc) 550 instr.IsAddr = false 551 552 // Add DebugRef to instr.X's referrers. 553 if refs := instr.X.Referrers(); refs != nil { 554 *refs = append(*refs, instr) 555 } 556 } else { 557 // A source expression denotes the address 558 // of an Alloc that was optimized away. 559 instr.X = nil 560 561 // Delete the DebugRef. 562 u.Instrs[i] = nil 563 u.gaps++ 564 } 565 } 566 } 567 } 568 569 // For each φ-node in a CFG successor, rename the edge. 570 for _, v := range u.Succs { 571 phis := newPhis[v] 572 if len(phis) == 0 { 573 continue 574 } 575 i := v.predIndex(u) 576 for _, np := range phis { 577 phi := np.phi 578 alloc := np.alloc 579 newval := renamed(renaming, alloc) 580 if debugLifting { 581 fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n", 582 phi.Name(), u, v, i, alloc.Name(), newval.Name()) 583 } 584 phi.Edges[i] = newval 585 if prefs := newval.Referrers(); prefs != nil { 586 *prefs = append(*prefs, phi) 587 } 588 } 589 } 590 591 // Continue depth-first recursion over domtree, pushing a 592 // fresh copy of the renaming map for each subtree. 593 for _, v := range u.dom.children { 594 // TODO(adonovan): opt: avoid copy on final iteration; use destructive update. 595 r := make([]Value, len(renaming)) 596 copy(r, renaming) 597 rename(v, r, newPhis) 598 } 599 }