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