Skip to content
Snippets Groups Projects
writer.go 73.8 KiB
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/buildcfg"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/syntax"
	"cmd/compile/internal/types2"
)

// 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 {

	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.
// 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),
		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), 0, 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)
}

func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
	tv, ok := pw.maybeTypeAndValue(x)
	if !ok {
		pw.fatalf(x, "missing Types entry: %v", syntax.String(x))
func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {
	tv := x.GetTypeInfo()

	// If x is a generic function whose type arguments are inferred
	// from assignment context, then we need to find its inferred type
	// in Info.Instances instead.
	if name, ok := x.(*syntax.Name); ok {
		if inst, ok := pw.info.Instances[name]; ok {
			tv.Type = inst.Type
		}
	}

	return tv, tv.Type != nil
}

// typeOf returns the Type of the given value expression.
func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type {
	tv := pw.typeAndValue(expr)
	if !tv.IsValue() {
		pw.fatalf(expr, "expected value: %v", syntax.String(expr))
	}
// A writer provides APIs for writing out an individual element.
type writer struct {
	p *pkgWriter

	// sig holds the signature for the current function body, if any.
	sig *types2.Signature

	// 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.
	// 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).

	// derivedIdx maps a Type to its corresponding index within the
	// derived slice, if present.
	derivedIdx map[types2.Type]pkgbits.Index
Loading
Loading full blame...