mirror of https://github.com/golang/go.git
338 lines
9.6 KiB
Go
338 lines
9.6 KiB
Go
// 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.
|
|
|
|
package noder
|
|
|
|
import (
|
|
"internal/pkgbits"
|
|
"io"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/reflectdata"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/goobj"
|
|
"cmd/internal/obj"
|
|
)
|
|
|
|
// This file implements the unified IR linker, which combines the
|
|
// local package's stub data with imported package data to produce a
|
|
// complete export data file. It also rewrites the compiler's
|
|
// extension data sections based on the results of compilation (e.g.,
|
|
// the function inlining cost and linker symbol index assignments).
|
|
//
|
|
// TODO(mdempsky): Using the name "linker" here is confusing, because
|
|
// readers are likely to mistake references to it for cmd/link. But
|
|
// there's a shortage of good names for "something that combines
|
|
// multiple parts into a cohesive whole"... e.g., "assembler" and
|
|
// "compiler" are also already taken.
|
|
|
|
// TODO(mdempsky): Should linker go into pkgbits? Probably the
|
|
// 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
|
|
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 {
|
|
rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx)
|
|
res[i] = rent
|
|
}
|
|
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)
|
|
|
|
absIdx := pr.AbsIdx(k, idx)
|
|
|
|
if newidx := pr.newindex[absIdx]; newidx != 0 {
|
|
return ^newidx
|
|
}
|
|
|
|
var newidx pkgbits.Index
|
|
switch k {
|
|
case pkgbits.RelocString:
|
|
newidx = l.relocString(pr, idx)
|
|
case pkgbits.RelocPkg:
|
|
newidx = l.relocPkg(pr, idx)
|
|
case pkgbits.RelocObj:
|
|
newidx = l.relocObj(pr, idx)
|
|
|
|
default:
|
|
// Generic relocations.
|
|
//
|
|
// TODO(mdempsky): Deduplicate more sections? In fact, I think
|
|
// every section could be deduplicated. This would also be easier
|
|
// if we do external relocations.
|
|
|
|
w := l.pw.NewEncoderRaw(k)
|
|
l.relocCommon(pr, &w, k, idx)
|
|
newidx = w.Idx
|
|
}
|
|
|
|
pr.newindex[absIdx] = ^newidx
|
|
|
|
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)
|
|
|
|
if newidx, ok := l.pkgs[path]; ok {
|
|
return newidx
|
|
}
|
|
|
|
r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef)
|
|
w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
|
|
l.pkgs[path] = w.Idx
|
|
|
|
// TODO(mdempsky): We end up leaving an empty string reference here
|
|
// from when the package was originally written as "". Probably not
|
|
// a big deal, but a little annoying. Maybe relocating
|
|
// cross-references in place is the way to go after all.
|
|
w.Relocs = l.relocAll(pr, r.Relocs)
|
|
|
|
_ = r.String() // original path
|
|
w.String(path)
|
|
|
|
io.Copy(&w.Data, &r.Data)
|
|
|
|
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)
|
|
|
|
if newidx, ok := l.decls[sym]; ok {
|
|
return newidx
|
|
}
|
|
|
|
if tag == pkgbits.ObjStub && path != "builtin" && path != "unsafe" {
|
|
pri, ok := objReader[sym]
|
|
if !ok {
|
|
base.Fatalf("missing reader for %q.%v", path, name)
|
|
}
|
|
assert(ok)
|
|
|
|
pr = pri.pr
|
|
idx = pri.idx
|
|
|
|
path2, name2, tag2 := pr.PeekObj(idx)
|
|
sym2 := types.NewPkg(path2, "").Lookup(name2)
|
|
assert(sym == sym2)
|
|
assert(tag2 != pkgbits.ObjStub)
|
|
}
|
|
|
|
w := l.pw.NewEncoderRaw(pkgbits.RelocObj)
|
|
wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt)
|
|
wname := l.pw.NewEncoderRaw(pkgbits.RelocName)
|
|
wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict)
|
|
|
|
l.decls[sym] = w.Idx
|
|
assert(wext.Idx == w.Idx)
|
|
assert(wname.Idx == w.Idx)
|
|
assert(wdict.Idx == w.Idx)
|
|
|
|
l.relocCommon(pr, &w, pkgbits.RelocObj, idx)
|
|
l.relocCommon(pr, &wname, pkgbits.RelocName, idx)
|
|
l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx)
|
|
|
|
// 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 local && obj != nil {
|
|
wext.Sync(pkgbits.SyncObject1)
|
|
switch tag {
|
|
case pkgbits.ObjFunc:
|
|
l.relocFuncExt(&wext, obj)
|
|
case pkgbits.ObjType:
|
|
l.relocTypeExt(&wext, obj)
|
|
case pkgbits.ObjVar:
|
|
l.relocVarExt(&wext, obj)
|
|
}
|
|
wext.Flush()
|
|
} else {
|
|
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 && !obj.Alias() {
|
|
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)
|
|
io.Copy(&w.Data, &r.Data)
|
|
w.Flush()
|
|
}
|
|
|
|
func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) {
|
|
w.Sync(pkgbits.SyncPragma)
|
|
w.Int(int(pragma))
|
|
}
|
|
|
|
func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) {
|
|
w.Sync(pkgbits.SyncFuncExt)
|
|
|
|
l.pragmaFlag(w, name.Func.Pragma)
|
|
l.linkname(w, name)
|
|
|
|
// Relocated extension data.
|
|
w.Bool(true)
|
|
|
|
// Record definition ABI so cross-ABI calls can be direct.
|
|
// This is important for the performance of calling some
|
|
// common functions implemented in assembly (e.g., bytealg).
|
|
w.Uint64(uint64(name.Func.ABI))
|
|
|
|
// Escape analysis.
|
|
for _, fs := range &types.RecvsParams {
|
|
for _, f := range fs(name.Type()).FieldSlice() {
|
|
w.String(f.Note)
|
|
}
|
|
}
|
|
|
|
if inl := name.Func.Inl; w.Bool(inl != nil) {
|
|
w.Len(int(inl.Cost))
|
|
w.Bool(inl.CanDelayResults)
|
|
}
|
|
|
|
w.Sync(pkgbits.SyncEOF)
|
|
}
|
|
|
|
func (l *linker) relocTypeExt(w *pkgbits.Encoder, name *ir.Name) {
|
|
w.Sync(pkgbits.SyncTypeExt)
|
|
|
|
typ := name.Type()
|
|
|
|
l.pragmaFlag(w, name.Pragma())
|
|
|
|
// For type T, export the index of type descriptor symbols of T and *T.
|
|
l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
|
|
l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
|
|
|
|
if typ.Kind() != types.TINTER {
|
|
for _, method := range typ.Methods().Slice() {
|
|
l.relocFuncExt(w, method.Nname.(*ir.Name))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) {
|
|
w.Sync(pkgbits.SyncVarExt)
|
|
l.linkname(w, name)
|
|
}
|
|
|
|
func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) {
|
|
w.Sync(pkgbits.SyncLinkname)
|
|
|
|
linkname := name.Sym().Linkname
|
|
if !l.lsymIdx(w, linkname, name.Linksym()) {
|
|
w.String(linkname)
|
|
}
|
|
}
|
|
|
|
func (l *linker) lsymIdx(w *pkgbits.Encoder, linkname string, lsym *obj.LSym) bool {
|
|
if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
|
|
w.Int64(-1)
|
|
return false
|
|
}
|
|
|
|
// For a defined symbol, export its index.
|
|
// For re-exporting an imported symbol, pass its index through.
|
|
w.Int64(int64(lsym.SymIdx))
|
|
return true
|
|
}
|