mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
We already use -lang=go1.18 to control the types2 type checker behavior. This CL does the same for the parser. Also, disable an assertion in the unified IR linker that depended on the -G flag. This assertion was more useful during initial bootstrapping of that code, but it's less critical now. With these two changes, "GOEXPERIMENT=unified ./make.bash" is enough to get a fully functional generics-enabled toolchain. There's no need to continue specifying custom compiler flags later on. Change-Id: I7766381926f3bb17eee2e5fcc182a38a39e937e1 Reviewed-on: https://go-review.googlesource.com/c/go/+/332373 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Matthew Dempsky <mdempsky@google.com> Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
311 lines
7.3 KiB
Go
311 lines
7.3 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)
|
|
wext := l.pw.newEncoderRaw(relocObjExt)
|
|
wdict := l.pw.newEncoderRaw(relocObjDict)
|
|
|
|
l.decls[sym] = w.idx
|
|
assert(wext.idx == w.idx)
|
|
assert(wdict.idx == w.idx)
|
|
|
|
l.relocCommon(pr, &w, relocObj, 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, []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)
|
|
if r.bool() {
|
|
r.len()
|
|
} else {
|
|
r.reloc(relocType)
|
|
}
|
|
|
|
// TODO(mdempsky): This result now needs to include the 'derived'
|
|
// bool too, but none of the callers currently depend on it
|
|
// anyway. Either fix it to be meaningful, or just get rid of it
|
|
// altogether.
|
|
bounds[i] = -1
|
|
}
|
|
|
|
tag := codeObj(r.code(syncCodeObj))
|
|
|
|
return path, name, tag, bounds
|
|
}
|