go/src/cmd/compile/internal/noder/linker.go
Cuong Manh Le 8f95eaddd3 [dev.typeparams] cmd/compile: fix missing sync implicit types
CL 328051 introduced new syncImplicitTypes, but forgot to add a sync
after syncAddBody in linker.relocFuncExt, cause the compiler crashes
when reading in package data.

Adding missing w.sync(syncImplicitTypes) call fixes this.

While at it, also run go generate to update code generated for
syncImplicitTypes, which is also missed in CL 328051.

Change-Id: Ic65092f69f8d8e63de15989c7f15b6e5633d8f9e
Reviewed-on: https://go-review.googlesource.com/c/go/+/328054
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-06-16 16:30:37 +00:00

297 lines
6.9 KiB
Go

// 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)
bside := l.pw.newEncoderRaw(relocObjExt)
assert(bside.idx == w.idx)
l.decls[sym] = w.idx
l.relocCommon(pr, &w, relocObj, idx)
var obj *ir.Name
if path == "" {
var ok bool
obj, ok = sym.Def.(*ir.Name)
// Generic types and functions won't have definitions.
// For now, just generically copy their extension data.
if !ok && base.Flag.G == 0 {
base.Fatalf("missing definition for %v", sym)
}
}
if obj != nil {
bside.sync(syncObject1)
switch tag {
case objFunc:
l.relocFuncExt(&bside, obj)
case objType:
l.relocTypeExt(&bside, obj)
case objVar:
l.relocVarExt(&bside, obj)
}
bside.flush()
} else {
l.relocCommon(pr, &bside, 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.sync(syncAddBody)
w.sync(syncImplicitTypes)
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, []int) {
r := pr.newDecoder(relocObj, idx, syncObject1)
r.sync(syncSym)
r.sync(syncPkg)
path := pr.peekPkgPath(r.reloc(relocPkg))
name := r.string()
assert(name != "")
r.sync(syncTypeParamBounds)
r.len() // implicits
bounds := make([]int, r.len())
for i := range bounds {
r.sync(syncType)
bounds[i] = r.reloc(relocType)
}
tag := codeObj(r.code(syncCodeObj))
return path, name, tag, bounds
}