]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.unified] cmd/compile: more Unified IR docs and review
authorMatthew Dempsky <mdempsky@google.com>
Tue, 24 May 2022 17:24:05 +0000 (10:24 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 14 Jun 2022 22:31:06 +0000 (22:31 +0000)
This adds more documentation throughout the core Unified IR logic and
removes their UNREVIEWED notices.

Updates #48194.

Change-Id: Iddd30edaee1c6ea8a05a5a7e013480e02be00d29
Reviewed-on: https://go-review.googlesource.com/c/go/+/411917
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/compile/internal/noder/codes.go
src/cmd/compile/internal/noder/linker.go
src/cmd/compile/internal/noder/quirks.go
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/noder/unified.go
src/cmd/compile/internal/noder/writer.go

index 59c8ec8121bc43495a13cbb4bee0d172186f044a..f7ad2503c2cdfe9b9c1d53e6c72562e533e24c9e 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -8,6 +6,7 @@ package noder
 
 import "internal/pkgbits"
 
+// A codeStmt distinguishes among statement encodings.
 type codeStmt int
 
 func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 }
@@ -31,6 +30,7 @@ const (
        stmtSelect
 )
 
+// A codeExpr distinguishes among expression encodings.
 type codeExpr int
 
 func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr }
@@ -66,6 +66,7 @@ const (
        assignExpr
 )
 
+// A codeDecl distinguishes among declaration encodings.
 type codeDecl int
 
 func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl }
index a58b9b930cfcf5f82b5978dcf151d93f237d795e..1626c04090988cbb0b6ec4af25ca552ce0dc262b 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -34,6 +32,9 @@ import (
 // low-level linking details can be moved there, but the logic for
 // handling extension data needs to stay in the compiler.
 
+// A linker combines a package's stub export data with any referenced
+// elements from imported packages into a single, self-contained
+// export data file.
 type linker struct {
        pw pkgbits.PkgEncoder
 
@@ -41,6 +42,9 @@ type linker struct {
        decls map[*types.Sym]pkgbits.Index
 }
 
+// relocAll ensures that all elements specified by pr and relocs are
+// copied into the output export data file, and returns the
+// corresponding indices in the output.
 func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt {
        res := make([]pkgbits.RelocEnt, len(relocs))
        for i, rent := range relocs {
@@ -50,6 +54,8 @@ func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.Re
        return res
 }
 
+// relocIdx ensures a single element is copied into the output export
+// data file, and returns the corresponding index in the output.
 func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index {
        assert(pr != nil)
 
@@ -85,10 +91,19 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index)
        return newidx
 }
 
+// relocString copies the specified string from pr into the output
+// export data file, deduplicating it against other strings.
 func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
        return l.pw.StringIdx(pr.StringIdx(idx))
 }
 
+// relocPkg copies the specified package from pr into the output
+// export data file, rewriting its import path to match how it was
+// imported.
+//
+// TODO(mdempsky): Since CL 391014, we already have the compilation
+// unit's import path, so there should be no need to rewrite packages
+// anymore.
 func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
        path := pr.PeekPkgPath(idx)
 
@@ -114,6 +129,9 @@ func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
        return w.Flush()
 }
 
+// relocObj copies the specified object from pr into the output export
+// data file, rewriting its compiler-private extension data (e.g.,
+// adding inlining cost and escape analysis results for functions).
 func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
        path, name, tag := pr.PeekObj(idx)
        sym := types.NewPkg(path, "").Lookup(name)
@@ -184,6 +202,8 @@ func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index {
        return w.Idx
 }
 
