Newer
Older
// 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 (
"fmt"
"internal/pkgbits"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types2"
)
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// This file implements the Unified IR package writer and defines the
// Unified IR export data format.
//
// Low-level coding details (e.g., byte-encoding of individual
// primitive values, or handling element bitstreams and
// cross-references) are handled by internal/pkgbits, so here we only
// concern ourselves with higher-level worries like mapping Go
// language constructs into elements.
// There are two central types in the writing process: the "writer"
// type handles writing out individual elements, while the "pkgWriter"
// type keeps track of which elements have already been created.
//
// For each sort of "thing" (e.g., position, package, object, type)
// that can be written into the export data, there are generally
// several methods that work together:
//
// - writer.thing handles writing out a *use* of a thing, which often
// means writing a relocation to that thing's encoded index.
//
// - pkgWriter.thingIdx handles reserving an index for a thing, and
// writing out any elements needed for the thing.
//
// - writer.doThing handles writing out the *definition* of a thing,
// which in general is a mix of low-level coding primitives (e.g.,
// ints and strings) or uses of other things.
//
// A design goal of Unified IR is to have a single, canonical writer
// implementation, but multiple reader implementations each tailored
// to their respective needs. For example, within cmd/compile's own
// backend, inlining is implemented largely by just re-running the
// function body reading code.
// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo,
// and better document the file format boundary between public and
// private data.
// A pkgWriter constructs Unified IR export data from the results of
// running the types2 type checker on a Go compilation unit.
type pkgWriter struct {
pkgbits.PkgEncoder
m posMap
curpkg *types2.Package
info *types2.Info
// Indices for previously written syntax and types2 things.
posBasesIdx map[*syntax.PosBase]pkgbits.Index
pkgsIdx map[*types2.Package]pkgbits.Index
typsIdx map[types2.Type]pkgbits.Index
objsIdx map[types2.Object]pkgbits.Index
// Maps from types2.Objects back to their syntax.Decl.
funDecls map[*types2.Func]*syntax.FuncDecl
typDecls map[*types2.TypeName]typeDeclGen
// linknames maps package-scope objects to their linker symbol name,
// if specified by a //go:linkname directive.
linknames map[types2.Object]string
// cgoPragmas accumulates any //go:cgo_* pragmas that need to be
// passed through to cmd/link.
cgoPragmas [][]string
}
// newPkgWriter returns an initialized pkgWriter for the specified
// package.
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
m: m,
curpkg: pkg,
info: info,
pkgsIdx: make(map[*types2.Package]pkgbits.Index),
objsIdx: make(map[types2.Object]pkgbits.Index),
typsIdx: make(map[types2.Type]pkgbits.Index),
posBasesIdx: make(map[*syntax.PosBase]pkgbits.Index),
funDecls: make(map[*types2.Func]*syntax.FuncDecl),
typDecls: make(map[*types2.TypeName]typeDeclGen),
linknames: make(map[types2.Object]string),
}
}
// errorf reports a user error about thing p.
func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) {
base.ErrorfAt(pw.m.pos(p), msg, args...)
}
// fatalf reports an internal compiler error about thing p.
func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) {
base.FatalfAt(pw.m.pos(p), msg, args...)
}
// unexpected reports a fatal error about a thing of unexpected
// dynamic type.
func (pw *pkgWriter) unexpected(what string, p poser) {
pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
}
// A writer provides APIs for writing out an individual element.
type writer struct {
p *pkgWriter
pkgbits.Encoder
// TODO(mdempsky): We should be able to prune localsIdx whenever a
// scope closes, and then maybe we can just use the same map for
// storing the TypeParams too (as their TypeName instead).
// localsIdx tracks any local variables declared within this
// function body. It's unused for writing out non-body things.
localsIdx map[*types2.Var]int
// closureVars tracks any free variables that are referenced by this
// function body. It's unused for writing out non-body things.
closureVars []posVar
closureVarsIdx map[*types2.Var]int // index of previously seen free variables
dict *writerDict
// derived tracks whether the type being written out references any
// type parameters. It's unused for writing non-type things.
derived bool
}
// A writerDict tracks types and objects that are used by a declaration.
type writerDict struct {
implicits []*types2.TypeName
// derived is a slice of type indices for computing derived types
// (i.e., types that depend on the declaration's type parameters).
Matthew Dempsky
committed
derived []derivedInfo
// derivedIdx maps a Type to its corresponding index within the
// derived slice, if present.
derivedIdx map[types2.Type]pkgbits.Index
Matthew Dempsky
committed
// funcs lists references to generic functions that were
// instantiated with derived types (i.e., that require
// sub-dictionaries when called at run time).
funcs []objInfo
// itabs lists itabs that are needed for dynamic type assertions
// (including type switches).
itabs []itabInfo
Matthew Dempsky
committed
}
// A derivedInfo represents a reference to an encoded generic Go type.
Matthew Dempsky
committed
type derivedInfo struct {
Matthew Dempsky
committed
needed bool
}
// A typeInfo represents a reference to an encoded Go type.
//
// If derived is true, then the typeInfo represents a generic Go type
// that contains type parameters. In this case, idx is an index into
// the readerDict.derived{,Types} arrays.
//
// Otherwise, the typeInfo represents a non-generic Go type, and idx
// is an index into the reader.typs array instead.
Matthew Dempsky
committed
type typeInfo struct {
Matthew Dempsky
committed
derived bool
}
// An objInfo represents a reference to an encoded, instantiated (if
// applicable) Go object.
Matthew Dempsky
committed
type objInfo struct {
idx pkgbits.Index // index for the generic function declaration
explicits []typeInfo // info for the type arguments
Matthew Dempsky
committed
}
// An itabInfo represents a reference to an encoded itab entry (i.e.,
// a non-empty interface type along with a concrete type that
// implements that interface).
//
// itabInfo is only used for
type itabInfo struct {
typIdx pkgbits.Index // always a derived type index
iface typeInfo // always a non-empty interface type
// anyDerived reports whether any of info's explicit type arguments
// are derived types.
Matthew Dempsky
committed
func (info objInfo) anyDerived() bool {
for _, explicit := range info.explicits {
if explicit.derived {
return true
}
}
return false
}
// equals reports whether info and other represent the same Go object
// (i.e., same base object and identical type arguments, if any).
Matthew Dempsky
committed
func (info objInfo) equals(other objInfo) bool {
if info.idx != other.idx {
return false
}
assert(len(info.explicits) == len(other.explicits))
for i, targ := range info.explicits {
if targ != other.explicits[i] {
return false
}
}
return true
func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer {
Encoder: pw.NewEncoder(k, marker),
p: pw,
}
}
// @@@ Positions
// pos writes the position of p into the element bitstream.
func (w *writer) pos(p poser) {
w.Sync(pkgbits.SyncPos)
pos := p.Pos()
// TODO(mdempsky): Track down the remaining cases here and fix them.
if !w.Bool(pos.IsKnown()) {
return
}
// TODO(mdempsky): Delta encoding.
w.posBase(pos.Base())
w.Uint(pos.Line())
w.Uint(pos.Col())
// posBase writes a reference to the given PosBase into the element
// bitstream.
func (w *writer) posBase(b *syntax.PosBase) {
w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b))
// posBaseIdx returns the index for the given PosBase.
func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index {
if idx, ok := pw.posBasesIdx[b]; ok {
return idx
}
w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase)
w.p.posBasesIdx[b] = w.Idx
w.String(trimFilename(b))
if !w.Bool(b.IsFileBase()) {
w.Uint(b.Line())
w.Uint(b.Col())
return w.Flush()
}
// @@@ Packages
// pkg writes a use of the given Package into the element bitstream.
func (w *writer) pkg(pkg *types2.Package) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg))
// pkgIdx returns the index for the given package, adding it to the
// package export data if needed.
func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index {
if idx, ok := pw.pkgsIdx[pkg]; ok {
return idx
}
w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
pw.pkgsIdx[pkg] = w.Idx
// The universe and package unsafe need to be handled specially by
// importers anyway, so we serialize them using just their package
// path. This ensures that readers don't confuse them for
// user-defined packages.
switch pkg {
case nil: // universe
w.String("builtin") // same package path used by godoc
case types2.Unsafe:
w.String("unsafe")
default:
// TODO(mdempsky): Write out pkg.Path() for curpkg too.
var path string
if pkg != w.p.curpkg {
path = pkg.Path()
}
base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path)
w.String(path)
w.String(pkg.Name())
w.Len(0) // was package height, but not necessary anymore.
w.Len(len(pkg.Imports()))
for _, imp := range pkg.Imports() {
w.pkg(imp)
}
}
return w.Flush()
}
// @@@ Types
var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
// typ writes a use of the given type into the bitstream.
func (w *writer) typ(typ types2.Type) {
Matthew Dempsky
committed
w.typInfo(w.p.typIdx(typ, w.dict))
}
// typInfo writes a use of the given type (specified as a typeInfo
// instead) into the bitstream.
Matthew Dempsky
committed
func (w *writer) typInfo(info typeInfo) {
w.Sync(pkgbits.SyncType)
if w.Bool(info.derived) {
w.derived = true
} else {
w.Reloc(pkgbits.RelocType, info.idx)
}
}
// typIdx returns the index where the export data description of type
// can be read back in. If no such index exists yet, it's created.
//
// typIdx also reports whether typ is a derived type; that is, whether
// its identity depends on type parameters.
Matthew Dempsky
committed
func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
if idx, ok := pw.typsIdx[typ]; ok {
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: false}
}
if dict != nil {
if idx, ok := dict.derivedIdx[typ]; ok {
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: true}
}
w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
w.dict = dict
switch typ := typ.(type) {
default:
base.Fatalf("unexpected type: %v (%T)", typ, typ)
case *types2.Basic:
switch kind := typ.Kind(); {
case kind == types2.Invalid:
base.Fatalf("unexpected types2.Invalid")
case types2.Typ[kind] == typ:
w.Code(pkgbits.TypeBasic)
w.Len(int(kind))
default:
// Handle "byte" and "rune" as references to their TypeName.
obj := types2.Universe.Lookup(typ.Name())
assert(obj.Type() == typ)
w.Code(pkgbits.TypeNamed)
w.obj(obj, nil)
}
case *types2.Named:
assert(typ.TypeParams().Len() == typ.TypeArgs().Len())
// TODO(mdempsky): Why do we need to loop here?
orig := typ
for orig.TypeArgs() != nil {
orig = orig.Origin()
w.Code(pkgbits.TypeNamed)
w.obj(orig.Obj(), typ.TypeArgs())
case *types2.TypeParam:
index := func() int {
for idx, name := range w.dict.implicits {
if name.Type().(*types2.TypeParam) == typ {
return idx
}
}
return len(w.dict.implicits) + typ.Index()
}()
w.derived = true
w.Code(pkgbits.TypeTypeParam)
w.Len(index)
case *types2.Array:
w.Code(pkgbits.TypeArray)
w.Uint64(uint64(typ.Len()))
w.typ(typ.Elem())
case *types2.Chan:
w.Code(pkgbits.TypeChan)
w.Len(int(typ.Dir()))
w.typ(typ.Elem())
case *types2.Map:
w.Code(pkgbits.TypeMap)
w.typ(typ.Key())
w.typ(typ.Elem())
case *types2.Pointer:
w.Code(pkgbits.TypePointer)
w.typ(typ.Elem())
case *types2.Signature:
Robert Griesemer
committed
base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
w.Code(pkgbits.TypeSignature)
w.signature(typ)
case *types2.Slice:
w.Code(pkgbits.TypeSlice)
w.typ(typ.Elem())
case *types2.Struct:
w.Code(pkgbits.TypeStruct)
w.structType(typ)
case *types2.Interface:
if typ == anyTypeName.Type() {
w.Code(pkgbits.TypeNamed)
w.obj(anyTypeName, nil)
break
}
w.Code(pkgbits.TypeInterface)
w.interfaceType(typ)
case *types2.Union:
w.Code(pkgbits.TypeUnion)
w.unionType(typ)
}
if w.derived {
idx := pkgbits.Index(len(dict.derived))
dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})
dict.derivedIdx[typ] = idx
Matthew Dempsky
committed
return typeInfo{idx: idx, derived: true}
}
pw.typsIdx[typ] = w.Idx
return typeInfo{idx: w.Flush(), derived: false}
}
func (w *writer) structType(typ *types2.Struct) {
w.Len(typ.NumFields())
for i := 0; i < typ.NumFields(); i++ {
f := typ.Field(i)
w.pos(f)
w.selector(f)
w.typ(f.Type())
w.String(typ.Tag(i))
w.Bool(f.Embedded())
}
}
func (w *writer) unionType(typ *types2.Union) {
w.Len(typ.Len())
Robert Griesemer
committed
for i := 0; i < typ.Len(); i++ {
t := typ.Term(i)
w.Bool(t.Tilde())
Robert Griesemer
committed
w.typ(t.Type())
}
}
func (w *writer) interfaceType(typ *types2.Interface) {
w.Len(typ.NumExplicitMethods())
w.Len(typ.NumEmbeddeds())
if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 {
w.Bool(typ.IsImplicit())
} else {
// Implicit interfaces always have 0 explicit methods and 1
// embedded type, so we skip writing out the implicit flag
// otherwise as a space optimization.
assert(!typ.IsImplicit())
}
for i := 0; i < typ.NumExplicitMethods(); i++ {
m := typ.ExplicitMethod(i)
sig := m.Type().(*types2.Signature)
assert(sig.TypeParams() == nil)
w.pos(m)
w.selector(m)
w.signature(sig)
}
for i := 0; i < typ.NumEmbeddeds(); i++ {
w.typ(typ.EmbeddedType(i))
}
}
func (w *writer) signature(sig *types2.Signature) {
w.Sync(pkgbits.SyncSignature)
w.params(sig.Params())
w.params(sig.Results())
w.Bool(sig.Variadic())
}
func (w *writer) params(typ *types2.Tuple) {
w.Sync(pkgbits.SyncParams)
w.Len(typ.Len())
for i := 0; i < typ.Len(); i++ {
w.param(typ.At(i))
}
}
func (w *writer) param(param *types2.Var) {
w.Sync(pkgbits.SyncParam)
w.pos(param)
w.localIdent(param)
w.typ(param.Type())
}
// @@@ Objects
// obj writes a use of the given object into the bitstream.
//
// If obj is a generic object, then explicits are the explicit type
// arguments used to instantiate it (i.e., used to substitute the
// object's own declared type parameters).
func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
explicitInfos := make([]typeInfo, explicits.Len())
for i := range explicitInfos {
explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict)
Matthew Dempsky
committed
}
info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos}
if _, ok := obj.(*types2.Func); ok && info.anyDerived() {
idx := -1
for i, prev := range w.dict.funcs {
if prev.equals(info) {
idx = i
}
}
if idx < 0 {
idx = len(w.dict.funcs)
w.dict.funcs = append(w.dict.funcs, info)
}
// TODO(mdempsky): Push up into expr; this shouldn't appear
// outside of expression context.
w.Sync(pkgbits.SyncObject)
w.Bool(true)
w.Len(idx)
Matthew Dempsky
committed
return
}
// TODO(mdempsky): Push up into typIdx; this shouldn't be needed
// except while writing out types.
if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
assert(ok)
if len(decl.implicits) != 0 {
w.derived = true
}
w.Sync(pkgbits.SyncObject)
w.Bool(false)
w.Reloc(pkgbits.RelocObj, info.idx)
w.Len(len(info.explicits))
Matthew Dempsky
committed
for _, info := range info.explicits {
w.typInfo(info)
// objIdx returns the index for the given Object, adding it to the
// export data as needed.
func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index {
// TODO(mdempsky): Validate that obj is a global object (or a local
// defined type, which we hoist to global scope anyway).
if idx, ok := pw.objsIdx[obj]; ok {
return idx
}
dict := &writerDict{
derivedIdx: make(map[types2.Type]pkgbits.Index),
}
if isDefinedType(obj) && obj.Pkg() == pw.curpkg {
decl, ok := pw.typDecls[obj.(*types2.TypeName)]
assert(ok)
dict.implicits = decl.implicits
}
// We encode objects into 4 elements across different sections, all
// sharing the same index:
//
// - RelocName has just the object's qualified name (i.e.,
// Object.Pkg and Object.Name) and the CodeObj indicating what
// specific type of Object it is (Var, Func, etc).
//
// - RelocObj has the remaining public details about the object,
// relevant to go/types importers.
//
// - RelocObjExt has additional private details about the object,
// which are only relevant to cmd/compile itself. This is
// separated from RelocObj so that go/types importers are
// unaffected by internal compiler changes.
//
// - RelocObjDict has public details about the object's type
// parameters and derived type's used by the object. This is
// separated to facilitate the eventual introduction of
// shape-based stenciling.
//
// TODO(mdempsky): Re-evaluate whether RelocName still makes sense
// to keep separate from RelocObj.
w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1)
wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1)
wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1)
wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1)
pw.objsIdx[obj] = w.Idx // break cycles
assert(wext.Idx == w.Idx)
assert(wname.Idx == w.Idx)
assert(wdict.Idx == w.Idx)
w.dict = dict
wext.dict = dict
code := w.doObj(wext, obj)
w.Flush()
wext.Flush()
wname.qualifiedIdent(obj)
wname.Code(code)
wname.Flush()
wdict.objDict(obj, w.dict)
// doObj writes the RelocObj definition for obj to w, and the
// RelocObjExt definition to wext.
func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj {
if obj.Pkg() != w.p.curpkg {
return pkgbits.ObjStub
}
switch obj := obj.(type) {
default:
w.p.unexpected("object", obj)
panic("unreachable")
case *types2.Const:
w.pos(obj)
w.Value(obj.Val())
return pkgbits.ObjConst
case *types2.Func:
decl, ok := w.p.funDecls[obj]
assert(ok)
sig := obj.Type().(*types2.Signature)
w.pos(obj)
w.typeParamNames(sig.TypeParams())
w.signature(sig)
w.pos(decl)
wext.funcExt(obj)
return pkgbits.ObjFunc
case *types2.TypeName:
decl, ok := w.p.typDecls[obj]
assert(ok)
if obj.IsAlias() {
w.pos(obj)
w.typ(obj.Type())
return pkgbits.ObjAlias
}
named := obj.Type().(*types2.Named)
assert(named.TypeArgs() == nil)
w.typeParamNames(named.TypeParams())
wext.typeExt(obj)
w.typExpr(decl.Type)
w.Len(named.NumMethods())
for i := 0; i < named.NumMethods(); i++ {
w.method(wext, named.Method(i))
return pkgbits.ObjType
case *types2.Var:
w.pos(obj)
w.typ(obj.Type())
wext.varExt(obj)
return pkgbits.ObjVar
}
}
// typExpr writes the type represented by the given expression.
Matthew Dempsky
committed
//
// TODO(mdempsky): Document how this differs from exprType.
func (w *writer) typExpr(expr syntax.Expr) {
tv, ok := w.p.info.Types[expr]
assert(ok)
assert(tv.IsType())
w.typ(tv.Type)
}
// objDict writes the dictionary needed for reading the given object.
func (w *writer) objDict(obj types2.Object, dict *writerDict) {
// TODO(mdempsky): Split objDict into multiple entries? reader.go
// doesn't care about the type parameter bounds, and reader2.go
// doesn't care about referenced functions.
w.dict = dict // TODO(mdempsky): This is a bit sketchy.
w.Len(len(dict.implicits))
tparams := objTypeParams(obj)
Robert Griesemer
committed
ntparams := tparams.Len()
w.Len(ntparams)
Robert Griesemer
committed
for i := 0; i < ntparams; i++ {
Robert Griesemer
committed
w.typ(tparams.At(i).Constraint())
nderived := len(dict.derived)
w.Len(nderived)
for _, typ := range dict.derived {
w.Reloc(pkgbits.RelocType, typ.idx)
w.Bool(typ.needed)
}
nfuncs := len(dict.funcs)
for _, fn := range dict.funcs {
w.Reloc(pkgbits.RelocObj, fn.idx)
w.Len(len(fn.explicits))
for _, targ := range fn.explicits {
w.typInfo(targ)
}
}
nitabs := len(dict.itabs)
w.Len(nitabs)
for _, itab := range dict.itabs {
w.typInfo(itab.iface)
}
assert(len(dict.derived) == nderived)
assert(len(dict.funcs) == nfuncs)
func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
w.Sync(pkgbits.SyncTypeParamNames)
Robert Griesemer
committed
ntparams := tparams.Len()
for i := 0; i < ntparams; i++ {
Robert Griesemer
committed
tparam := tparams.At(i).Obj()
w.pos(tparam)
w.localIdent(tparam)
}
}
func (w *writer) method(wext *writer, meth *types2.Func) {
decl, ok := w.p.funDecls[meth]
assert(ok)
sig := meth.Type().(*types2.Signature)
w.Sync(pkgbits.SyncMethod)
w.pos(meth)
w.selector(meth)
w.typeParamNames(sig.RecvTypeParams())
w.param(sig.Recv())
w.signature(sig)
w.pos(decl) // XXX: Hack to workaround linker limitations.
wext.funcExt(meth)
}
// qualifiedIdent writes out the name of an object declared at package
// scope. (For now, it's also used to refer to local defined types.)
func (w *writer) qualifiedIdent(obj types2.Object) {
w.Sync(pkgbits.SyncSym)
name := obj.Name()
if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
assert(ok)
if decl.gen != 0 {
// TODO(mdempsky): Find a better solution than embedding middle
// dot in the symbol name; this is terrible.
name = fmt.Sprintf("%s·%v", name, decl.gen)
}
}
w.pkg(obj.Pkg())
w.String(name)
}
// TODO(mdempsky): We should be able to omit pkg from both localIdent
// and selector, because they should always be known from context.
// However, past frustrations with this optimization in iexport make
// me a little nervous to try it again.
// localIdent writes the name of a locally declared object (i.e.,
// objects that can only be accessed by non-qualified name, within the
// context of a particular function).
func (w *writer) localIdent(obj types2.Object) {
assert(!isGlobal(obj))
w.Sync(pkgbits.SyncLocalIdent)
w.pkg(obj.Pkg())
w.String(obj.Name())
}
// selector writes the name of a field or method (i.e., objects that
// can only be accessed using selector expressions).
func (w *writer) selector(obj types2.Object) {
w.Sync(pkgbits.SyncSelector)
w.pkg(obj.Pkg())
w.String(obj.Name())
}
// @@@ Compiler extensions
func (w *writer) funcExt(obj *types2.Func) {
decl, ok := w.p.funDecls[obj]
assert(ok)
// TODO(mdempsky): Extend these pragma validation flags to account
// for generics. E.g., linkname probably doesn't make sense at
// least.
pragma := asPragmaFlag(decl.Pragma)
if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {
w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")
}
if decl.Body != nil {
if pragma&ir.Noescape != 0 {
w.p.errorf(decl, "can only use //go:noescape with external func implementations")
}
if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 {
// Stack growth can't handle uintptr arguments that may
// be pointers (as we don't know which are pointers
// when creating the stack map). Thus uintptrkeepalive
// functions (and all transitive callees) must be
// nosplit.
//
// N.B. uintptrescapes implies uintptrkeepalive but it
// is OK since the arguments must escape to the heap.
//
// TODO(prattmic): Add recursive nosplit check of callees.
// TODO(prattmic): Functions with no body (i.e.,
// assembly) must also be nosplit, but we can't check
// that here.
w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")
}
} else {
if base.Flag.Complete || decl.Name.Value == "init" {
// Linknamed functions are allowed to have no body. Hopefully
// the linkname target has a body. See issue 23311.
if _, ok := w.p.linknames[obj]; !ok {
w.p.errorf(decl, "missing function body")
}
}
}
sig, block := obj.Type().(*types2.Signature), decl.Body
body, closureVars := w.p.bodyIdx(sig, block, w.dict)
assert(len(closureVars) == 0)
w.Sync(pkgbits.SyncFuncExt)
w.pragmaFlag(pragma)
w.linkname(obj)
w.Bool(false) // stub extension
w.Reloc(pkgbits.RelocBody, body)
w.Sync(pkgbits.SyncEOF)
}
func (w *writer) typeExt(obj *types2.TypeName) {
decl, ok := w.p.typDecls[obj]
assert(ok)
w.Sync(pkgbits.SyncTypeExt)
w.pragmaFlag(asPragmaFlag(decl.Pragma))
// No LSym.SymIdx info yet.
w.Int64(-1)
w.Int64(-1)
}
func (w *writer) varExt(obj *types2.Var) {
w.Sync(pkgbits.SyncVarExt)
w.linkname(obj)
}
func (w *writer) linkname(obj types2.Object) {
w.Sync(pkgbits.SyncLinkname)
w.Int64(-1)
w.String(w.p.linknames[obj])
}
func (w *writer) pragmaFlag(p ir.PragmaFlag) {
w.Sync(pkgbits.SyncPragma)
w.Int(int(p))
}
// @@@ Function bodies
// bodyIdx returns the index for the given function body (specified by
// block), adding it to the export data
func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) {
w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody)
w.dict = dict
w.funcargs(sig)
if w.Bool(block != nil) {
w.stmts(block.List)
w.pos(block.Rbrace)
}
return w.Flush(), w.closureVars
}
func (w *writer) funcargs(sig *types2.Signature) {
do := func(params *types2.Tuple, result bool) {
for i := 0; i < params.Len(); i++ {
w.funcarg(params.At(i), result)
}
}
if recv := sig.Recv(); recv != nil {
w.funcarg(recv, false)
}
do(sig.Params(), false)
do(sig.Results(), true)
}
func (w *writer) funcarg(param *types2.Var, result bool) {
if param.Name() != "" || result {
w.addLocal(param)
}
}
// addLocal records the declaration of a new local variable.
func (w *writer) addLocal(obj *types2.Var) {
w.Sync(pkgbits.SyncAddLocal)
idx := len(w.localsIdx)
if pkgbits.EnableSync {
w.Int(idx)
if w.localsIdx == nil {
w.localsIdx = make(map[*types2.Var]int)
}
w.localsIdx[obj] = idx
}
// useLocal writes a reference to the given local or free variable
// into the bitstream.
func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
w.Sync(pkgbits.SyncUseObjLocal)