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
5package inlheur
6
7import (
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.
31type 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.
38func 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.
50func (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).
63func (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).
78func (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).
91func (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
99func (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
106func (nf *nameFinder) reassigned(n *ir.Name) bool {
107	if nf.ro == nil {
108		return true
109	}
110	return nf.ro.Reassigned(n)
111}
112
113func (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
124func 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