Skip to content

Commit

Permalink
go/ssa: update builder doc comment
Browse files Browse the repository at this point in the history
Also, avoid the terms "ground" and "abstract".

Change-Id: I23f85a6f8db4d8fbb5db19600f42c07bf449d2f1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/539555
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Tim King <taking@google.com>
  • Loading branch information
adonovan authored and gopherbot committed Nov 3, 2023
1 parent 942d9ac commit 7e0917a
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 104 deletions.
135 changes: 51 additions & 84 deletions go/ssa/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,73 @@

package ssa

// This file implements the BUILD phase of SSA construction.
// This file defines the builder, which builds SSA-form IR for function bodies.
//
// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
// (create.go), all packages are constructed and type-checked and
// definitions of all package members are created, method-sets are
// computed, and wrapper methods are synthesized.
// ssa.Packages are created in arbitrary order.
// SSA construction has two phases, "create" and "build". First, one
// or more packages are created in any order by a sequence of calls to
// CreatePackage, either from syntax or from mere type information.
// Each created package has a complete set of Members (const, var,
// type, func) that can be accessed through methods like
// Program.FuncValue.
//
// In the BUILD phase (builder.go), the builder traverses the AST of
// each Go source function and generates SSA instructions for the
// function body. Initializer expressions for package-level variables
// are emitted to the package's init() function in the order specified
// by go/types.Info.InitOrder, then code for each function in the
// package is generated in lexical order.
// The BUILD phases for distinct packages are independent and are
// executed in parallel.
// It is not necessary to call CreatePackage for all dependencies of
// each syntax package, only for its direct imports. (In future
// perhaps even this restriction may be lifted.)
//
// TODO(adonovan): indeed, building functions is now embarrassingly parallel.
// Audit for concurrency then benchmark using more goroutines.
// Second, packages created from syntax are built, by one or more
// calls to Package.Build, which may be concurrent; or by a call to
// Program.Build, which builds all packages in parallel. Building
// traverses the type-annotated syntax tree of each function body and
// creates SSA-form IR, a control-flow graph of instructions,
// populating fields such as Function.Body, .Params, and others.
//
// State:
// Building may create additional methods, including:
// - wrapper methods (e.g. for embeddding, or implicit &recv)
// - bound method closures (e.g. for use(recv.f))
// - thunks (e.g. for use(I.f) or use(T.f))
// - generic instances (e.g. to produce f[int] from f[any]).
// As these methods are created, they are added to the build queue,
// and then processed in turn, until a fixed point is reached,
// Since these methods might belong to packages that were not
// created (by a call to CreatePackage), their Pkg field is unset.
//
// The Package's and Program's indices (maps) are populated and
// mutated during the CREATE phase, but during the BUILD phase they
// remain constant. The sole exception is Prog.methodSets and its
// related maps, which are protected by a dedicated mutex.
// Instances of generic functions may be either instantiated (f[int]
// is a copy of f[T] with substitutions) or wrapped (f[int] delegates
// to f[T]), depending on the availability of generic syntax and the
// InstantiateGenerics mode flag.
//
// Generic functions declared in a package P can be instantiated from functions
// outside of P. This happens independently of the CREATE and BUILD phase of P.
// Each package has an initializer function named "init" that calls
// the initializer functions of each direct import, computes and
// assigns the initial value of each global variable, and calls each
// source-level function named "init". (These generate SSA functions
// named "init#1", "init#2", etc.)
//
// Synthetics:
// Runtime types
//
// During the BUILD phase new functions can be created and built. These include:
// - wrappers (wrappers, bounds, thunks)
// - generic function instantiations
// These functions do not belong to a specific Pkg (Pkg==nil). Instead the
// Package that led to them being CREATED is obligated to ensure these
// are BUILT during the BUILD phase of the Package.
// Each MakeInterface operation is a conversion from a non-interface
// type to an interface type. The semantics of this operation requires
// a runtime type descriptor, which is the type portion of an
// interface, and the value abstracted by reflect.Type.
//
// Runtime types:
// The program accumulates all non-parameterized types that are
// encountered as MakeInterface operands, along with all types that
// may be derived from them using reflection. This set is available as
// Program.RuntimeTypes, and the methods of these types may be
// reachable via interface calls or reflection even if they are never
// referenced from the SSA IR. (In practice, algorithms such as RTA
// that compute reachability from package main perform their own
// tracking of runtime types at a finer grain, so this feature is not
// very useful.)
//
// A concrete type is a type that is fully monomorphized with concrete types,
// i.e. it cannot reach a TypeParam type.
// Some concrete types require full runtime type information. Cases
// include checking whether a type implements an interface or
// interpretation by the reflect package. All such types that may require
// this information will have all of their method sets built and will be added to Prog.methodSets.
// A type T is considered to require runtime type information if it is
// a runtime type and has a non-empty method set and either:
// - T flows into a MakeInterface instructions,
// - T appears in a concrete exported member, or
// - T is a type reachable from a type S that has non-empty method set.
// For any such type T, method sets must be created before the BUILD
// phase of the package is done.
//
// Function literals:
//
// The BUILD phase of a function literal (anonymous function) is tied to the
// BUILD phase of the enclosing parent function. The FreeVars of an anonymous
// function are discovered by building the anonymous function. This in turn
// changes which variables must be bound in a MakeClosure instruction in the
// parent. Anonymous functions also track where they are referred to in their
// parent function.
//
// Create and build:
//
// Construction happens in two phases, "create" and "build".
// Package and Function data structures are created by CreatePackage.
// However, fields of Function such as Body, Params, and others are
// populated only during building, which happens later (or never).
//
// A complete Program is built (in parallel) by calling Program.Build,
// but individual packages may built by calling Package.Build.
//
// The Function.build fields determines the algorithm for building the
// function body. It is cleared to mark that building is complete.
// Function literals
//
// Anonymous functions must be built as soon as they are encountered,
// as it may affect locals of the enclosing function, but they are not
// marked 'built' until the end of the outermost enclosing function.
// (Among other things, this causes them to be logged in top-down order.)
//
// The {start,finish}Body functions must be called (in that order)
// around construction of the Body.
//
// Building a package may trigger the creation of new functions for
// wrapper methods and instantiations. The Package.Build operation
// will build these additional functions, and any that they in turn
// create, until it converges.
//
// Program.MethodValue may also trigger the creation of new functions,
// and it too must build iterately until it converges.
//
// Program.NewFunction:
//
// This is a low level operation for creating functions that do not exist in
// the source. Use with caution.
//
// TODO(taking): Use consistent terminology for "concrete".
// TODO(taking): Use consistent terminology for "monomorphization"/"instantiate"/"expand".
// The Function.build fields determines the algorithm for building the
// function body. It is cleared to mark that building is complete.

