1 // Copyright 2023 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 inlheur 6 7 import ( 8 "cmd/compile/internal/ir" 9 "go/constant" 10 ) 11 12 // nameFinder provides a set of "isXXX" query methods for clients to 13 // ask whether a given AST node corresponds to a function, a constant 14 // value, and so on. These methods use an underlying ir.ReassignOracle 15 // to return more precise results in cases where an "interesting" 16 // value is assigned to a singly-defined local temp. Example: 17 // 18 // const q = 101 19 // fq := func() int { return q } 20 // copyOfConstant := q 21 // copyOfFunc := f 22 // interestingCall(copyOfConstant, copyOfFunc) 23 // 24 // A name finder query method invoked on the arguments being passed to 25 // "interestingCall" will be able detect that 'copyOfConstant' always 26 // evaluates to a constant (even though it is in fact a PAUTO local 27 // variable). A given nameFinder can also operate without using 28 // ir.ReassignOracle (in cases where it is not practical to look 29 // at the entire function); in such cases queries will still work 30 // for explicit constant values and functions. 31 type nameFinder struct { 32 ro *ir.ReassignOracle 33 } 34 35 // newNameFinder returns a new nameFinder object with a reassignment 36 // oracle initialized based on the function fn, or if fn is nil, 37 // without an underlying ReassignOracle. 38 func newNameFinder(fn *ir.Func) *nameFinder { 39 var ro *ir.ReassignOracle 40 if fn != nil { 41 ro = &ir.ReassignOracle{} 42 ro.Init(fn) 43 } 44 return &nameFinder{ro: ro} 45 } 46 47 // funcName returns the *ir.Name for the func or method 48 // corresponding to node 'n', or nil if n can't be proven 49 // to contain a function value. 50 func (nf *nameFinder) funcName(n ir.Node) *ir.Name { 51 sv := n 52 if nf.ro != nil { 53 sv = nf.ro.StaticValue(n) 54 } 55 if name := ir.StaticCalleeName(sv); name != nil { 56 return name 57 } 58 return nil 59 } 60 61 // isAllocatedMem returns true if node n corresponds to a memory 62 // allocation expression (make, new, or equivalent). 63 func (nf *nameFinder) isAllocatedMem(n ir.Node) bool { 64 sv := n 65 if nf.ro != nil { 66 sv = nf.ro.StaticValue(n) 67 } 68 switch sv.Op() { 69 case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT: 70 return true 71 } 72 return false 73 } 74 75 // constValue returns the underlying constant.Value for an AST node n 76 // if n is itself a constant value/expr, or if n is a singly assigned 77 // local containing constant expr/value (or nil not constant). 78 func (nf *nameFinder) constValue(n ir.Node) constant.Value { 79 sv := n 80 if nf.ro != nil { 81 sv = nf.ro.StaticValue(n) 82 } 83 if sv.Op() == ir.OLITERAL { 84 return sv.Val() 85 } 86 return nil 87 } 88 89 // isNil returns whether n is nil (or singly 90 // assigned local containing nil). 91 func (nf *nameFinder) isNil(n ir.Node) bool { 92 sv := n 93 if nf.ro != nil { 94 sv = nf.ro.StaticValue(n) 95 } 96 return sv.Op() == ir.ONIL 97 } 98 99 func (nf *nameFinder) staticValue(n ir.Node) ir.Node { 100 if nf.ro == nil { 101 return n 102 } 103 return nf.ro.StaticValue(n) 104 } 105 106 func (nf *nameFinder) reassigned(n *ir.Name) bool { 107 if nf.ro == nil { 108 return true 109 } 110 return nf.ro.Reassigned(n) 111 } 112 113 func (nf *nameFinder) isConcreteConvIface(n ir.Node) bool { 114 sv := n 115 if nf.ro != nil { 116 sv = nf.ro.StaticValue(n) 117 } 118 if sv.Op() != ir.OCONVIFACE { 119 return false 120 } 121 return !sv.(*ir.ConvExpr).X.Type().IsInterface() 122 } 123 124 func isSameFuncName(v1, v2 *ir.Name) bool { 125 // NB: there are a few corner cases where pointer equality 126 // doesn't work here, but this should be good enough for 127 // our purposes here. 128 return v1 == v2 129 } 130