gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/ssa/ssautil/switch.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 // +build go1.5 6 7 package ssautil 8 9 // This file implements discovery of switch and type-switch constructs 10 // from low-level control flow. 11 // 12 // Many techniques exist for compiling a high-level switch with 13 // constant cases to efficient machine code. The optimal choice will 14 // depend on the data type, the specific case values, the code in the 15 // body of each case, and the hardware. 16 // Some examples: 17 // - a lookup table (for a switch that maps constants to constants) 18 // - a computed goto 19 // - a binary tree 20 // - a perfect hash 21 // - a two-level switch (to partition constant strings by their first byte). 22 23 import ( 24 "bytes" 25 "fmt" 26 "go/token" 27 "go/types" 28 29 "github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa" 30 ) 31 32 // A ConstCase represents a single constant comparison. 33 // It is part of a Switch. 34 type ConstCase struct { 35 Block *ssa.BasicBlock // block performing the comparison 36 Body *ssa.BasicBlock // body of the case 37 Value *ssa.Const // case comparand 38 } 39 40 // A TypeCase represents a single type assertion. 41 // It is part of a Switch. 42 type TypeCase struct { 43 Block *ssa.BasicBlock // block performing the type assert 44 Body *ssa.BasicBlock // body of the case 45 Type types.Type // case type 46 Binding ssa.Value // value bound by this case 47 } 48 49 // A Switch is a logical high-level control flow operation 50 // (a multiway branch) discovered by analysis of a CFG containing 51 // only if/else chains. It is not part of the ssa.Instruction set. 52 // 53 // One of ConstCases and TypeCases has length >= 2; 54 // the other is nil. 55 // 56 // In a value switch, the list of cases may contain duplicate constants. 57 // A type switch may contain duplicate types, or types assignable 58 // to an interface type also in the list. 59 // TODO(adonovan): eliminate such duplicates. 60 // 61 type Switch struct { 62 Start *ssa.BasicBlock // block containing start of if/else chain 63 X ssa.Value // the switch operand 64 ConstCases []ConstCase // ordered list of constant comparisons 65 TypeCases []TypeCase // ordered list of type assertions 66 Default *ssa.BasicBlock // successor if all comparisons fail 67 } 68 69 func (sw *Switch) String() string { 70 // We represent each block by the String() of its 71 // first Instruction, e.g. "print(42:int)". 72 var buf bytes.Buffer 73 if sw.ConstCases != nil { 74 fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name()) 75 for _, c := range sw.ConstCases { 76 fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0]) 77 } 78 } else { 79 fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name()) 80 for _, c := range sw.TypeCases { 81 fmt.Fprintf(&buf, "case %s %s: %s\n", 82 c.Binding.Name(), c.Type, c.Body.Instrs[0]) 83 } 84 } 85 if sw.Default != nil { 86 fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0]) 87 } 88 fmt.Fprintf(&buf, "}") 89 return buf.String() 90 } 91 92 // Switches examines the control-flow graph of fn and returns the 93 // set of inferred value and type switches. A value switch tests an 94 // ssa.Value for equality against two or more compile-time constant 95 // values. Switches involving link-time constants (addresses) are 96 // ignored. A type switch type-asserts an ssa.Value against two or 97 // more types. 98 // 99 // The switches are returned in dominance order. 100 // 101 // The resulting switches do not necessarily correspond to uses of the 102 // 'switch' keyword in the source: for example, a single source-level 103 // switch statement with non-constant cases may result in zero, one or 104 // many Switches, one per plural sequence of constant cases. 105 // Switches may even be inferred from if/else- or goto-based control flow. 106 // (In general, the control flow constructs of the source program 107 // cannot be faithfully reproduced from the SSA representation.) 108 // 109 func Switches(fn *ssa.Function) []Switch { 110 // Traverse the CFG in dominance order, so we don't 111 // enter an if/else-chain in the middle. 112 var switches []Switch 113 seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet 114 for _, b := range fn.DomPreorder() { 115 if x, k := isComparisonBlock(b); x != nil { 116 // Block b starts a switch. 117 sw := Switch{Start: b, X: x} 118 valueSwitch(&sw, k, seen) 119 if len(sw.ConstCases) > 1 { 120 switches = append(switches, sw) 121 } 122 } 123 124 if y, x, T := isTypeAssertBlock(b); y != nil { 125 // Block b starts a type switch. 126 sw := Switch{Start: b, X: x} 127 typeSwitch(&sw, y, T, seen) 128 if len(sw.TypeCases) > 1 { 129 switches = append(switches, sw) 130 } 131 } 132 } 133 return switches 134 } 135 136 func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) { 137 b := sw.Start 138 x := sw.X 139 for x == sw.X { 140 if seen[b] { 141 break 142 } 143 seen[b] = true 144 145 sw.ConstCases = append(sw.ConstCases, ConstCase{ 146 Block: b, 147 Body: b.Succs[0], 148 Value: k, 149 }) 150 b = b.Succs[1] 151 if len(b.Instrs) > 2 { 152 // Block b contains not just 'if x == k', 153 // so it may have side effects that 154 // make it unsafe to elide. 155 break 156 } 157 if len(b.Preds) != 1 { 158 // Block b has multiple predecessors, 159 // so it cannot be treated as a case. 160 break 161 } 162 x, k = isComparisonBlock(b) 163 } 164 sw.Default = b 165 } 166 167 func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) { 168 b := sw.Start 169 x := sw.X 170 for x == sw.X { 171 if seen[b] { 172 break 173 } 174 seen[b] = true 175 176 sw.TypeCases = append(sw.TypeCases, TypeCase{ 177 Block: b, 178 Body: b.Succs[0], 179 Type: T, 180 Binding: y, 181 }) 182 b = b.Succs[1] 183 if len(b.Instrs) > 4 { 184 // Block b contains not just 185 // {TypeAssert; Extract #0; Extract #1; If} 186 // so it may have side effects that 187 // make it unsafe to elide. 188 break 189 } 190 if len(b.Preds) != 1 { 191 // Block b has multiple predecessors, 192 // so it cannot be treated as a case. 193 break 194 } 195 y, x, T = isTypeAssertBlock(b) 196 } 197 sw.Default = b 198 } 199 200 // isComparisonBlock returns the operands (v, k) if a block ends with 201 // a comparison v==k, where k is a compile-time constant. 202 // 203 func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) { 204 if n := len(b.Instrs); n >= 2 { 205 if i, ok := b.Instrs[n-1].(*ssa.If); ok { 206 if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL { 207 if k, ok := binop.Y.(*ssa.Const); ok { 208 return binop.X, k 209 } 210 if k, ok := binop.X.(*ssa.Const); ok { 211 return binop.Y, k 212 } 213 } 214 } 215 } 216 return 217 } 218 219 // isTypeAssertBlock returns the operands (y, x, T) if a block ends with 220 // a type assertion "if y, ok := x.(T); ok {". 221 // 222 func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) { 223 if n := len(b.Instrs); n >= 4 { 224 if i, ok := b.Instrs[n-1].(*ssa.If); ok { 225 if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 { 226 if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b { 227 // hack: relies upon instruction ordering. 228 if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok { 229 return ext0, ta.X, ta.AssertedType 230 } 231 } 232 } 233 } 234 } 235 return 236 }