import (
"fmt"
Expand Down
5 changes: 2 additions & 3 deletions go/ssa/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func (c *creator) Add(fn *Function) {
func (c *creator) At(i int) *Function { return (*c)[i] }
func (c *creator) Len() int { return len(*c) }

// CreatePackage constructs and returns an SSA Package from the
// CreatePackage creates and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
Expand All @@ -221,7 +221,7 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
objects: make(map[types.Object]Member),
Pkg: pkg,
syntax: info != nil,
// transient values (CREATE and BUILD phases)
// transient values (cleared after Package.Build)
info: info,
files: files,
initVersion: make(map[ast.Expr]string),
Expand All @@ -241,7 +241,6 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
p.Members[p.init.name] = p.init
p.created.Add(p.init)

// CREATE phase.
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 {
// Go source package.
Expand Down
3 changes: 0 additions & 3 deletions go/ssa/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,6 @@
// The ssa/ssautil package provides various utilities that depend only
// on the public API of this package.
//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
// TODO(adonovan): write a how-to document for all the various cases
// of trying to determine corresponding elements across the four
// domains of source locations, ast.Nodes, types.Objects,
Expand Down
12 changes: 6 additions & 6 deletions go/ssa/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
)

// MethodValue returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// abstract (interface or parameterized) method.
// wrapper methods on demand. It returns nil if sel denotes an
// interface or generic method.
//
// Precondition: sel.Kind() == MethodVal.
//
Expand All @@ -29,11 +29,11 @@ func (prog *Program) MethodValue(sel *types.Selection) *Function {
}
T := sel.Recv()
if types.IsInterface(T) {
return nil // abstract method (interface, possibly type param)
return nil // interface method or type parameter
}

if prog.parameterized.isParameterized(T) {
return nil // abstract method (generic)
return nil // generic method
}

if prog.mode&LogSource != 0 {
Expand Down Expand Up @@ -97,7 +97,7 @@ func (prog *Program) objectMethod(obj *types.Func, cr *creator) *Function {

// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is abstract, and panics if T has no such method.
// is an interface method or generic method, and panics if T has no such method.
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
Expand Down Expand Up @@ -280,7 +280,7 @@ func forEachReachable(msets *typeutil.MethodSetCache, T types.Type, f func(types
}

case *typeparams.TypeParam, *typeparams.Union:
// Type parameters cannot be reached from ground types.
// forEachReachable must not be called on parameterized types.
panic(T)

default:
Expand Down
13 changes: 6 additions & 7 deletions go/ssa/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ type Node interface {
//
// A generic function is a function or method that has uninstantiated type
// parameters (TypeParams() != nil). Consider a hypothetical generic
// method, (*Map[K,V]).Get. It may be instantiated with all ground
// (non-parameterized) types as (*Map[string,int]).Get or with
// method, (*Map[K,V]).Get. It may be instantiated with all
// non-parameterized types as (*Map[string,int]).Get or with
// parameterized types as (*Map[string,U]).Get, where U is a type parameter.
// In both instantiations, Origin() refers to the instantiated generic
// method, (*Map[K,V]).Get, TypeParams() refers to the parameters [K,V] of
Expand Down Expand Up @@ -344,14 +344,13 @@ type Function struct {
topLevelOrigin *Function // the origin function if this is an instance of a source function. nil if Parent()!=nil.
generic *generic // instances of this function, if generic

// The following fields are set transiently during building,
// then cleared.
// The following fields are cleared after building.
currentBlock *BasicBlock // where to emit code
vars map[*types.Var]Value // addresses of local variables
namedResults []*Alloc // tuple of named results
targets *targets // linked stack of branch targets
lblocks map[*types.Label]*lblock // labelled blocks
subst *subster // non-nil => expand generic body using this type substitution of ground types
subst *subster // type parameter substitutions (if non-nil)
}

// BasicBlock represents an SSA basic block.
Expand Down Expand Up @@ -1400,7 +1399,7 @@ type anInstruction struct {
// represents a dynamically dispatched call to an interface method.
// In this mode, Value is the interface value and Method is the
// interface's abstract method. The interface value may be a type
// parameter. Note: an abstract method may be shared by multiple
// parameter. Note: an interface method may be shared by multiple
// interfaces due to embedding; Value.Type() provides the specific
// interface used for this call.
//
Expand All @@ -1418,7 +1417,7 @@ type anInstruction struct {
// the last element of Args is a slice.
type CallCommon struct {
Value Value // receiver (invoke mode) or func value (call mode)
Method *types.Func // abstract method (invoke mode)
Method *types.Func // interface method (invoke mode)
Args []Value // actual parameters (in static method call, includes receiver)
pos token.Pos // position of CallExpr.Lparen, iff explicit in source
}
Expand Down
2 changes: 1 addition & 1 deletion go/ssa/ssautil/visit.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool {

// Historically, Program.RuntimeTypes used to include the type
// of any exported member of a package loaded from syntax that
// has a non-parameterized (ground) type, plus all types
// has a non-parameterized type, plus all types
// reachable from that type using reflection, even though
// these runtime types may not be required for them.
//
Expand Down

0 comments on commit 7e0917a

Please sign in to comment.