github.com/jd-ly/tools@v0.5.7/go/ssa/blockopt.go (about) 1 // Copyright 2013 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 // Simple block optimizations to simplify the control flow graph. 8 9 // TODO(adonovan): opt: instead of creating several "unreachable" blocks 10 // per function in the Builder, reuse a single one (e.g. at Blocks[1]) 11 // to reduce garbage. 12 13 import ( 14 "fmt" 15 "os" 16 ) 17 18 // If true, perform sanity checking and show progress at each 19 // successive iteration of optimizeBlocks. Very verbose. 20 const debugBlockOpt = false 21 22 // markReachable sets Index=-1 for all blocks reachable from b. 23 func markReachable(b *BasicBlock) { 24 b.Index = -1 25 for _, succ := range b.Succs { 26 if succ.Index == 0 { 27 markReachable(succ) 28 } 29 } 30 } 31 32 // deleteUnreachableBlocks marks all reachable blocks of f and 33 // eliminates (nils) all others, including possibly cyclic subgraphs. 34 // 35 func deleteUnreachableBlocks(f *Function) { 36 const white, black = 0, -1 37 // We borrow b.Index temporarily as the mark bit. 38 for _, b := range f.Blocks { 39 b.Index = white 40 } 41 markReachable(f.Blocks[0]) 42 if f.Recover != nil { 43 markReachable(f.Recover) 44 } 45 for i, b := range f.Blocks { 46 if b.Index == white { 47 for _, c := range b.Succs { 48 if c.Index == black { 49 c.removePred(b) // delete white->black edge 50 } 51 } 52 if debugBlockOpt { 53 fmt.Fprintln(os.Stderr, "unreachable", b) 54 } 55 f.Blocks[i] = nil // delete b 56 } 57 } 58 f.removeNilBlocks() 59 } 60 61 // jumpThreading attempts to apply simple jump-threading to block b, 62 // in which a->b->c become a->c if b is just a Jump. 63 // The result is true if the optimization was applied. 64 // 65 func jumpThreading(f *Function, b *BasicBlock) bool { 66 if b.Index == 0 { 67 return false // don't apply to entry block 68 } 69 if b.Instrs == nil { 70 return false 71 } 72 if _, ok := b.Instrs[0].(*Jump); !ok { 73 return false // not just a jump 74 } 75 c := b.Succs[0] 76 if c == b { 77 return false // don't apply to degenerate jump-to-self. 78 } 79 if c.hasPhi() { 80 return false // not sound without more effort 81 } 82 for j, a := range b.Preds { 83 a.replaceSucc(b, c) 84 85 // If a now has two edges to c, replace its degenerate If by Jump. 86 if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c { 87 jump := new(Jump) 88 jump.setBlock(a) 89 a.Instrs[len(a.Instrs)-1] = jump 90 a.Succs = a.Succs[:1] 91 c.removePred(b) 92 } else { 93 if j == 0 { 94 c.replacePred(b, a) 95 } else { 96 c.Preds = append(c.Preds, a) 97 } 98 } 99 100 if debugBlockOpt { 101 fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c) 102 } 103 } 104 f.Blocks[b.Index] = nil // delete b 105 return true 106 } 107 108 // fuseBlocks attempts to apply the block fusion optimization to block 109 // a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1. 110 // The result is true if the optimization was applied. 111 // 112 func fuseBlocks(f *Function, a *BasicBlock) bool { 113 if len(a.Succs) != 1 { 114 return false 115 } 116 b := a.Succs[0] 117 if len(b.Preds) != 1 { 118 return false 119 } 120 121 // Degenerate &&/|| ops may result in a straight-line CFG 122 // containing φ-nodes. (Ideally we'd replace such them with 123 // their sole operand but that requires Referrers, built later.) 124 if b.hasPhi() { 125 return false // not sound without further effort 126 } 127 128 // Eliminate jump at end of A, then copy all of B across. 129 a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...) 130 for _, instr := range b.Instrs { 131 instr.setBlock(a) 132 } 133 134 // A inherits B's successors 135 a.Succs = append(a.succs2[:0], b.Succs...) 136 137 // Fix up Preds links of all successors of B. 138 for _, c := range b.Succs { 139 c.replacePred(b, a) 140 } 141 142 if debugBlockOpt { 143 fmt.Fprintln(os.Stderr, "fuseBlocks", a, b) 144 } 145 146 f.Blocks[b.Index] = nil // delete b 147 return true 148 } 149 150 // optimizeBlocks() performs some simple block optimizations on a 151 // completed function: dead block elimination, block fusion, jump 152 // threading. 153 // 154 func optimizeBlocks(f *Function) { 155 deleteUnreachableBlocks(f) 156 157 // Loop until no further progress. 158 changed := true 159 for changed { 160 changed = false 161 162 if debugBlockOpt { 163 f.WriteTo(os.Stderr) 164 mustSanityCheck(f, nil) 165 } 166 167 for _, b := range f.Blocks { 168 // f.Blocks will temporarily contain nils to indicate 169 // deleted blocks; we remove them at the end. 170 if b == nil { 171 continue 172 } 173 174 // Fuse blocks. b->c becomes bc. 175 if fuseBlocks(f, b) { 176 changed = true 177 } 178 179 // a->b->c becomes a->c if b contains only a Jump. 180 if jumpThreading(f, b) { 181 changed = true 182 continue // (b was disconnected) 183 } 184 } 185 } 186 f.removeNilBlocks() 187 }