github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/ssa/branchelim.go (about) 1 // Copyright 2017 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 // branchelim tries to elminiate branches by 8 // generating CondSelect instructions. 9 // 10 // Search for basic blocks that look like 11 // 12 // bb0 bb0 13 // | \ / \ 14 // | bb1 or bb1 bb2 <- trivial if/else blocks 15 // | / \ / 16 // bb2 bb3 17 // 18 // where the intermediate blocks are mostly empty (with no side-effects); 19 // rewrite Phis in the postdominator as CondSelects. 20 func branchelim(f *Func) { 21 // FIXME: add support for lowering CondSelects on more architectures 22 if f.Config.arch != "arm64" { 23 return 24 } 25 26 change := true 27 for change { 28 change = false 29 for _, b := range f.Blocks { 30 change = elimIf(f, b) || elimIfElse(f, b) || change 31 } 32 } 33 } 34 35 func canCondSelect(v *Value) bool { 36 // For now, stick to simple scalars that fit in registers 37 sz := v.Type.Size() 38 return sz <= v.Block.Func.Config.RegSize && (v.Type.IsInteger() || v.Type.IsPtrShaped()) 39 } 40 41 func elimIf(f *Func, dom *Block) bool { 42 // See if dom is an If with one arm that 43 // is trivial and succeeded by the other 44 // successor of dom. 45 if dom.Kind != BlockIf || dom.Likely != BranchUnknown { 46 return false 47 } 48 var simple, post *Block 49 for i := range dom.Succs { 50 bb, other := dom.Succs[i].Block(), dom.Succs[i^1].Block() 51 if isLeafPlain(bb) && bb.Succs[0].Block() == other { 52 simple = bb 53 post = other 54 break 55 } 56 } 57 if simple == nil || len(post.Preds) != 2 || post == dom { 58 return false 59 } 60 61 // We've found our diamond CFG of blocks. 62 // Now decide if fusing 'simple' into dom+post 63 // looks profitable. 64 65 // Check that there are Phis, and that all of them 66 // can be safely rewritten to CondSelect. 67 hasphis := false 68 for _, v := range post.Values { 69 if v.Op == OpPhi { 70 hasphis = true 71 if !canCondSelect(v) { 72 return false 73 } 74 } 75 } 76 if !hasphis { 77 return false 78 } 79 80 // Pick some upper bound for the number of instructions 81 // we'd be willing to execute just to generate a dead 82 // argument to CondSelect. In the worst case, this is 83 // the number of useless instructions executed. 84 const maxfuseinsts = 2 85 86 if len(simple.Values) > maxfuseinsts || !allTrivial(simple) { 87 return false 88 } 89 90 // Replace Phi instructions in b with CondSelect instructions 91 swap := (post.Preds[0].Block() == dom) != (dom.Succs[0].Block() == post) 92 for _, v := range post.Values { 93 if v.Op != OpPhi { 94 continue 95 } 96 v.Op = OpCondSelect 97 if swap { 98 v.Args[0], v.Args[1] = v.Args[1], v.Args[0] 99 } 100 v.AddArg(dom.Control) 101 } 102 103 // Put all of the instructions into 'dom' 104 // and update the CFG appropriately. 105 dom.Kind = post.Kind 106 dom.SetControl(post.Control) 107 dom.Aux = post.Aux 108 dom.Succs = append(dom.Succs[:0], post.Succs...) 109 for i := range dom.Succs { 110 e := dom.Succs[i] 111 e.b.Preds[e.i].b = dom 112 } 113 114 for i := range simple.Values { 115 simple.Values[i].Block = dom 116 } 117 for i := range post.Values { 118 post.Values[i].Block = dom 119 } 120 dom.Values = append(dom.Values, simple.Values...) 121 dom.Values = append(dom.Values, post.Values...) 122 123 // Trash 'post' and 'simple' 124 clobberBlock(post) 125 clobberBlock(simple) 126 127 f.invalidateCFG() 128 return true 129 } 130 131 // is this a BlockPlain with one predecessor? 132 func isLeafPlain(b *Block) bool { 133 return b.Kind == BlockPlain && len(b.Preds) == 1 134 } 135 136 func clobberBlock(b *Block) { 137 b.Values = nil 138 b.Preds = nil 139 b.Succs = nil 140 b.Aux = nil 141 b.SetControl(nil) 142 b.Kind = BlockInvalid 143 } 144 145 func elimIfElse(f *Func, b *Block) bool { 146 // See if 'b' ends in an if/else: it should 147 // have two successors, both of which are BlockPlain 148 // and succeeded by the same block. 149 if b.Kind != BlockIf || b.Likely != BranchUnknown { 150 return false 151 } 152 yes, no := b.Succs[0].Block(), b.Succs[1].Block() 153 if !isLeafPlain(yes) || len(yes.Values) > 1 || !allTrivial(yes) { 154 return false 155 } 156 if !isLeafPlain(no) || len(no.Values) > 1 || !allTrivial(no) { 157 return false 158 } 159 if b.Succs[0].Block().Succs[0].Block() != b.Succs[1].Block().Succs[0].Block() { 160 return false 161 } 162 // block that postdominates the if/else 163 post := b.Succs[0].Block().Succs[0].Block() 164 if len(post.Preds) != 2 || post == b { 165 return false 166 } 167 hasphis := false 168 for _, v := range post.Values { 169 if v.Op == OpPhi { 170 hasphis = true 171 if !canCondSelect(v) { 172 return false 173 } 174 } 175 } 176 if !hasphis { 177 return false 178 } 179 180 // now we're committed: rewrite each Phi as a CondSelect 181 swap := post.Preds[0].Block() != b.Succs[0].Block() 182 for _, v := range post.Values { 183 if v.Op != OpPhi { 184 continue 185 } 186 v.Op = OpCondSelect 187 if swap { 188 v.Args[0], v.Args[1] = v.Args[1], v.Args[0] 189 } 190 v.AddArg(b.Control) 191 } 192 193 // Move the contents of all of these 194 // blocks into 'b' and update CFG edges accordingly 195 b.Kind = post.Kind 196 b.SetControl(post.Control) 197 b.Aux = post.Aux 198 b.Succs = append(b.Succs[:0], post.Succs...) 199 for i := range b.Succs { 200 e := b.Succs[i] 201 e.b.Preds[e.i].b = b 202 } 203 for i := range post.Values { 204 post.Values[i].Block = b 205 } 206 for i := range yes.Values { 207 yes.Values[i].Block = b 208 } 209 for i := range no.Values { 210 no.Values[i].Block = b 211 } 212 b.Values = append(b.Values, yes.Values...) 213 b.Values = append(b.Values, no.Values...) 214 b.Values = append(b.Values, post.Values...) 215 216 // trash post, yes, and no 217 clobberBlock(yes) 218 clobberBlock(no) 219 clobberBlock(post) 220 221 f.invalidateCFG() 222 return true 223 } 224 225 func allTrivial(b *Block) bool { 226 // don't fuse memory ops, Phi ops, divides (can panic), 227 // or anything else with side-effects 228 for _, v := range b.Values { 229 if v.Op == OpPhi || isDivMod(v.Op) || v.Type.IsMemory() || 230 v.MemoryArg() != nil || opcodeTable[v.Op].hasSideEffects { 231 return false 232 } 233 } 234 return true 235 } 236 237 func isDivMod(op Op) bool { 238 switch op { 239 case OpDiv8, OpDiv8u, OpDiv16, OpDiv16u, 240 OpDiv32, OpDiv32u, OpDiv64, OpDiv64u, OpDiv128u, 241 OpDiv32F, OpDiv64F, 242 OpMod8, OpMod8u, OpMod16, OpMod16u, 243 OpMod32, OpMod32u, OpMod64, OpMod64u: 244 return true 245 default: 246 return false 247 } 248 }