1package irutil 2 3import ( 4 "go/types" 5 "strings" 6 7 "honnef.co/go/tools/go/ir" 8 "honnef.co/go/tools/go/types/typeutil" 9) 10 11func Reachable(from, to *ir.BasicBlock) bool { 12 if from == to { 13 return true 14 } 15 if from.Dominates(to) { 16 return true 17 } 18 19 found := false 20 Walk(from, func(b *ir.BasicBlock) bool { 21 if b == to { 22 found = true 23 return false 24 } 25 return true 26 }) 27 return found 28} 29 30func Walk(b *ir.BasicBlock, fn func(*ir.BasicBlock) bool) { 31 seen := map[*ir.BasicBlock]bool{} 32 wl := []*ir.BasicBlock{b} 33 for len(wl) > 0 { 34 b := wl[len(wl)-1] 35 wl = wl[:len(wl)-1] 36 if seen[b] { 37 continue 38 } 39 seen[b] = true 40 if !fn(b) { 41 continue 42 } 43 wl = append(wl, b.Succs...) 44 } 45} 46 47func Vararg(x *ir.Slice) ([]ir.Value, bool) { 48 var out []ir.Value 49 slice, ok := x.X.(*ir.Alloc) 50 if !ok { 51 return nil, false 52 } 53 for _, ref := range *slice.Referrers() { 54 if ref == x { 55 continue 56 } 57 if ref.Block() != x.Block() { 58 return nil, false 59 } 60 idx, ok := ref.(*ir.IndexAddr) 61 if !ok { 62 return nil, false 63 } 64 if len(*idx.Referrers()) != 1 { 65 return nil, false 66 } 67 store, ok := (*idx.Referrers())[0].(*ir.Store) 68 if !ok { 69 return nil, false 70 } 71 out = append(out, store.Val) 72 } 73 return out, true 74} 75 76func CallName(call *ir.CallCommon) string { 77 if call.IsInvoke() { 78 return "" 79 } 80 switch v := call.Value.(type) { 81 case *ir.Function: 82 fn, ok := v.Object().(*types.Func) 83 if !ok { 84 return "" 85 } 86 return typeutil.FuncName(fn) 87 case *ir.Builtin: 88 return v.Name() 89 } 90 return "" 91} 92 93func IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name } 94 95func IsCallToAny(call *ir.CallCommon, names ...string) bool { 96 q := CallName(call) 97 for _, name := range names { 98 if q == name { 99 return true 100 } 101 } 102 return false 103} 104 105func FilterDebug(instr []ir.Instruction) []ir.Instruction { 106 var out []ir.Instruction 107 for _, ins := range instr { 108 if _, ok := ins.(*ir.DebugRef); !ok { 109 out = append(out, ins) 110 } 111 } 112 return out 113} 114 115func IsExample(fn *ir.Function) bool { 116 if !strings.HasPrefix(fn.Name(), "Example") { 117 return false 118 } 119 f := fn.Prog.Fset.File(fn.Pos()) 120 if f == nil { 121 return false 122 } 123 return strings.HasSuffix(f.Name(), "_test.go") 124} 125 126// Flatten recursively returns the underlying value of an ir.Sigma or 127// ir.Phi node. If all edges in an ir.Phi node are the same (after 128// flattening), the flattened edge will get returned. If flattening is 129// not possible, nil is returned. 130func Flatten(v ir.Value) ir.Value { 131 failed := false 132 seen := map[ir.Value]struct{}{} 133 var out ir.Value 134 var dfs func(v ir.Value) 135 dfs = func(v ir.Value) { 136 if failed { 137 return 138 } 139 if _, ok := seen[v]; ok { 140 return 141 } 142 seen[v] = struct{}{} 143 144 switch v := v.(type) { 145 case *ir.Sigma: 146 dfs(v.X) 147 case *ir.Phi: 148 for _, e := range v.Edges { 149 dfs(e) 150 } 151 default: 152 if out == nil { 153 out = v 154 } else if out != v { 155 failed = true 156 } 157 } 158 } 159 dfs(v) 160 161 if failed { 162 return nil 163 } 164 return out 165} 166