github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/gc/phi.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 gc 6 7 import ( 8 "github.com/gagliardetto/golang-go/cmd/compile/internal/ssa" 9 "github.com/gagliardetto/golang-go/cmd/compile/internal/types" 10 "github.com/gagliardetto/golang-go/cmd/internal/src" 11 "container/heap" 12 "fmt" 13 ) 14 15 // This file contains the algorithm to place phi nodes in a function. 16 // For small functions, we use Braun, Buchwald, Hack, Leißa, Mallon, and Zwinkau. 17 // https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf 18 // For large functions, we use Sreedhar & Gao: A Linear Time Algorithm for Placing Φ-Nodes. 19 // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.1979&rep=rep1&type=pdf 20 21 const smallBlocks = 500 22 23 const debugPhi = false 24 25 // insertPhis finds all the places in the function where a phi is 26 // necessary and inserts them. 27 // Uses FwdRef ops to find all uses of variables, and s.defvars to find 28 // all definitions. 29 // Phi values are inserted, and all FwdRefs are changed to a Copy 30 // of the appropriate phi or definition. 31 // TODO: make this part of cmd/compile/internal/ssa somehow? 32 func (s *state) insertPhis() { 33 if len(s.f.Blocks) <= smallBlocks { 34 sps := simplePhiState{s: s, f: s.f, defvars: s.defvars} 35 sps.insertPhis() 36 return 37 } 38 ps := phiState{s: s, f: s.f, defvars: s.defvars} 39 ps.insertPhis() 40 } 41 42 type phiState struct { 43 s *state // SSA state 44 f *ssa.Func // function to work on 45 defvars []map[*Node]*ssa.Value // defined variables at end of each block 46 47 varnum map[*Node]int32 // variable numbering 48 49 // properties of the dominator tree 50 idom []*ssa.Block // dominator parents 51 tree []domBlock // dominator child+sibling 52 level []int32 // level in dominator tree (0 = root or unreachable, 1 = children of root, ...) 53 54 // scratch locations 55 priq blockHeap // priority queue of blocks, higher level (toward leaves) = higher priority 56 q []*ssa.Block // inner loop queue 57 queued *sparseSet // has been put in q 58 hasPhi *sparseSet // has a phi 59 hasDef *sparseSet // has a write of the variable we're processing 60 61 // miscellaneous 62 placeholder *ssa.Value // dummy value to use as a "not set yet" placeholder. 63 } 64 65 func (s *phiState) insertPhis() { 66 if debugPhi { 67 fmt.Println(s.f.String()) 68 } 69 70 // Find all the variables for which we need to match up reads & writes. 71 // This step prunes any basic-block-only variables from consideration. 72 // Generate a numbering for these variables. 73 s.varnum = map[*Node]int32{} 74 var vars []*Node 75 var vartypes []*types.Type 76 for _, b := range s.f.Blocks { 77 for _, v := range b.Values { 78 if v.Op != ssa.OpFwdRef { 79 continue 80 } 81 var_ := v.Aux.(*Node) 82 83 // Optimization: look back 1 block for the definition. 84 if len(b.Preds) == 1 { 85 c := b.Preds[0].Block() 86 if w := s.defvars[c.ID][var_]; w != nil { 87 v.Op = ssa.OpCopy 88 v.Aux = nil 89 v.AddArg(w) 90 continue 91 } 92 } 93 94 if _, ok := s.varnum[var_]; ok { 95 continue 96 } 97 s.varnum[var_] = int32(len(vartypes)) 98 if debugPhi { 99 fmt.Printf("var%d = %v\n", len(vartypes), var_) 100 } 101 vars = append(vars, var_) 102 vartypes = append(vartypes, v.Type) 103 } 104 } 105 106 if len(vartypes) == 0 { 107 return 108 } 109 110 // Find all definitions of the variables we need to process. 111 // defs[n] contains all the blocks in which variable number n is assigned. 112 defs := make([][]*ssa.Block, len(vartypes)) 113 for _, b := range s.f.Blocks { 114 for var_ := range s.defvars[b.ID] { // TODO: encode defvars some other way (explicit ops)? make defvars[n] a slice instead of a map. 115 if n, ok := s.varnum[var_]; ok { 116 defs[n] = append(defs[n], b) 117 } 118 } 119 } 120 121 // Make dominator tree. 122 s.idom = s.f.Idom() 123 s.tree = make([]domBlock, s.f.NumBlocks()) 124 for _, b := range s.f.Blocks { 125 p := s.idom[b.ID] 126 if p != nil { 127 s.tree[b.ID].sibling = s.tree[p.ID].firstChild 128 s.tree[p.ID].firstChild = b 129 } 130 } 131 // Compute levels in dominator tree. 132 // With parent pointers we can do a depth-first walk without 133 // any auxiliary storage. 134 s.level = make([]int32, s.f.NumBlocks()) 135 b := s.f.Entry 136 levels: 137 for { 138 if p := s.idom[b.ID]; p != nil { 139 s.level[b.ID] = s.level[p.ID] + 1 140 if debugPhi { 141 fmt.Printf("level %s = %d\n", b, s.level[b.ID]) 142 } 143 } 144 if c := s.tree[b.ID].firstChild; c != nil { 145 b = c 146 continue 147 } 148 for { 149 if c := s.tree[b.ID].sibling; c != nil { 150 b = c 151 continue levels 152 } 153 b = s.idom[b.ID] 154 if b == nil { 155 break levels 156 } 157 } 158 } 159 160 // Allocate scratch locations. 161 s.priq.level = s.level 162 s.q = make([]*ssa.Block, 0, s.f.NumBlocks()) 163 s.queued = newSparseSet(s.f.NumBlocks()) 164 s.hasPhi = newSparseSet(s.f.NumBlocks()) 165 s.hasDef = newSparseSet(s.f.NumBlocks()) 166 s.placeholder = s.s.entryNewValue0(ssa.OpUnknown, types.TypeInvalid) 167 168 // Generate phi ops for each variable. 169 for n := range vartypes { 170 s.insertVarPhis(n, vars[n], defs[n], vartypes[n]) 171 } 172 173 // Resolve FwdRefs to the correct write or phi. 174 s.resolveFwdRefs() 175 176 // Erase variable numbers stored in AuxInt fields of phi ops. They are no longer needed. 177 for _, b := range s.f.Blocks { 178 for _, v := range b.Values { 179 if v.Op == ssa.OpPhi { 180 v.AuxInt = 0 181 } 182 } 183 } 184 } 185 186 func (s *phiState) insertVarPhis(n int, var_ *Node, defs []*ssa.Block, typ *types.Type) { 187 priq := &s.priq 188 q := s.q 189 queued := s.queued 190 queued.clear() 191 hasPhi := s.hasPhi 192 hasPhi.clear() 193 hasDef := s.hasDef 194 hasDef.clear() 195 196 // Add defining blocks to priority queue. 197 for _, b := range defs { 198 priq.a = append(priq.a, b) 199 hasDef.add(b.ID) 200 if debugPhi { 201 fmt.Printf("def of var%d in %s\n", n, b) 202 } 203 } 204 heap.Init(priq) 205 206 // Visit blocks defining variable n, from deepest to shallowest. 207 for len(priq.a) > 0 { 208 currentRoot := heap.Pop(priq).(*ssa.Block) 209 if debugPhi { 210 fmt.Printf("currentRoot %s\n", currentRoot) 211 } 212 // Walk subtree below definition. 213 // Skip subtrees we've done in previous iterations. 214 // Find edges exiting tree dominated by definition (the dominance frontier). 215 // Insert phis at target blocks. 216 if queued.contains(currentRoot.ID) { 217 s.s.Fatalf("root already in queue") 218 } 219 q = append(q, currentRoot) 220 queued.add(currentRoot.ID) 221 for len(q) > 0 { 222 b := q[len(q)-1] 223 q = q[:len(q)-1] 224 if debugPhi { 225 fmt.Printf(" processing %s\n", b) 226 } 227 228 currentRootLevel := s.level[currentRoot.ID] 229 for _, e := range b.Succs { 230 c := e.Block() 231 // TODO: if the variable is dead at c, skip it. 232 if s.level[c.ID] > currentRootLevel { 233 // a D-edge, or an edge whose target is in currentRoot's subtree. 234 continue 235 } 236 if hasPhi.contains(c.ID) { 237 continue 238 } 239 // Add a phi to block c for variable n. 240 hasPhi.add(c.ID) 241 v := c.NewValue0I(currentRoot.Pos, ssa.OpPhi, typ, int64(n)) // TODO: line number right? 242 // Note: we store the variable number in the phi's AuxInt field. Used temporarily by phi building. 243 s.s.addNamedValue(var_, v) 244 for range c.Preds { 245 v.AddArg(s.placeholder) // Actual args will be filled in by resolveFwdRefs. 246 } 247 if debugPhi { 248 fmt.Printf("new phi for var%d in %s: %s\n", n, c, v) 249 } 250 if !hasDef.contains(c.ID) { 251 // There's now a new definition of this variable in block c. 252 // Add it to the priority queue to explore. 253 heap.Push(priq, c) 254 hasDef.add(c.ID) 255 } 256 } 257 258 // Visit children if they have not been visited yet. 259 for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling { 260 if !queued.contains(c.ID) { 261 q = append(q, c) 262 queued.add(c.ID) 263 } 264 } 265 } 266 } 267 } 268 269 // resolveFwdRefs links all FwdRef uses up to their nearest dominating definition. 270 func (s *phiState) resolveFwdRefs() { 271 // Do a depth-first walk of the dominator tree, keeping track 272 // of the most-recently-seen value for each variable. 273 274 // Map from variable ID to SSA value at the current point of the walk. 275 values := make([]*ssa.Value, len(s.varnum)) 276 for i := range values { 277 values[i] = s.placeholder 278 } 279 280 // Stack of work to do. 281 type stackEntry struct { 282 b *ssa.Block // block to explore 283 284 // variable/value pair to reinstate on exit 285 n int32 // variable ID 286 v *ssa.Value 287 288 // Note: only one of b or n,v will be set. 289 } 290 var stk []stackEntry 291 292 stk = append(stk, stackEntry{b: s.f.Entry}) 293 for len(stk) > 0 { 294 work := stk[len(stk)-1] 295 stk = stk[:len(stk)-1] 296 297 b := work.b 298 if b == nil { 299 // On exit from a block, this case will undo any assignments done below. 300 values[work.n] = work.v 301 continue 302 } 303 304 // Process phis as new defs. They come before FwdRefs in this block. 305 for _, v := range b.Values { 306 if v.Op != ssa.OpPhi { 307 continue 308 } 309 n := int32(v.AuxInt) 310 // Remember the old assignment so we can undo it when we exit b. 311 stk = append(stk, stackEntry{n: n, v: values[n]}) 312 // Record the new assignment. 313 values[n] = v 314 } 315 316 // Replace a FwdRef op with the current incoming value for its variable. 317 for _, v := range b.Values { 318 if v.Op != ssa.OpFwdRef { 319 continue 320 } 321 n := s.varnum[v.Aux.(*Node)] 322 v.Op = ssa.OpCopy 323 v.Aux = nil 324 v.AddArg(values[n]) 325 } 326 327 // Establish values for variables defined in b. 328 for var_, v := range s.defvars[b.ID] { 329 n, ok := s.varnum[var_] 330 if !ok { 331 // some variable not live across a basic block boundary. 332 continue 333 } 334 // Remember the old assignment so we can undo it when we exit b. 335 stk = append(stk, stackEntry{n: n, v: values[n]}) 336 // Record the new assignment. 337 values[n] = v 338 } 339 340 // Replace phi args in successors with the current incoming value. 341 for _, e := range b.Succs { 342 c, i := e.Block(), e.Index() 343 for j := len(c.Values) - 1; j >= 0; j-- { 344 v := c.Values[j] 345 if v.Op != ssa.OpPhi { 346 break // All phis will be at the end of the block during phi building. 347 } 348 // Only set arguments that have been resolved. 349 // For very wide CFGs, this significantly speeds up phi resolution. 350 // See golang.org/issue/8225. 351 if w := values[v.AuxInt]; w.Op != ssa.OpUnknown { 352 v.SetArg(i, w) 353 } 354 } 355 } 356 357 // Walk children in dominator tree. 358 for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling { 359 stk = append(stk, stackEntry{b: c}) 360 } 361 } 362 } 363 364 // domBlock contains extra per-block information to record the dominator tree. 365 type domBlock struct { 366 firstChild *ssa.Block // first child of block in dominator tree 367 sibling *ssa.Block // next child of parent in dominator tree 368 } 369 370 // A block heap is used as a priority queue to implement the PiggyBank 371 // from Sreedhar and Gao. That paper uses an array which is better 372 // asymptotically but worse in the common case when the PiggyBank 373 // holds a sparse set of blocks. 374 type blockHeap struct { 375 a []*ssa.Block // block IDs in heap 376 level []int32 // depth in dominator tree (static, used for determining priority) 377 } 378 379 func (h *blockHeap) Len() int { return len(h.a) } 380 func (h *blockHeap) Swap(i, j int) { a := h.a; a[i], a[j] = a[j], a[i] } 381 382 func (h *blockHeap) Push(x interface{}) { 383 v := x.(*ssa.Block) 384 h.a = append(h.a, v) 385 } 386 func (h *blockHeap) Pop() interface{} { 387 old := h.a 388 n := len(old) 389 x := old[n-1] 390 h.a = old[:n-1] 391 return x 392 } 393 func (h *blockHeap) Less(i, j int) bool { 394 return h.level[h.a[i].ID] > h.level[h.a[j].ID] 395 } 396 397 // TODO: stop walking the iterated domininance frontier when 398 // the variable is dead. Maybe detect that by checking if the 399 // node we're on is reverse dominated by all the reads? 400 // Reverse dominated by the highest common successor of all the reads? 401 402 // copy of ../ssa/sparseset.go 403 // TODO: move this file to ../ssa, then use sparseSet there. 404 type sparseSet struct { 405 dense []ssa.ID 406 sparse []int32 407 } 408 409 // newSparseSet returns a sparseSet that can represent 410 // integers between 0 and n-1 411 func newSparseSet(n int) *sparseSet { 412 return &sparseSet{dense: nil, sparse: make([]int32, n)} 413 } 414 415 func (s *sparseSet) contains(x ssa.ID) bool { 416 i := s.sparse[x] 417 return i < int32(len(s.dense)) && s.dense[i] == x 418 } 419 420 func (s *sparseSet) add(x ssa.ID) { 421 i := s.sparse[x] 422 if i < int32(len(s.dense)) && s.dense[i] == x { 423 return 424 } 425 s.dense = append(s.dense, x) 426 s.sparse[x] = int32(len(s.dense)) - 1 427 } 428 429 func (s *sparseSet) clear() { 430 s.dense = s.dense[:0] 431 } 432 433 // Variant to use for small functions. 434 type simplePhiState struct { 435 s *state // SSA state 436 f *ssa.Func // function to work on 437 fwdrefs []*ssa.Value // list of FwdRefs to be processed 438 defvars []map[*Node]*ssa.Value // defined variables at end of each block 439 reachable []bool // which blocks are reachable 440 } 441 442 func (s *simplePhiState) insertPhis() { 443 s.reachable = ssa.ReachableBlocks(s.f) 444 445 // Find FwdRef ops. 446 for _, b := range s.f.Blocks { 447 for _, v := range b.Values { 448 if v.Op != ssa.OpFwdRef { 449 continue 450 } 451 s.fwdrefs = append(s.fwdrefs, v) 452 var_ := v.Aux.(*Node) 453 if _, ok := s.defvars[b.ID][var_]; !ok { 454 s.defvars[b.ID][var_] = v // treat FwdDefs as definitions. 455 } 456 } 457 } 458 459 var args []*ssa.Value 460 461 loop: 462 for len(s.fwdrefs) > 0 { 463 v := s.fwdrefs[len(s.fwdrefs)-1] 464 s.fwdrefs = s.fwdrefs[:len(s.fwdrefs)-1] 465 b := v.Block 466 var_ := v.Aux.(*Node) 467 if b == s.f.Entry { 468 // No variable should be live at entry. 469 s.s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, var_, v) 470 } 471 if !s.reachable[b.ID] { 472 // This block is dead. 473 // It doesn't matter what we use here as long as it is well-formed. 474 v.Op = ssa.OpUnknown 475 v.Aux = nil 476 continue 477 } 478 // Find variable value on each predecessor. 479 args = args[:0] 480 for _, e := range b.Preds { 481 args = append(args, s.lookupVarOutgoing(e.Block(), v.Type, var_, v.Pos)) 482 } 483 484 // Decide if we need a phi or not. We need a phi if there 485 // are two different args (which are both not v). 486 var w *ssa.Value 487 for _, a := range args { 488 if a == v { 489 continue // self-reference 490 } 491 if a == w { 492 continue // already have this witness 493 } 494 if w != nil { 495 // two witnesses, need a phi value 496 v.Op = ssa.OpPhi 497 v.AddArgs(args...) 498 v.Aux = nil 499 continue loop 500 } 501 w = a // save witness 502 } 503 if w == nil { 504 s.s.Fatalf("no witness for reachable phi %s", v) 505 } 506 // One witness. Make v a copy of w. 507 v.Op = ssa.OpCopy 508 v.Aux = nil 509 v.AddArg(w) 510 } 511 } 512 513 // lookupVarOutgoing finds the variable's value at the end of block b. 514 func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t *types.Type, var_ *Node, line src.XPos) *ssa.Value { 515 for { 516 if v := s.defvars[b.ID][var_]; v != nil { 517 return v 518 } 519 // The variable is not defined by b and we haven't looked it up yet. 520 // If b has exactly one predecessor, loop to look it up there. 521 // Otherwise, give up and insert a new FwdRef and resolve it later. 522 if len(b.Preds) != 1 { 523 break 524 } 525 b = b.Preds[0].Block() 526 if !s.reachable[b.ID] { 527 // This is rare; it happens with oddly interleaved infinite loops in dead code. 528 // See issue 19783. 529 break 530 } 531 } 532 // Generate a FwdRef for the variable and return that. 533 v := b.NewValue0A(line, ssa.OpFwdRef, t, var_) 534 s.defvars[b.ID][var_] = v 535 s.s.addNamedValue(var_, v) 536 s.fwdrefs = append(s.fwdrefs, v) 537 return v 538 }