| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package inlheur |
| |
| import ( |
| "cmd/compile/internal/ir" |
| "go/constant" |
| ) |
| |
| // nameFinder provides a set of "isXXX" query methods for clients to |
| // ask whether a given AST node corresponds to a function, a constant |
| // value, and so on. These methods use an underlying ir.ReassignOracle |
| // to return more precise results in cases where an "interesting" |
| // value is assigned to a singly-defined local temp. Example: |
| // |
| // const q = 101 |
| // fq := func() int { return q } |
| // copyOfConstant := q |
| // copyOfFunc := f |
| // interestingCall(copyOfConstant, copyOfFunc) |
| // |
| // A name finder query method invoked on the arguments being passed to |
| // "interestingCall" will be able detect that 'copyOfConstant' always |
| // evaluates to a constant (even though it is in fact a PAUTO local |
| // variable). A given nameFinder can also operate without using |
| // ir.ReassignOracle (in cases where it is not practical to look |
| // at the entire function); in such cases queries will still work |
| // for explicit constant values and functions. |
| type nameFinder struct { |
| ro *ir.ReassignOracle |
| } |
| |
| // newNameFinder returns a new nameFinder object with a reassignment |
| // oracle initialized based on the function fn, or if fn is nil, |
| // without an underlying ReassignOracle. |
| func newNameFinder(fn *ir.Func) *nameFinder { |
| var ro *ir.ReassignOracle |
| if fn != nil { |
| ro = &ir.ReassignOracle{} |
| ro.Init(fn) |
| } |
| return &nameFinder{ro: ro} |
| } |
| |
| // funcName returns the *ir.Name for the func or method |
| // corresponding to node 'n', or nil if n can't be proven |
| // to contain a function value. |
| func (nf *nameFinder) funcName(n ir.Node) *ir.Name { |
| sv := n |
| if nf.ro != nil { |
| sv = nf.ro.StaticValue(n) |
| } |
| if name := ir.StaticCalleeName(sv); name != nil { |
| return name |
| } |
| return nil |
| } |
| |
| // isAllocatedMem returns true if node n corresponds to a memory |
| // allocation expression (make, new, or equivalent). |
| func (nf *nameFinder) isAllocatedMem(n ir.Node) bool { |
| sv := n |
| if nf.ro != nil { |
| sv = nf.ro.StaticValue(n) |
| } |
| switch sv.Op() { |
| case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT: |
| return true |
| } |
| return false |
| } |
| |
| // constValue returns the underlying constant.Value for an AST node n |
| // if n is itself a constant value/expr, or if n is a singly assigned |
| // local containing constant expr/value (or nil not constant). |
| func (nf *nameFinder) constValue(n ir.Node) constant.Value { |
| sv := n |
| if nf.ro != nil { |
| sv = nf.ro.StaticValue(n) |
| } |
| if sv.Op() == ir.OLITERAL { |
| return sv.Val() |
| } |
| return nil |
| } |
| |
| // isNil returns whether n is nil (or singly |
| // assigned local containing nil). |
| func (nf *nameFinder) isNil(n ir.Node) bool { |
| sv := n |
| if nf.ro != nil { |
| sv = nf.ro.StaticValue(n) |
| } |
| return sv.Op() == ir.ONIL |
| } |
| |
| func (nf *nameFinder) staticValue(n ir.Node) ir.Node { |
| if nf.ro == nil { |
| return n |
| } |
| return nf.ro.StaticValue(n) |
| } |
| |
| func (nf *nameFinder) reassigned(n *ir.Name) bool { |
| if nf.ro == nil { |
| return true |
| } |
| return nf.ro.Reassigned(n) |
| } |
| |
| func (nf *nameFinder) isConcreteConvIface(n ir.Node) bool { |
| sv := n |
| if nf.ro != nil { |
| sv = nf.ro.StaticValue(n) |
| } |
| if sv.Op() != ir.OCONVIFACE { |
| return false |
| } |
| return !sv.(*ir.ConvExpr).X.Type().IsInterface() |
| } |
| |
| func isSameFuncName(v1, v2 *ir.Name) bool { |
| // NB: there are a few corner cases where pointer equality |
| // doesn't work here, but this should be good enough for |
| // our purposes here. |
| return v1 == v2 |
| } |