+// relocCommon copies the specified element from pr into w,
+// recursively relocating any referenced elements as well.
 func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) {
        r := pr.NewDecoderRaw(k, idx)
        w.Relocs = l.relocAll(pr, r.Relocs)
index c4cb9b9a2c2e73a5bbe08bbffdc69a88dd979be1..a22577f9656f963fabbfad071ac196fb5d93f378 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -12,12 +10,12 @@ import (
        "cmd/compile/internal/syntax"
 )
 
-// This file defines helper functions useful for satisfying toolstash
-// -cmp when compared against the legacy frontend behavior, but can be
-// removed after that's no longer a concern.
-
 // typeExprEndPos returns the position that noder would leave base.Pos
 // after parsing the given type expression.
+//
+// Deprecated: This function exists to emulate position semantics from
+// Go 1.17, necessary for compatibility with the backend DWARF
+// generation logic that assigns variables to their appropriate scope.
 func typeExprEndPos(expr0 syntax.Expr) syntax.Pos {
        for {
                switch expr := expr0.(type) {
index e8401c5775f192e5dbad33b14de159ce13ab4fdf..5ebc776605f5783421e71cfd5188db9197f51f00 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -26,15 +24,25 @@ import (
        "cmd/internal/src"
 )
 
+// This file implements cmd/compile backend's reader for the Unified
+// IR export data.
+
+// A pkgReader reads Unified IR export data.
 type pkgReader struct {
        pkgbits.PkgDecoder
 
+       // Indices for encoded things; lazily populated as needed.
+       //
+       // Note: Objects (i.e., ir.Names) are lazily instantiated by
+       // populating their types.Sym.Def; see objReader below.
+
        posBases []*src.PosBase
        pkgs     []*types.Pkg
        typs     []*types.Type
 
-       // offset for rewriting the given index into the output,
-       // but bitwise inverted so we can detect if we're missing the entry or not.
+       // offset for rewriting the given (absolute!) index into the output,
+       // but bitwise inverted so we can detect if we're missing the entry
+       // or not.
        newindex []pkgbits.Index
 }
 
@@ -50,6 +58,8 @@ func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
        }
 }
 
+// A pkgReaderIndex compactly identifies an index (and its
+// corresponding dictionary) within a package's export data.
 type pkgReaderIndex struct {
        pr   *pkgReader
        idx  pkgbits.Index
@@ -69,6 +79,7 @@ func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pk
        }
 }
 
+// A writer provides APIs for reading an individual element.
 type reader struct {
        pkgbits.Decoder
 
@@ -162,6 +173,7 @@ func setValue(name *ir.Name, val constant.Value) {
 
 // @@@ Positions
 
+// pos reads a position from the bitstream.
 func (r *reader) pos() src.XPos {
        return base.Ctxt.PosTable.XPos(r.pos0())
 }
@@ -178,10 +190,13 @@ func (r *reader) pos0() src.Pos {
        return src.MakePos(posBase, line, col)
 }
 
+// posBase reads a position base from the bitstream.
 func (r *reader) posBase() *src.PosBase {
        return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
 }
 
+// posBaseIdx returns the specified position base, reading it first if
+// needed.
 func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
        if b := pr.posBases[idx]; b != nil {
                return b
@@ -222,6 +237,7 @@ func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase {
        return b
 }
 
+// TODO(mdempsky): Document this.
 func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
        if r.inlCall == nil {
                return oldBase
@@ -236,36 +252,23 @@ func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
        return newBase
 }
 
+// TODO(mdempsky): Document this.
 func (r *reader) updatePos(xpos src.XPos) src.XPos {
        pos := base.Ctxt.PosTable.Pos(xpos)
        pos.SetBase(r.inlPosBase(pos.Base()))
        return base.Ctxt.PosTable.XPos(pos)
 }
 
-func (r *reader) origPos(xpos src.XPos) src.XPos {
-       if r.inlCall == nil {
-               return xpos
-       }
-
-       pos := base.Ctxt.PosTable.Pos(xpos)
-       for old, new := range r.inlPosBases {
-               if pos.Base() == new {
-                       pos.SetBase(old)
-                       return base.Ctxt.PosTable.XPos(pos)
-               }
-       }
-
-       base.FatalfAt(xpos, "pos base missing from inlPosBases")
-       panic("unreachable")
-}
-
 // @@@ Packages
 
+// pkg reads a package reference from the bitstream.
 func (r *reader) pkg() *types.Pkg {
        r.Sync(pkgbits.SyncPkg)
        return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
 }
 
+// pkgIdx returns the specified package from the export data, reading
+// it first if needed.
 func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
        if pkg := pr.pkgs[idx]; pkg != nil {
                return pkg
@@ -276,6 +279,7 @@ func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg {
        return pkg
 }
 
+// doPkg reads a package definition from the bitstream.
 func (r *reader) doPkg() *types.Pkg {
        path := r.String()
        switch path {
@@ -530,8 +534,12 @@ func (r *reader) param() (*types.Pkg, *types.Field) {
 
 // @@@ Objects
 
+// objReader maps qualified identifiers (represented as *types.Sym) to
+// a pkgReader and corresponding index that can be used for reading
+// that object's definition.
 var objReader = map[*types.Sym]pkgReaderIndex{}
 
+// obj reads an instantiated object reference from the bitstream.
 func (r *reader) obj() ir.Node {
        r.Sync(pkgbits.SyncObject)
 
@@ -567,6 +575,8 @@ func (r *reader) obj() ir.Node {
        return r.p.objIdx(idx, implicits, explicits)
 }
 
+// objIdx returns the specified object from the bitstream,
+// instantiated with the given type arguments, if any.
 func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type) ir.Node {
        rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
        _, sym := rname.qualifiedIdent()
@@ -707,6 +717,7 @@ func (r *reader) mangle(sym *types.Sym) *types.Sym {
        return sym.Pkg.Lookup(buf.String())
 }
 
+// objDictIdx reads and returns the specified object dictionary.
 func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type) *readerDict {
        r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
 
@@ -955,6 +966,8 @@ var bodyReader = map[*ir.Func]pkgReaderIndex{}
 // constructed.
 var todoBodies []*ir.Func
 
+// addBody reads a function body reference from the element bitstream,
+// and associates it with fn.
 func (r *reader) addBody(fn *ir.Func) {
        pri := pkgReaderIndex{r.p, r.Reloc(pkgbits.RelocBody), r.dict}
        bodyReader[fn] = pri
@@ -978,6 +991,8 @@ func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
        r.funcBody(fn)
 }
 
+// funcBody reads a function body definition from the element
+// bitstream, and populates fn with it.
 func (r *reader) funcBody(fn *ir.Func) {
        r.curfn = fn
        r.closureVars = fn.ClosureVars
@@ -1995,6 +2010,8 @@ func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
 
 var inlgen = 0
 
+// InlineCall implements inline.NewInline by re-reading the function
+// body from its Unified IR export data.
 func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
        // TODO(mdempsky): Turn callerfn into an explicit parameter.
        callerfn := ir.CurFunc
@@ -2281,18 +2298,21 @@ func (r *reader) needWrapper(typ *types.Type) {
        }
 }
 
+// importedDef reports whether r is reading from an imported and
+// non-generic element.
+//
+// If a type was found in an imported package, then we can assume that
+// package (or one of its transitive dependencies) already generated
+// method wrappers for it.
+//
+// Exception: If we're instantiating an imported generic type or
+// function, we might be instantiating it with type arguments not
+// previously seen before.
+//
+// TODO(mdempsky): Distinguish when a generic function or type was
+// instantiated in an imported package so that we can add types to
+// haveWrapperTypes instead.
 func (r *reader) importedDef() bool {
-       // If a type was found in an imported package, then we can assume
-       // that package (or one of its transitive dependencies) already
-       // generated method wrappers for it.
-       //
-       // Exception: If we're instantiating an imported generic type or
-       // function, we might be instantiating it with type arguments not
-       // previously seen before.
-       //
-       // TODO(mdempsky): Distinguish when a generic function or type was
-       // instantiated in an imported package so that we can add types to
-       // haveWrapperTypes instead.
        return r.p != localPkgReader && !r.hasTypeParams()
 }
 
index f7cf7f90b2a1a31f0f09d011297dc6abbcab4254..95486af66c313ec0e7e2aea222e4d08bdfa5f00e 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -226,6 +224,8 @@ func freePackage(pkg *types2.Package) {
        base.Fatalf("package never finalized")
 }
 
+// readPackage reads package export data from pr to populate
+// importpkg.
 func readPackage(pr *pkgReader, importpkg *types.Pkg) {
        r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
 
@@ -252,6 +252,8 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg) {
        }
 }
 
+// writeUnifiedExport writes to `out` the finalized, self-contained
+// Unified IR export data file for the current compilation unit.
 func writeUnifiedExport(out io.Writer) {
        l := linker{
                pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
index 4d133e033e6ee03fb3391023b091e3ca99f48546..8ef63a0085626e0d9673147534bf27f5e9a12ff3 100644 (file)
@@ -1,5 +1,3 @@
-// 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.
@@ -16,6 +14,45 @@ import (
        "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 {
        pkgbits.PkgEncoder
 
@@ -23,18 +60,29 @@ type pkgWriter struct {
        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
-       globalsIdx  map[types2.Object]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  map[types2.Object]string
+       // 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),
@@ -43,9 +91,9 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
                curpkg: pkg,
                info:   info,
 
-               pkgsIdx:    make(map[*types2.Package]pkgbits.Index),
-               globalsIdx: make(map[types2.Object]pkgbits.Index),
-               typsIdx:    make(map[types2.Type]pkgbits.Index),
+               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),
 
@@ -56,18 +104,23 @@ func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
        }
 }
 
+// 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
 
@@ -77,13 +130,19 @@ type writer struct {
        // scope closes, and then maybe we can just use the same map for
        // storing the TypeParams too (as their TypeName instead).
 
-       // variables declared within this function
+       // 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    []posObj
-       closureVarsIdx 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
+       dict *writerDict
+
+       // derived tracks whether the type being written out references any
+       // type parameters. It's unused for writing non-type things.
        derived bool
 }
 
@@ -128,16 +187,25 @@ type typeInfo struct {
        derived bool
 }
 
+// An objInfo represents a reference to an encoded, instantiated (if
+// applicable) Go object.
 type objInfo struct {
        idx       pkgbits.Index // index for the generic function declaration
        explicits []typeInfo    // info for the type arguments
 }
 
+// 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.
 func (info objInfo) anyDerived() bool {
        for _, explicit := range info.explicits {
                if explicit.derived {
@@ -147,6 +215,8 @@ func (info objInfo) anyDerived() bool {
        return false
 }
 
+// equals reports whether info and other represent the same Go object
+// (i.e., same base object and identical type arguments, if any).
 func (info objInfo) equals(other objInfo) bool {
        if info.idx != other.idx {
                return false
@@ -169,6 +239,7 @@ func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *
 
 // @@@ Positions
 
+// pos writes the position of p into the element bitstream.
 func (w *writer) pos(p poser) {
        w.Sync(pkgbits.SyncPos)
        pos := p.Pos()
@@ -178,17 +249,19 @@ func (w *writer) pos(p poser) {
                return
        }
 
-       // TODO(mdempsky): Delta encoding. Also, if there's a b-side, update
-       // its position base too (but not vice versa!).
+       // 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
@@ -210,11 +283,14 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index {
 
 // @@@ 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
@@ -256,10 +332,13 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index {
 
 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) {
        w.typInfo(w.p.typIdx(typ, w.dict))
 }
 
+// typInfo writes a use of the given type (specified as a typeInfo
+// instead) into the bitstream.
 func (w *writer) typInfo(info typeInfo) {
        w.Sync(pkgbits.SyncType)
        if w.Bool(info.derived) {
@@ -468,6 +547,11 @@ func (w *writer) param(param *types2.Var) {
 
 // @@@ 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 {
@@ -515,8 +599,13 @@ func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
        }
 }
 
+// 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 {
-       if idx, ok := pw.globalsIdx[obj]; ok {
+       // 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
        }
 
@@ -530,12 +619,35 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index {
                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.globalsIdx[obj] = w.Idx // break cycles
+       pw.objsIdx[obj] = w.Idx // break cycles
        assert(wext.Idx == w.Idx)
        assert(wname.Idx == w.Idx)
        assert(wdict.Idx == w.Idx)
@@ -557,6 +669,8 @@ func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index {
        return w.Idx
 }
 
+// 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
@@ -726,8 +840,8 @@ func (w *writer) qualifiedIdent(obj types2.Object) {
 // 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 name, within the context of a
-// particular function).
+// 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)
@@ -789,7 +903,7 @@ func (w *writer) funcExt(obj *types2.Func) {
        }
 
        sig, block := obj.Type().(*types2.Signature), decl.Body
-       body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict)
+       body, closureVars := w.p.bodyIdx(sig, block, w.dict)
        assert(len(closureVars) == 0)
 
        w.Sync(pkgbits.SyncFuncExt)
@@ -831,7 +945,9 @@ func (w *writer) pragmaFlag(p ir.PragmaFlag) {
 
 // @@@ Function bodies
 
-func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posObj) {
+// 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
 
@@ -864,6 +980,7 @@ func (w *writer) funcarg(param *types2.Var, result bool) {
        }
 }
 
+// addLocal records the declaration of a new local variable.
 func (w *writer) addLocal(obj *types2.Var) {
        w.Sync(pkgbits.SyncAddLocal)
        idx := len(w.localsIdx)
@@ -876,6 +993,8 @@ func (w *writer) addLocal(obj *types2.Var) {
        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)
 
@@ -890,7 +1009,7 @@ func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
                        w.closureVarsIdx = make(map[*types2.Var]int)
                }
                idx = len(w.closureVars)
-               w.closureVars = append(w.closureVars, posObj{pos, obj})
+               w.closureVars = append(w.closureVars, posVar{pos, obj})
                w.closureVarsIdx[obj] = idx
        }
        w.Len(idx)
@@ -913,6 +1032,7 @@ func (w *writer) closeAnotherScope() {
 
 // @@@ Statements
 
+// stmt writes the given statement into the function body bitstream.
 func (w *writer) stmt(stmt syntax.Stmt) {
        var stmts []syntax.Stmt
        if stmt != nil {
@@ -1213,6 +1333,7 @@ func (w *writer) optLabel(label *syntax.Name) {
 
 // @@@ Expressions
 
+// expr writes the given expression into the function body bitstream.
 func (w *writer) expr(expr syntax.Expr) {
        base.Assertf(expr != nil, "missing expression")
 
@@ -1439,7 +1560,7 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
        assert(ok)
        sig := tv.Type.(*types2.Signature)
 
-       body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict)
+       body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict)
 
        w.Sync(pkgbits.SyncFuncLit)
        w.pos(expr)
@@ -1448,15 +1569,15 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
        w.Len(len(closureVars))
        for _, cv := range closureVars {
                w.pos(cv.pos)
-               w.useLocal(cv.pos, cv.obj)
+               w.useLocal(cv.pos, cv.var_)
        }
 
        w.Reloc(pkgbits.RelocBody, body)
 }
 
-type posObj struct {
-       pos syntax.Pos
-       obj *types2.Var
+type posVar struct {
+       pos  syntax.Pos
+       var_ *types2.Var
 }
 
 func (w *writer) exprList(expr syntax.Expr) {
@@ -1509,6 +1630,9 @@ func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) {
        w.typInfo(info)
 }
 
+// isInterface reports whether typ is known to be an interface type.
+// If typ is a type parameter, then isInterface reports an internal
+// compiler error instead.
 func isInterface(typ types2.Type) bool {
        if _, ok := typ.(*types2.TypeParam); ok {
                // typ is a type parameter and may be instantiated as either a
@@ -1521,6 +1645,7 @@ func isInterface(typ types2.Type) bool {
        return ok
 }
 
+// op writes an Op into the bitstream.
 func (w *writer) op(op ir.Op) {
        // TODO(mdempsky): Remove in favor of explicit codes? Would make
        // export data more stable against internal refactorings, but low
@@ -1561,6 +1686,12 @@ type fileImports struct {
        importedEmbed, importedUnsafe bool
 }
 
+// declCollector is a visitor type that collects compiler-needed
+// information about declarations that types2 doesn't track.
+//
+// Notably, it maps declared types and functions back to their
+// declaration statement, keeps track of implicit type parameters, and
+// assigns unique type "generation" numbers to local defined types.
 type declCollector struct {
        pw         *pkgWriter
        typegen    *int