Skip to content

Commit 42bd21b

Browse files
committed
cmd/compile: support lookup of functions from export data
As of CL 539699, PGO-based devirtualization supports devirtualization of function values in addition to interface method calls. As with CL 497175, we need to explicitly look up functions from export data that may not be imported already. Symbol naming is ambiguous (`foo.Bar.func1` could be a closure or a method), so we simply attempt to do both types of lookup. That said, closures are defined in export data only as OCLOSURE nodes in the enclosing function, which this CL does not yet attempt to expand. For #61577. Change-Id: Ic7205b046218a4dfb8c4162ece3620ed1c3cb40a Reviewed-on: https://go-review.googlesource.com/c/go/+/540258 Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
1 parent fb6ff1e commit 42bd21b

File tree

6 files changed

+73
-46
lines changed

6 files changed

+73
-46
lines changed

src/cmd/compile/internal/noder/unified.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ var localPkgReader *pkgReader
3030
// LookupMethodFunc returns the ir.Func for an arbitrary full symbol name if
3131
// that function exists in the set of available export data.
3232
//
33-
// This allows lookup of arbitrary methods that aren't otherwise referenced by
34-
// the local package and thus haven't been read yet.
33+
// This allows lookup of arbitrary functions and methods that aren't otherwise
34+
// referenced by the local package and thus haven't been read yet.
3535
//
3636
// TODO(prattmic): Does not handle instantiation of generic types. Currently
3737
// profiles don't contain the original type arguments, so we won't be able to
@@ -40,7 +40,7 @@ var localPkgReader *pkgReader
4040
// TODO(prattmic): Hit rate of this function is usually fairly low, and errors
4141
// are only used when debug logging is enabled. Consider constructing cheaper
4242
// errors by default.
43-
func LookupMethodFunc(fullName string) (*ir.Func, error) {
43+
func LookupFunc(fullName string) (*ir.Func, error) {
4444
pkgPath, symName, err := ir.ParseLinkFuncName(fullName)
4545
if err != nil {
4646
return nil, fmt.Errorf("error parsing symbol name %q: %v", fullName, err)
@@ -51,6 +51,43 @@ func LookupMethodFunc(fullName string) (*ir.Func, error) {
5151
return nil, fmt.Errorf("pkg %s doesn't exist in %v", pkgPath, types.PkgMap())
5252
}
5353

54+
// Symbol naming is ambiguous. We can't necessarily distinguish between
55+
// a method and a closure. e.g., is foo.Bar.func1 a closure defined in
56+
// function Bar, or a method on type Bar? Thus we must simply attempt
57+
// to lookup both.
58+
59+
fn, err := lookupFunction(pkg, symName)
60+
if err == nil {
61+
return fn, nil
62+
}
63+
64+
fn, mErr := lookupMethod(pkg, symName)
65+
if mErr == nil {
66+
return fn, nil
67+
}
68+
69+
return nil, fmt.Errorf("%s is not a function (%v) or method (%v)", fullName, err, mErr)
70+
}
71+
72+
func lookupFunction(pkg *types.Pkg, symName string) (*ir.Func, error) {
73+
sym := pkg.Lookup(symName)
74+
75+
// TODO(prattmic): Enclosed functions (e.g., foo.Bar.func1) are not
76+
// present in objReader, only as OCLOSURE nodes in the enclosing
77+
// function.
78+
pri, ok := objReader[sym]
79+
if !ok {
80+
return nil, fmt.Errorf("func sym %v missing objReader", sym)
81+
}
82+
83+
name := pri.pr.objIdx(pri.idx, nil, nil, false).(*ir.Name)
84+
if name.Op() != ir.ONAME || name.Class != ir.PFUNC {
85+
return nil, fmt.Errorf("func sym %v refers to non-function name: %v", sym, name)
86+
}
87+
return name.Func, nil
88+
}
89+
90+
func lookupMethod(pkg *types.Pkg, symName string) (*ir.Func, error) {
5491
// N.B. readPackage creates a Sym for every object in the package to
5592
// initialize objReader and importBodyReader, even if the object isn't
5693
// read.
@@ -130,7 +167,7 @@ func LookupMethodFunc(fullName string) (*ir.Func, error) {
130167
func unified(m posMap, noders []*noder) {
131168
inline.InlineCall = unifiedInlineCall
132169
typecheck.HaveInlineBody = unifiedHaveInlineBody
133-
pgo.LookupMethodFunc = LookupMethodFunc
170+
pgo.LookupFunc = LookupFunc
134171

135172
data := writePkgStub(m, noders)
136173

src/cmd/compile/internal/pgo/irgraph.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,9 @@ func addIREdge(callerNode *IRNode, callerName string, call ir.Node, callee *ir.F
366366
callerNode.OutEdges[namedEdge] = edge
367367
}
368368

369-
// LookupMethodFunc looks up a method in export data. It is expected to be
370-
// overridden by package noder, to break a dependency cycle.
371-
var LookupMethodFunc = func(fullName string) (*ir.Func, error) {
369+
// LookupFunc looks up a function or method in export data. It is expected to
370+
// be overridden by package noder, to break a dependency cycle.
371+
var LookupFunc = func(fullName string) (*ir.Func, error) {
372372
base.Fatalf("pgo.LookupMethodFunc not overridden")
373373
panic("unreachable")
374374
}
@@ -425,9 +425,7 @@ func addIndirectEdges(g *IRGraph, namedEdgeMap NamedEdgeMap) {
425425
// function may still be available from export data of
426426
// a transitive dependency.
427427
//
428-
// TODO(prattmic): Currently we only attempt to lookup
429-
// methods because we can only devirtualize interface
430-
// calls, not any function pointer. Generic types are
428+
// TODO(prattmic): Parameterized types/functions are
431429
// not supported.
432430
//
433431
// TODO(prattmic): This eager lookup during graph load
@@ -437,7 +435,7 @@ func addIndirectEdges(g *IRGraph, namedEdgeMap NamedEdgeMap) {
437435
// devirtualization. Instantiation of generic functions
438436
// will likely need to be done at the devirtualization
439437
// site, if at all.
440-
fn, err := LookupMethodFunc(key.CalleeName)
438+
fn, err := LookupFunc(key.CalleeName)
441439
if err == nil {
442440
if base.Debug.PGODebug >= 3 {
443441
fmt.Printf("addIndirectEdges: %s found in export data\n", key.CalleeName)

src/cmd/compile/internal/test/pgo_devirtualize_test.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,32 +77,30 @@ go 1.19
7777
},
7878
// ExerciseFuncConcrete
7979
{
80-
pos: "./devirt.go:178:18",
80+
pos: "./devirt.go:173:36",
8181
callee: "AddFn",
8282
},
83-
// TODO(prattmic): Export data lookup for function value callees not implemented.
84-
//{
85-
// pos: "./devirt.go:179:15",
86-
// callee: "mult.MultFn",
87-
//},
83+
{
84+
pos: "./devirt.go:173:15",
85+
callee: "mult.MultFn",
86+
},
8887
// ExerciseFuncField
8988
{
90-
pos: "./devirt.go:218:13",
89+
pos: "./devirt.go:207:35",
9190
callee: "AddFn",
9291
},
93-
// TODO(prattmic): Export data lookup for function value callees not implemented.
94-
//{
95-
// pos: "./devirt.go:219:19",
96-
// callee: "mult.MultFn",
97-
//},
92+
{
93+
pos: "./devirt.go:207:19",
94+
callee: "mult.MultFn",
95+
},
9896
// ExerciseFuncClosure
9997
// TODO(prattmic): Closure callees not implemented.
10098
//{
101-
// pos: "./devirt.go:266:9",
99+
// pos: "./devirt.go:249:27",
102100
// callee: "AddClosure.func1",
103101
//},
104102
//{
105-
// pos: "./devirt.go:267:15",
103+
// pos: "./devirt.go:249:15",
106104
// callee: "mult.MultClosure.func1",
107105
//},
108106
}

src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,7 @@ func ExerciseFuncConcrete(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
170170
// If they were not mutually exclusive (for example, two
171171
// AddFunc calls), then we could not definitively select the
172172
// correct callee.
173-
//
174-
// TODO(prattmic): Export data lookup for function value
175-
// callees not implemented, meaning the type is unavailable.
176-
//sink += int(m(42, int64(a(1, 2))))
177-
178-
v := selectA(i)(one(i), 2)
179-
val += int(m(42, int64(v)))
173+
val += int(m(42, int64(selectA(i)(one(i), 2))))
180174
}
181175
return val
182176
}
@@ -210,13 +204,7 @@ func ExerciseFuncField(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
210204
// If they were not mutually exclusive (for example, two
211205
// AddFunc calls), then we could not definitively select the
212206
// correct callee.
213-
//
214-
// TODO(prattmic): Export data lookup for function value
215-
// callees not implemented, meaning the type is unavailable.
216-
//sink += int(ops.m(42, int64(ops.a(1, 2))))
217-
218-
v := ops.a(1, 2)
219-
val += int(ops.m(42, int64(v)))
207+
val += int(ops.m(42, int64(ops.a(1, 2))))
220208
}
221209
return val
222210
}
@@ -258,13 +246,7 @@ func ExerciseFuncClosure(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
258246
// If they were not mutually exclusive (for example, two
259247
// AddFunc calls), then we could not definitively select the
260248
// correct callee.
261-
//
262-
// TODO(prattmic): Export data lookup for function value
263-
// callees not implemented, meaning the type is unavailable.
264-
//sink += int(m(42, int64(a(1, 2))))
265-
266-
v := a(1, 2)
267-
val += int(m(42, int64(v)))
249+
val += int(m(42, int64(a(1, 2))))
268250
}
269251
return val
270252
}
Binary file not shown.

src/cmd/compile/internal/test/testdata/pgo/devirtualize/mult.pkg/mult.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ func (NegMult) Multiply(a, b int) int {
3535
type MultFunc func(int64, int64) int64
3636

3737
func MultFn(a, b int64) int64 {
38+
for i := 0; i < 1000; i++ {
39+
sink++
40+
}
3841
return a * b
3942
}
4043

4144
func NegMultFn(a, b int64) int64 {
45+
for i := 0; i < 1000; i++ {
46+
sink++
47+
}
4248
return -1 * a * b
4349
}
4450

@@ -47,6 +53,9 @@ func MultClosure() MultFunc {
4753
// Explicit closure to differentiate from AddClosure.
4854
c := 1
4955
return func(a, b int64) int64 {
56+
for i := 0; i < 1000; i++ {
57+
sink++
58+
}
5059
return a * b * int64(c)
5160
}
5261
}
@@ -55,6 +64,9 @@ func MultClosure() MultFunc {
5564
func NegMultClosure() MultFunc {
5665
c := 1
5766
return func(a, b int64) int64 {
67+
for i := 0; i < 1000; i++ {
68+
sink++
69+
}
5870
return -1 * a * b * int64(c)
5971
}
6072
}

0 commit comments

Comments
 (0)