github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/irutil/util.go (about) 1 package irutil 2 3 import ( 4 "go/types" 5 "strings" 6 7 "github.com/amarpal/go-tools/go/ir" 8 "github.com/amarpal/go-tools/go/types/typeutil" 9 ) 10 11 func 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 30 func 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 47 func Vararg(x *ir.Slice) ([]ir.Value, bool) { 48 var out []ir.Value 49 alloc, ok := ir.Unwrap(x.X).(*ir.Alloc) 50 if !ok { 51 return nil, false 52 } 53 var checkAlloc func(alloc ir.Value) bool 54 checkAlloc = func(alloc ir.Value) bool { 55 for _, ref := range *alloc.Referrers() { 56 if ref == x { 57 continue 58 } 59 if ref.Block() != x.Block() { 60 return false 61 } 62 switch ref := ref.(type) { 63 case *ir.IndexAddr: 64 idx := ref 65 if len(*idx.Referrers()) != 1 { 66 return false 67 } 68 store, ok := (*idx.Referrers())[0].(*ir.Store) 69 if !ok { 70 return false 71 } 72 out = append(out, store.Val) 73 case *ir.Copy: 74 if !checkAlloc(ref) { 75 return false 76 } 77 default: 78 return false 79 } 80 } 81 return true 82 } 83 if !checkAlloc(alloc) { 84 return nil, false 85 } 86 return out, true 87 } 88 89 func CallName(call *ir.CallCommon) string { 90 if call.IsInvoke() { 91 return "" 92 } 93 switch v := call.Value.(type) { 94 case *ir.Function: 95 fn, ok := v.Object().(*types.Func) 96 if !ok { 97 return "" 98 } 99 return typeutil.FuncName(fn) 100 case *ir.Builtin: 101 return v.Name() 102 } 103 return "" 104 } 105 106 func IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name } 107 108 func IsCallToAny(call *ir.CallCommon, names ...string) bool { 109 q := CallName(call) 110 for _, name := range names { 111 if q == name { 112 return true 113 } 114 } 115 return false 116 } 117 118 func FilterDebug(instr []ir.Instruction) []ir.Instruction { 119 var out []ir.Instruction 120 for _, ins := range instr { 121 if _, ok := ins.(*ir.DebugRef); !ok { 122 out = append(out, ins) 123 } 124 } 125 return out 126 } 127 128 func IsExample(fn *ir.Function) bool { 129 if !strings.HasPrefix(fn.Name(), "Example") { 130 return false 131 } 132 f := fn.Prog.Fset.File(fn.Pos()) 133 if f == nil { 134 return false 135 } 136 return strings.HasSuffix(f.Name(), "_test.go") 137 } 138 139 // Flatten recursively returns the underlying value of an ir.Sigma or 140 // ir.Phi node. If all edges in an ir.Phi node are the same (after 141 // flattening), the flattened edge will get returned. If flattening is 142 // not possible, nil is returned. 143 func Flatten(v ir.Value) ir.Value { 144 failed := false 145 seen := map[ir.Value]struct{}{} 146 var out ir.Value 147 var dfs func(v ir.Value) 148 dfs = func(v ir.Value) { 149 if failed { 150 return 151 } 152 if _, ok := seen[v]; ok { 153 return 154 } 155 seen[v] = struct{}{} 156 157 switch v := v.(type) { 158 case *ir.Sigma: 159 dfs(v.X) 160 case *ir.Phi: 161 for _, e := range v.Edges { 162 dfs(e) 163 } 164 default: 165 if out == nil { 166 out = v 167 } else if out != v { 168 failed = true 169 } 170 } 171 } 172 dfs(v) 173 174 if failed { 175 return nil 176 } 177 return out 178 }