// LookupMethodFunc returns the ir.Func for an arbitrary full symbol name if
// that function exists in the set of available export data.
//
-// This allows lookup of arbitrary methods that aren't otherwise referenced by
-// the local package and thus haven't been read yet.
+// This allows lookup of arbitrary functions and methods that aren't otherwise
+// referenced by the local package and thus haven't been read yet.
//
// TODO(prattmic): Does not handle instantiation of generic types. Currently
// profiles don't contain the original type arguments, so we won't be able to
// TODO(prattmic): Hit rate of this function is usually fairly low, and errors
// are only used when debug logging is enabled. Consider constructing cheaper
// errors by default.
-func LookupMethodFunc(fullName string) (*ir.Func, error) {
+func LookupFunc(fullName string) (*ir.Func, error) {
pkgPath, symName, err := ir.ParseLinkFuncName(fullName)
if err != nil {
return nil, fmt.Errorf("error parsing symbol name %q: %v", fullName, err)
return nil, fmt.Errorf("pkg %s doesn't exist in %v", pkgPath, types.PkgMap())
}
+ // Symbol naming is ambiguous. We can't necessarily distinguish between
+ // a method and a closure. e.g., is foo.Bar.func1 a closure defined in
+ // function Bar, or a method on type Bar? Thus we must simply attempt
+ // to lookup both.
+
+ fn, err := lookupFunction(pkg, symName)
+ if err == nil {
+ return fn, nil
+ }
+
+ fn, mErr := lookupMethod(pkg, symName)
+ if mErr == nil {
+ return fn, nil
+ }
+
+ return nil, fmt.Errorf("%s is not a function (%v) or method (%v)", fullName, err, mErr)
+}
+
+func lookupFunction(pkg *types.Pkg, symName string) (*ir.Func, error) {
+ sym := pkg.Lookup(symName)
+
+ // TODO(prattmic): Enclosed functions (e.g., foo.Bar.func1) are not
+ // present in objReader, only as OCLOSURE nodes in the enclosing
+ // function.
+ pri, ok := objReader[sym]
+ if !ok {
+ return nil, fmt.Errorf("func sym %v missing objReader", sym)
+ }
+
+ name := pri.pr.objIdx(pri.idx, nil, nil, false).(*ir.Name)
+ if name.Op() != ir.ONAME || name.Class != ir.PFUNC {
+ return nil, fmt.Errorf("func sym %v refers to non-function name: %v", sym, name)
+ }
+ return name.Func, nil
+}
+
+func lookupMethod(pkg *types.Pkg, symName string) (*ir.Func, error) {
// N.B. readPackage creates a Sym for every object in the package to
// initialize objReader and importBodyReader, even if the object isn't
// read.
func unified(m posMap, noders []*noder) {
inline.InlineCall = unifiedInlineCall
typecheck.HaveInlineBody = unifiedHaveInlineBody
- pgo.LookupMethodFunc = LookupMethodFunc
+ pgo.LookupFunc = LookupFunc
data := writePkgStub(m, noders)
callerNode.OutEdges[namedEdge] = edge
}
-// LookupMethodFunc looks up a method in export data. It is expected to be
-// overridden by package noder, to break a dependency cycle.
-var LookupMethodFunc = func(fullName string) (*ir.Func, error) {
+// LookupFunc looks up a function or method in export data. It is expected to
+// be overridden by package noder, to break a dependency cycle.
+var LookupFunc = func(fullName string) (*ir.Func, error) {
base.Fatalf("pgo.LookupMethodFunc not overridden")
panic("unreachable")
}
// function may still be available from export data of
// a transitive dependency.
//
- // TODO(prattmic): Currently we only attempt to lookup
- // methods because we can only devirtualize interface
- // calls, not any function pointer. Generic types are
+ // TODO(prattmic): Parameterized types/functions are
// not supported.
//
// TODO(prattmic): This eager lookup during graph load
// devirtualization. Instantiation of generic functions
// will likely need to be done at the devirtualization
// site, if at all.
- fn, err := LookupMethodFunc(key.CalleeName)
+ fn, err := LookupFunc(key.CalleeName)
if err == nil {
if base.Debug.PGODebug >= 3 {
fmt.Printf("addIndirectEdges: %s found in export data\n", key.CalleeName)
},
// ExerciseFuncConcrete
{
- pos: "./devirt.go:178:18",
+ pos: "./devirt.go:173:36",
callee: "AddFn",
},
- // TODO(prattmic): Export data lookup for function value callees not implemented.
- //{
- // pos: "./devirt.go:179:15",
- // callee: "mult.MultFn",
- //},
+ {
+ pos: "./devirt.go:173:15",
+ callee: "mult.MultFn",
+ },
// ExerciseFuncField
{
- pos: "./devirt.go:218:13",
+ pos: "./devirt.go:207:35",
callee: "AddFn",
},
- // TODO(prattmic): Export data lookup for function value callees not implemented.
- //{
- // pos: "./devirt.go:219:19",
- // callee: "mult.MultFn",
- //},
+ {
+ pos: "./devirt.go:207:19",
+ callee: "mult.MultFn",
+ },
// ExerciseFuncClosure
// TODO(prattmic): Closure callees not implemented.
//{
- // pos: "./devirt.go:266:9",
+ // pos: "./devirt.go:249:27",
// callee: "AddClosure.func1",
//},
//{
- // pos: "./devirt.go:267:15",
+ // pos: "./devirt.go:249:15",
// callee: "mult.MultClosure.func1",
//},
}
// If they were not mutually exclusive (for example, two
// AddFunc calls), then we could not definitively select the
// correct callee.
- //
- // TODO(prattmic): Export data lookup for function value
- // callees not implemented, meaning the type is unavailable.
- //sink += int(m(42, int64(a(1, 2))))
-
- v := selectA(i)(one(i), 2)
- val += int(m(42, int64(v)))
+ val += int(m(42, int64(selectA(i)(one(i), 2))))
}
return val
}
// If they were not mutually exclusive (for example, two
// AddFunc calls), then we could not definitively select the
// correct callee.
- //
- // TODO(prattmic): Export data lookup for function value
- // callees not implemented, meaning the type is unavailable.
- //sink += int(ops.m(42, int64(ops.a(1, 2))))
-
- v := ops.a(1, 2)
- val += int(ops.m(42, int64(v)))
+ val += int(ops.m(42, int64(ops.a(1, 2))))
}
return val
}
// If they were not mutually exclusive (for example, two
// AddFunc calls), then we could not definitively select the
// correct callee.
- //
- // TODO(prattmic): Export data lookup for function value
- // callees not implemented, meaning the type is unavailable.
- //sink += int(m(42, int64(a(1, 2))))
-
- v := a(1, 2)
- val += int(m(42, int64(v)))
+ val += int(m(42, int64(a(1, 2))))
}
return val
}
type MultFunc func(int64, int64) int64
func MultFn(a, b int64) int64 {
+ for i := 0; i < 1000; i++ {
+ sink++
+ }
return a * b
}
func NegMultFn(a, b int64) int64 {
+ for i := 0; i < 1000; i++ {
+ sink++
+ }
return -1 * a * b
}
// Explicit closure to differentiate from AddClosure.
c := 1
return func(a, b int64) int64 {
+ for i := 0; i < 1000; i++ {
+ sink++
+ }
return a * b * int64(c)
}
}
func NegMultClosure() MultFunc {
c := 1
return func(a, b int64) int64 {
+ for i := 0; i < 1000; i++ {
+ sink++
+ }
return -1 * a * b * int64(c)
}
}