diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/linker.go')
-rw-r--r-- | src/cmd/compile/internal/noder/linker.go | 107 |
1 files changed, 85 insertions, 22 deletions
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go index a58b9b930c..0f39fdec05 100644 --- a/src/cmd/compile/internal/noder/linker.go +++ b/src/cmd/compile/internal/noder/linker.go @@ -1,5 +1,3 @@ -// UNREVIEWED - // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -34,13 +32,20 @@ import ( // low-level linking details can be moved there, but the logic for // handling extension data needs to stay in the compiler. +// A linker combines a package's stub export data with any referenced +// elements from imported packages into a single, self-contained +// export data file. type linker struct { pw pkgbits.PkgEncoder - pkgs map[string]pkgbits.Index - decls map[*types.Sym]pkgbits.Index + pkgs map[string]pkgbits.Index + decls map[*types.Sym]pkgbits.Index + bodies map[*types.Sym]pkgbits.Index } +// relocAll ensures that all elements specified by pr and relocs are +// copied into the output export data file, and returns the +// corresponding indices in the output. func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt { res := make([]pkgbits.RelocEnt, len(relocs)) for i, rent := range relocs { @@ -50,6 +55,8 @@ func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.Re return res } +// relocIdx ensures a single element is copied into the output export +// data file, and returns the corresponding index in the output. func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index { assert(pr != nil) @@ -85,10 +92,19 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) return newidx } +// relocString copies the specified string from pr into the output +// export data file, deduplicating it against other strings. func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { return l.pw.StringIdx(pr.StringIdx(idx)) } +// relocPkg copies the specified package from pr into the output +// export data file, rewriting its import path to match how it was +// imported. +// +// TODO(mdempsky): Since CL 391014, we already have the compilation +// unit's import path, so there should be no need to rewrite packages +// anymore. func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { path := pr.PeekPkgPath(idx) @@ -114,6 +130,9 @@ func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { return w.Flush() } +// relocObj copies the specified object from pr into the output export +// data file, rewriting its compiler-private extension data (e.g., +// adding inlining cost and escape analysis results for functions). func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { path, name, tag := pr.PeekObj(idx) sym := types.NewPkg(path, "").Lookup(name) @@ -152,21 +171,12 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { l.relocCommon(pr, &wname, pkgbits.RelocName, idx) l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx) - var obj *ir.Name - if sym.Pkg == types.LocalPkg { - var ok bool - obj, ok = sym.Def.(*ir.Name) - - // Generic types and functions and declared constraint types won't - // have definitions. - // For now, just generically copy their extension data. - // TODO(mdempsky): Restore assertion. - if !ok && false { - base.Fatalf("missing definition for %v", sym) - } - } + // Generic types and functions won't have definitions, and imported + // objects may not either. + obj, _ := sym.Def.(*ir.Name) + local := sym.Pkg == types.LocalPkg - if obj != nil { + if local && obj != nil { wext.Sync(pkgbits.SyncObject1) switch tag { case pkgbits.ObjFunc: @@ -181,9 +191,66 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx) } + // Check if we need to export the inline bodies for functions and + // methods. + if obj != nil { + if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC { + l.exportBody(obj, local) + } + + if obj.Op() == ir.OTYPE { + if typ := obj.Type(); !typ.IsInterface() { + for _, method := range typ.Methods().Slice() { + l.exportBody(method.Nname.(*ir.Name), local) + } + } + } + } + return w.Idx } +// exportBody exports the given function or method's body, if +// appropriate. local indicates whether it's a local function or +// method available on a locally declared type. (Due to cross-package +// type aliases, a method may be imported, but still available on a +// locally declared type.) +func (l *linker) exportBody(obj *ir.Name, local bool) { + assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC) + + fn := obj.Func + if fn.Inl == nil { + return // not inlinable anyway + } + + // As a simple heuristic, if the function was declared in this + // package or we inlined it somewhere in this package, then we'll + // (re)export the function body. This isn't perfect, but seems + // reasonable in practice. In particular, it has the nice property + // that in the worst case, adding a blank import ensures the + // function body is available for inlining. + // + // TODO(mdempsky): Reimplement the reachable method crawling logic + // from typecheck/crawler.go. + exportBody := local || fn.Inl.Body != nil + if !exportBody { + return + } + + sym := obj.Sym() + if _, ok := l.bodies[sym]; ok { + // Due to type aliases, we might visit methods multiple times. + base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj) + return + } + + pri, ok := bodyReaderFor(fn) + assert(ok) + l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx) +} + +// relocCommon copies the specified element from pr into w, +// recursively relocating any referenced elements as well. func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) { r := pr.NewDecoderRaw(k, idx) w.Relocs = l.relocAll(pr, r.Relocs) @@ -220,10 +287,6 @@ func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) { if inl := name.Func.Inl; w.Bool(inl != nil) { w.Len(int(inl.Cost)) w.Bool(inl.CanDelayResults) - - pri, ok := bodyReader[name.Func] - assert(ok) - w.Reloc(pkgbits.RelocBody, l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)) } w.Sync(pkgbits.SyncEOF) |