github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/compile/internal/ssa/fuse.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 // fuse simplifies control flow by joining basic blocks. 8 func fuse(f *Func) { 9 for changed := true; changed; { 10 changed = false 11 for _, b := range f.Blocks { 12 changed = fuseBlockIf(b) || changed 13 changed = fuseBlockPlain(b) || changed 14 } 15 } 16 } 17 18 // fuseBlockIf handles the following cases where s0 and s1 are empty blocks. 19 // 20 // b b b b 21 // / \ | \ / | | | 22 // s0 s1 | s1 s0 | | | 23 // \ / | / \ | | | 24 // ss ss ss ss 25 // 26 // If all Phi ops in ss have identical variables for slots corresponding to 27 // s0, s1 and b then the branch can be dropped. 28 // This optimization often comes up in switch statements with multiple 29 // expressions in a case clause: 30 // switch n { 31 // case 1,2,3: return 4 32 // } 33 // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway. 34 func fuseBlockIf(b *Block) bool { 35 if b.Kind != BlockIf { 36 return false 37 } 38 39 var ss0, ss1 *Block 40 s0 := b.Succs[0].b 41 i0 := b.Succs[0].i 42 if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 { 43 s0, ss0 = b, s0 44 } else { 45 ss0 = s0.Succs[0].b 46 i0 = s0.Succs[0].i 47 } 48 s1 := b.Succs[1].b 49 i1 := b.Succs[1].i 50 if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 { 51 s1, ss1 = b, s1 52 } else { 53 ss1 = s1.Succs[0].b 54 i1 = s1.Succs[0].i 55 } 56 57 if ss0 != ss1 { 58 return false 59 } 60 ss := ss0 61 62 // s0 and s1 are equal with b if the corresponding block is missing 63 // (2nd, 3rd and 4th case in the figure). 64 65 for _, v := range ss.Values { 66 if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] { 67 return false 68 } 69 } 70 71 // Now we have two of following b->ss, b->s0->ss and b->s1->ss, 72 // with s0 and s1 empty if exist. 73 // We can replace it with b->ss without if all OpPhis in ss 74 // have identical predecessors (verified above). 75 // No critical edge is introduced because b will have one successor. 76 if s0 != b && s1 != b { 77 // Replace edge b->s0->ss with b->ss. 78 // We need to keep a slot for Phis corresponding to b. 79 b.Succs[0] = Edge{ss, i0} 80 ss.Preds[i0] = Edge{b, 0} 81 b.removeEdge(1) 82 s1.removeEdge(0) 83 } else if s0 != b { 84 b.removeEdge(0) 85 s0.removeEdge(0) 86 } else if s1 != b { 87 b.removeEdge(1) 88 s1.removeEdge(0) 89 } else { 90 b.removeEdge(1) 91 } 92 b.Kind = BlockPlain 93 b.SetControl(nil) 94 95 // Trash the empty blocks s0 & s1. 96 if s0 != b { 97 s0.Kind = BlockInvalid 98 s0.Values = nil 99 s0.Succs = nil 100 s0.Preds = nil 101 } 102 if s1 != b { 103 s1.Kind = BlockInvalid 104 s1.Values = nil 105 s1.Succs = nil 106 s1.Preds = nil 107 } 108 return true 109 } 110 111 func fuseBlockPlain(b *Block) bool { 112 if b.Kind != BlockPlain { 113 return false 114 } 115 116 c := b.Succs[0].b 117 if len(c.Preds) != 1 { 118 return false 119 } 120 121 // move all of b's values to c. 122 for _, v := range b.Values { 123 v.Block = c 124 } 125 // Use whichever value slice is larger, in the hopes of avoiding growth. 126 // However, take care to avoid c.Values pointing to b.valstorage. 127 // See golang.org/issue/18602. 128 if cap(c.Values) >= cap(b.Values) || len(b.Values) <= len(b.valstorage) { 129 c.Values = append(c.Values, b.Values...) 130 } else { 131 c.Values = append(b.Values, c.Values...) 132 } 133 134 // replace b->c edge with preds(b) -> c 135 c.predstorage[0] = Edge{} 136 if len(b.Preds) > len(b.predstorage) { 137 c.Preds = b.Preds 138 } else { 139 c.Preds = append(c.predstorage[:0], b.Preds...) 140 } 141 for i, e := range c.Preds { 142 p := e.b 143 p.Succs[e.i] = Edge{c, i} 144 } 145 f := b.Func 146 if f.Entry == b { 147 f.Entry = c 148 } 149 f.invalidateCFG() 150 151 // trash b, just in case 152 b.Kind = BlockInvalid 153 b.Values = nil 154 b.Preds = nil 155 b.Succs = nil 156 return true 157 }