github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/critical.go (about) 1 // Copyright 2015 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 // critical splits critical edges (those that go from a block with 8 // more than one outedge to a block with more than one inedge). 9 // Regalloc wants a critical-edge-free CFG so it can implement phi values. 10 func critical(f *Func) { 11 // maps from phi arg ID to the new block created for that argument 12 blocks := make([]*Block, f.NumValues()) 13 // need to iterate over f.Blocks without range, as we might 14 // need to split critical edges on newly constructed blocks 15 for j := 0; j < len(f.Blocks); j++ { 16 b := f.Blocks[j] 17 if len(b.Preds) <= 1 { 18 continue 19 } 20 21 var phi *Value 22 // determine if we've only got a single phi in this 23 // block, this is easier to handle than the general 24 // case of a block with multiple phi values. 25 for _, v := range b.Values { 26 if v.Op == OpPhi { 27 if phi != nil { 28 phi = nil 29 break 30 } 31 phi = v 32 } 33 } 34 35 // reset our block map 36 if phi != nil { 37 for _, v := range phi.Args { 38 blocks[v.ID] = nil 39 } 40 } 41 42 // split input edges coming from multi-output blocks. 43 for i := 0; i < len(b.Preds); i++ { 44 c := b.Preds[i] 45 if c.Kind == BlockPlain { 46 continue // only single output block 47 } 48 49 var d *Block // new block used to remove critical edge 50 reusedBlock := false // if true, then this is not the first use of this block 51 if phi != nil { 52 argID := phi.Args[i].ID 53 // find or record the block that we used to split 54 // critical edges for this argument 55 if d = blocks[argID]; d == nil { 56 // splitting doesn't necessarily remove the critical edge, 57 // since we're iterating over len(f.Blocks) above, this forces 58 // the new blocks to be re-examined. 59 d = f.NewBlock(BlockPlain) 60 d.Line = c.Line 61 blocks[argID] = d 62 if f.pass.debug > 0 { 63 f.Config.Warnl(c.Line, "split critical edge") 64 } 65 } else { 66 reusedBlock = true 67 } 68 } else { 69 // no existing block, so allocate a new block 70 // to place on the edge 71 d = f.NewBlock(BlockPlain) 72 d.Line = c.Line 73 if f.pass.debug > 0 { 74 f.Config.Warnl(c.Line, "split critical edge") 75 } 76 } 77 78 // if this not the first argument for the 79 // block, then we need to remove the 80 // corresponding elements from the block 81 // predecessors and phi args 82 if reusedBlock { 83 d.Preds = append(d.Preds, c) 84 b.Preds[i] = nil 85 phi.Args[i].Uses-- 86 phi.Args[i] = nil 87 } else { 88 // splice it in 89 d.Preds = append(d.Preds, c) 90 d.Succs = append(d.Succs, b) 91 b.Preds[i] = d 92 } 93 94 // replace b with d in c's successor list. 95 for j, b2 := range c.Succs { 96 if b2 == b { 97 c.Succs[j] = d 98 break 99 } 100 } 101 } 102 103 // clean up phi's args and b's predecessor list 104 if phi != nil { 105 phi.Args = filterNilValues(phi.Args) 106 b.Preds = filterNilBlocks(b.Preds) 107 // splitting occasionally leads to a phi having 108 // a single argument (occurs with -N) 109 if len(phi.Args) == 1 { 110 phi.Op = OpCopy 111 } 112 } 113 } 114 } 115 116 // filterNilValues preserves the order of v, while filtering out nils. 117 func filterNilValues(v []*Value) []*Value { 118 nv := v[:0] 119 for i := range v { 120 if v[i] != nil { 121 nv = append(nv, v[i]) 122 } 123 } 124 return nv 125 } 126 127 // filterNilBlocks preserves the order of b, while filtering out nils. 128 func filterNilBlocks(b []*Block) []*Block { 129 nb := b[:0] 130 for i := range b { 131 if b[i] != nil { 132 nb = append(nb, b[i]) 133 } 134 } 135 return nb 136 }