| // 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. | |
| package noder | |
| import ( | |
| "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. | |
| type linker struct { | |
| pw pkgEncoder | |
| pkgs map[string]int | |
| decls map[*types.Sym]int | |
| } | |
| func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt { | |
| res := make([]relocEnt, len(relocs)) | |
| for i, rent := range relocs { | |
| rent.idx = l.relocIdx(pr, rent.kind, rent.idx) | |
| res[i] = rent | |
| } | |
| return res | |
| } | |
| func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int { | |
| assert(pr != nil) | |
| absIdx := pr.absIdx(k, idx) | |
| if newidx := pr.newindex[absIdx]; newidx != 0 { | |
| return ^newidx | |
| } | |
| var newidx int | |
| switch k { | |
| case relocString: | |
| newidx = l.relocString(pr, idx) | |
| case relocPkg: | |
| newidx = l.relocPkg(pr, idx) | |
| case 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 | |
| } | |
| func (l *linker) relocString(pr *pkgReader, idx int) int { | |
| return l.pw.stringIdx(pr.stringIdx(idx)) | |
| } | |
| func (l *linker) relocPkg(pr *pkgReader, idx int) int { | |
| path := pr.peekPkgPath(idx) | |
| if newidx, ok := l.pkgs[path]; ok { | |
| return newidx | |
| } | |
| r := pr.newDecoder(relocPkg, idx, syncPkgDef) | |
| w := l.pw.newEncoder(relocPkg, 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() | |
| } | |
| func (l *linker) relocObj(pr *pkgReader, idx int) int { | |
| path, name, tag := pr.peekObj(idx) | |
| sym := types.NewPkg(path, "").Lookup(name) | |
| if newidx, ok := l.decls[sym]; ok { | |
| return newidx | |
| } | |
| if tag == 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 != objStub) | |
| } | |
| w := l.pw.newEncoderRaw(relocObj) | |
| wext := l.pw.newEncoderRaw(relocObjExt) | |
| wname := l.pw.newEncoderRaw(relocName) | |
| wdict := l.pw.newEncoderRaw(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, relocObj, idx) | |
| l.relocCommon(pr, &wname, relocName, idx) | |
| l.relocCommon(pr, &wdict, relocObjDict, idx) | |
| var obj *ir.Name | |
| if path == "" { | |
| 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) | |
| } | |
| } | |
| if obj != nil { | |
| wext.sync(syncObject1) | |
| switch tag { | |
| case objFunc: | |
| l.relocFuncExt(&wext, obj) | |
| case objType: | |
| l.relocTypeExt(&wext, obj) | |
| case objVar: | |
| l.relocVarExt(&wext, obj) | |
| } | |
| wext.flush() | |
| } else { | |
| l.relocCommon(pr, &wext, relocObjExt, idx) | |
| } | |
| return w.idx | |
| } | |
| func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) { | |
| 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 *encoder, pragma ir.PragmaFlag) { | |
| w.sync(syncPragma) | |
| w.int(int(pragma)) | |
| } | |
| func (l *linker) relocFuncExt(w *encoder, name *ir.Name) { | |
| w.sync(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) | |
| pri, ok := bodyReader[name.Func] | |
| assert(ok) | |
| w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx)) | |
| } | |
| w.sync(syncEOF) | |
| } | |
| func (l *linker) relocTypeExt(w *encoder, name *ir.Name) { | |
| w.sync(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 *encoder, name *ir.Name) { | |
| w.sync(syncVarExt) | |
| l.linkname(w, name) | |
| } | |
| func (l *linker) linkname(w *encoder, name *ir.Name) { | |
| w.sync(syncLinkname) | |
| linkname := name.Sym().Linkname | |
| if !l.lsymIdx(w, linkname, name.Linksym()) { | |
| w.string(linkname) | |
| } | |
| } | |
| func (l *linker) lsymIdx(w *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 | |
| } | |
| // @@@ Helpers | |
| // TODO(mdempsky): These should probably be removed. I think they're a | |
| // smell that the export data format is not yet quite right. | |
| func (pr *pkgDecoder) peekPkgPath(idx int) string { | |
| r := pr.newDecoder(relocPkg, idx, syncPkgDef) | |
| path := r.string() | |
| if path == "" { | |
| path = pr.pkgPath | |
| } | |
| return path | |
| } | |
| func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj) { | |
| r := pr.newDecoder(relocName, idx, syncObject1) | |
| r.sync(syncSym) | |
| r.sync(syncPkg) | |
| path := pr.peekPkgPath(r.reloc(relocPkg)) | |
| name := r.string() | |
| assert(name != "") | |
| tag := codeObj(r.code(syncCodeObj)) | |
| return path, name, tag | |
| } |