import (
"fmt"
- "internal/goversion"
"internal/pkgbits"
"io"
"runtime"
"cmd/compile/internal/base"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/pgo"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/compile/internal/types2"
// later.
var localPkgReader *pkgReader
+// 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 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
+// create the runtime dictionaries.
+//
+// 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 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)
+ }
+
+ pkg, ok := types.PkgMap()[pkgPath]
+ if !ok {
+ 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.
+ //
+ // However, objReader is only initialized for top-level objects, so we
+ // must first lookup the type and use that to find the method rather
+ // than looking for the method directly.
+ typ, meth, err := ir.LookupMethodSelector(pkg, symName)
+ if err != nil {
+ return nil, fmt.Errorf("error looking up method symbol %q: %v", symName, err)
+ }
+
+ pri, ok := objReader[typ]
+ if !ok {
+ return nil, fmt.Errorf("type sym %v missing objReader", typ)
+ }
+
+ name := pri.pr.objIdx(pri.idx, nil, nil, false).(*ir.Name)
+ if name.Op() != ir.OTYPE {
+ return nil, fmt.Errorf("type sym %v refers to non-type name: %v", typ, name)
+ }
+ if name.Alias() {
+ return nil, fmt.Errorf("type sym %v refers to alias", typ)
+ }
+
+ for _, m := range name.Type().Methods() {
+ if m.Sym == meth {
+ fn := m.Nname.(*ir.Name).Func
+ return fn, nil
+ }
+ }
+
+ return nil, fmt.Errorf("method %s missing from method set of %v", symName, typ)
+}
+
// unified constructs the local package's Internal Representation (IR)
// from its syntax tree (AST).
//
func unified(m posMap, noders []*noder) {
inline.InlineCall = unifiedInlineCall
typecheck.HaveInlineBody = unifiedHaveInlineBody
+ pgo.LookupFunc = LookupFunc
data := writePkgStub(m, noders)
- // We already passed base.Flag.Lang to types2 to handle validating
- // the user's source code. Bump it up now to the current version and
- // re-parse, so typecheck doesn't complain if we construct IR that
- // utilizes newer Go features.
- base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version)
- types.ParseLangFlag()
-
target := typecheck.Target
- typecheck.TypecheckAllowed = true
-
localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
readPackage(localPkgReader, types.LocalPkg, true)
r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
r.pkgInit(types.LocalPkg, target)
- // Type-check any top-level assignments. We ignore non-assignments
- // here because other declarations are typechecked as they're
- // constructed.
- for i, ndecls := 0, len(target.Decls); i < ndecls; i++ {
- switch n := target.Decls[i]; n.Op() {
- case ir.OAS, ir.OAS2:
- target.Decls[i] = typecheck.Stmt(n)
- }
- }
-
readBodies(target, false)
// Check that nothing snuck past typechecking.
- for _, n := range target.Decls {
- if n.Typecheck() == 0 {
- base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
+ for _, fn := range target.Funcs {
+ if fn.Typecheck() == 0 {
+ base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn)
}
// For functions, check that at least their first statement (if
// any) was typechecked too.
- if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 {
+ if len(fn.Body) != 0 {
if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
}
// For functions originally came from package runtime,
// mark as norace to prevent instrumenting, see issue #60439.
- for _, n := range target.Decls {
- if fn, ok := n.(*ir.Func); ok {
- if !base.Flag.CompilingRuntime && types.IsRuntimePkg(fn.Sym().Pkg) {
- fn.Pragma |= ir.Norace
- }
+ for _, fn := range target.Funcs {
+ if !base.Flag.CompilingRuntime && types.RuntimeSymName(fn.Sym()) != "" {
+ fn.Pragma |= ir.Norace
}
}
// necessary on instantiations of imported generic functions, so their
// inlining costs can be computed.
func readBodies(target *ir.Package, duringInlining bool) {
- var inlDecls []ir.Node
+ var inlDecls []*ir.Func
// Don't use range--bodyIdx can add closures to todoBodies.
for {
if duringInlining && canSkipNonGenericMethod {
inlDecls = append(inlDecls, fn)
} else {
- target.Decls = append(target.Decls, fn)
+ target.Funcs = append(target.Funcs, fn)
}
}
base.Flag.LowerM = oldLowerM
for _, fn := range inlDecls {
- fn.(*ir.Func).Body = nil // free memory
+ fn.Body = nil // free memory
}
}
}
if r.Bool() {
sym := importpkg.Lookup(".inittask")
- task := ir.NewNameAt(src.NoXPos, sym)
+ task := ir.NewNameAt(src.NoXPos, sym, nil)
task.Class = ir.PEXTERN
sym.Def = task
}