github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway. 29 func fuseBlockIf(b *Block) bool { 30 if b.Kind != BlockIf { 31 return false 32 } 33 34 var ss0, ss1 *Block 35 s0 := b.Succs[0] 36 if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 { 37 s0, ss0 = b, s0 38 } else { 39 ss0 = s0.Succs[0] 40 } 41 s1 := b.Succs[1] 42 if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 { 43 s1, ss1 = b, s1 44 } else { 45 ss1 = s1.Succs[0] 46 } 47 48 if ss0 != ss1 { 49 return false 50 } 51 ss := ss0 52 53 // s0 and s1 are equal with b if the corresponding block is missing 54 // (2nd, 3rd and 4th case in the figure). 55 i0, i1 := -1, -1 56 for i, p := range ss.Preds { 57 if p == s0 { 58 i0 = i 59 } 60 if p == s1 { 61 i1 = i 62 } 63 } 64 if i0 == -1 || i1 == -1 { 65 b.Fatalf("invalid predecessors") 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 ss.removePred(s0) 80 81 // Replace edge b->s1->ss with b->ss. 82 // We need to keep a slot for Phis corresponding to b. 83 for i := range b.Succs { 84 if b.Succs[i] == s1 { 85 b.Succs[i] = ss 86 } 87 } 88 for i := range ss.Preds { 89 if ss.Preds[i] == s1 { 90 ss.Preds[i] = b 91 } 92 } 93 } else if s0 != b { 94 ss.removePred(s0) 95 } else if s1 != b { 96 ss.removePred(s1) 97 } 98 b.Kind = BlockPlain 99 b.SetControl(nil) 100 b.Succs = append(b.Succs[:0], ss) 101 102 // Trash the empty blocks s0 & s1. 103 if s0 != b { 104 s0.Kind = BlockInvalid 105 s0.Values = nil 106 s0.Succs = nil 107 s0.Preds = nil 108 } 109 if s1 != b { 110 s1.Kind = BlockInvalid 111 s1.Values = nil 112 s1.Succs = nil 113 s1.Preds = nil 114 } 115 return true 116 } 117 118 func fuseBlockPlain(b *Block) bool { 119 if b.Kind != BlockPlain { 120 return false 121 } 122 123 c := b.Succs[0] 124 if len(c.Preds) != 1 { 125 return false 126 } 127 128 // move all of b'c values to c. 129 for _, v := range b.Values { 130 v.Block = c 131 c.Values = append(c.Values, v) 132 } 133 134 // replace b->c edge with preds(b) -> c 135 c.predstorage[0] = nil 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 _, p := range c.Preds { 142 for i, q := range p.Succs { 143 if q == b { 144 p.Succs[i] = c 145 } 146 } 147 } 148 if f := b.Func; f.Entry == b { 149 f.Entry = c 150 } 151 152 // trash b, just in case 153 b.Kind = BlockInvalid 154 b.Values = nil 155 b.Preds = nil 156 b.Succs = nil 157 return true 158 }