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  }