]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/compile/internal/noder/unified.go
cmd/compile: support lookup of functions from export data
[gostls13.git] / src / cmd / compile / internal / noder / unified.go
index 0afa505550c8172ef6d9e6fa0d73e5ddc54a25aa..a803e53502a965d5375c3cd9ac24153a22d0b9fd 100644 (file)
@@ -6,7 +6,6 @@ package noder
 
 import (
        "fmt"
-       "internal/goversion"
        "internal/pkgbits"
        "io"
        "runtime"
@@ -16,6 +15,7 @@ import (
        "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"
@@ -27,6 +27,102 @@ import (
 // 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).
 //
@@ -71,47 +167,29 @@ var localPkgReader *pkgReader
 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)
                        }
@@ -120,11 +198,9 @@ func unified(m posMap, noders []*noder) {
 
        // 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
                }
        }
 
@@ -138,7 +214,7 @@ func unified(m posMap, noders []*noder) {
 // 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 {
@@ -175,7 +251,7 @@ func readBodies(target *ir.Package, duringInlining bool) {
                                if duringInlining && canSkipNonGenericMethod {
                                        inlDecls = append(inlDecls, fn)
                                } else {
-                                       target.Decls = append(target.Decls, fn)
+                                       target.Funcs = append(target.Funcs, fn)
                                }
                        }
 
@@ -208,7 +284,7 @@ func readBodies(target *ir.Package, duringInlining bool) {
                base.Flag.LowerM = oldLowerM
 
                for _, fn := range inlDecls {
-                       fn.(*ir.Func).Body = nil // free memory
+                       fn.Body = nil // free memory
                }
        }
 }
@@ -331,7 +407,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
 
                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
                }