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