--- /dev/null
+package ssa
+
+// This file defines the SSA builder.
+//
+// The builder has two phases, CREATE and BUILD. In the CREATE
+// phase, all packages are constructed and type-checked and
+// definitions of all package members are created, method-sets are
+// computed, and bridge methods are synthesized. The create phase
+// proceeds in topological order over the import dependency graph,
+// initiated by client calls to CreatePackage.
+//
+// In the BUILD phase, the Builder traverses the AST of each Go source
+// function and generates SSA instructions for the function body.
+// Within each package, building proceeds in a topological order over
+// the symbol reference graph, whose roots are the set of
+// package-level declarations in lexical order.
+//
+// In principle, the BUILD phases for each package can occur in
+// parallel, and that is our goal though there remains work to do.
+// Currently we ensure that all the imports of a package are fully
+// built before we start building it.
+//
+// The Builder's and Program's indices (maps) are populated and
+// mutated during the CREATE phase, but during the BUILD phase they
+// remain constant, with the following exceptions:
+// - demoteSelector mutates Builder.types during the BUILD phase.
+// TODO(adonovan): fix: let's not do that.
+// - globalValueSpec mutates Builder.nTo1Vars.
+// TODO(adonovan): make this a per-Package map so it's thread-safe.
+// - Program.methodSets is populated lazily across phases.
+// It uses a mutex so that access from multiple threads is serialized.
+
+// TODO(adonovan): fix the following:
+// - append, delete details.
+// - support f(g()) where g has multiple result parameters.
+// - finish emitCompare, emitArith.
+// - banish "untyped" types everywhere except package/universal constants?
+// - concurrent SSA code generation of multiple packages.
+// - consider function-local NamedTypes.
+// They can have nonempty method-sets due to promotion. Test.
+// - polish.
+// - tests.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "os"
+ "strconv"
+)
+
+var (
+ varOk = &types.Var{Name: "ok", Type: tBool}
+
+ // Type constants.
+ tBool = types.Typ[types.Bool]
+ tByte = types.Typ[types.Byte]
+ tFloat32 = types.Typ[types.Float32]
+ tFloat64 = types.Typ[types.Float64]
+ tInt = types.Typ[types.Int]
+ tInvalid = types.Typ[types.Invalid]
+ tUntypedNil = types.Typ[types.UntypedNil]
+ tRangeIter = &types.Basic{Name: "iter"} // the type of all "range" iterators
+
+ // The result type of a "select".
+ tSelect = &types.Result{Values: []*types.Var{
+ {Name: "index", Type: tInt},
+ {Name: "recv", Type: tInvalid},
+ varOk,
+ }}
+
+ // SSA Value constants.
+ vZero = intLiteral(0)
+ vOne = intLiteral(1)
+ vTrue = newLiteral(true, tBool)
+ vFalse = newLiteral(false, tBool)
+)
+
+// A Builder creates the SSA representation of a single program.
+// Instances may be created using NewBuilder.
+//
+// The SSA Builder constructs a Program containing Package instances
+// for packages of Go source code, loading, parsing and recursively
+// constructing packages for all imported dependencies as well.
+//
+// If the UseGCImporter mode flag is specified, binary object files
+// produced by the gc compiler will be loaded instead of source code
+// for all imported packages. Such files supply only the types of
+// package-level declarations and values of constants, but no code, so
+// this mode will not yield a whole program. It is intended for
+// analyses that perform intraprocedural analysis of a single package.
+//
+// A typical client will create a Builder with NewBuilder; call
+// CreatePackage for the "root" package(s), e.g. main; then call
+// BuildPackage on the same set of packages to construct SSA-form code
+// for functions and methods. After that, the representation of the
+// program (Builder.Prog) is complete and transitively closed, and the
+// Builder object can be discarded to reclaim its memory. The
+// client's analysis may then begin.
+//
+type Builder struct {
+ Prog *Program // the program being built
+ mode BuilderMode // set of mode bits
+ loader SourceLoader // the loader for imported source files
+ importErrs map[string]error // across-packages import cache of failures
+ packages map[*types.Package]*Package // SSA packages by types.Package
+ types map[ast.Expr]types.Type // inferred types of expressions
+ constants map[ast.Expr]*Literal // values of constant expressions
+ idents map[*ast.Ident]types.Object // canonical type objects of all named entities
+ globals map[types.Object]Value // all package-level funcs and vars, and universal built-ins
+ nTo1Vars map[*ast.ValueSpec]bool // set of n:1 ValueSpecs already built [not threadsafe]
+ typechecker types.Context // the typechecker context (stateless)
+}
+
+// BuilderMode is a bitmask of options for diagnostics and checking.
+type BuilderMode uint
+
+const (
+ LogPackages BuilderMode = 1 << iota // Dump package inventory to stderr
+ LogFunctions // Dump function SSA code to stderr
+ LogSource // Show source locations as SSA builder progresses
+ SanityCheckFunctions // Perform sanity checking of function bodies
+ UseGCImporter // Ignore SourceLoader; use gc-compiled object code for all imports
+)
+
+// NewBuilder creates and returns a new SSA builder.
+//
+// mode is a bitfield of options controlling verbosity, logging and
+// additional sanity checks.
+//
+// loader is a SourceLoader function that finds, loads and parses Go
+// source files for a given import path. (It is ignored if the mode
+// bits include UseGCImporter.)
+//
+// errh is an optional error handler that is called for each error
+// encountered during type checking; if nil, only the first type error
+// will be returned, via the result of CreatePackage.
+//
+func NewBuilder(mode BuilderMode, loader SourceLoader, errh func(error)) *Builder {
+ b := &Builder{
+ Prog: &Program{
+ Files: token.NewFileSet(),
+ Packages: make(map[string]*Package),
+ Builtins: make(map[types.Object]*Builtin),
+ methodSets: make(map[types.Type]MethodSet),
+ concreteMethods: make(map[*types.Method]*Function),
+ mode: mode,
+ },
+ mode: mode,
+ loader: loader,
+ constants: make(map[ast.Expr]*Literal),
+ globals: make(map[types.Object]Value),
+ idents: make(map[*ast.Ident]types.Object),
+ importErrs: make(map[string]error),
+ nTo1Vars: make(map[*ast.ValueSpec]bool),
+ packages: make(map[*types.Package]*Package),
+ types: make(map[ast.Expr]types.Type),
+ }
+
+ // TODO(adonovan): opt: record the Expr/Ident calls into
+ // constants/idents/types maps associated with the containing
+ // package so we can discard them once that package is built.
+ b.typechecker = types.Context{
+ // TODO(adonovan): permit the client to specify these
+ // values. Perhaps expose the types.Context parameter
+ // directly (though of course we'll have to override
+ // the Expr/Ident/Import callbacks).
+ IntSize: 8,
+ PtrSize: 8,
+ Error: errh,
+ Expr: func(x ast.Expr, typ types.Type, val interface{}) {
+ b.types[x] = typ
+ if val != nil {
+ b.constants[x] = newLiteral(val, typ)
+ }
+ },
+ Ident: func(ident *ast.Ident, obj types.Object) {
+ // Invariants:
+ // - obj is non-nil.
+ // - isBlankIdent(ident) <=> obj.GetType()==nil
+ b.idents[ident] = obj
+ },
+ Import: func(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ return b.doImport(imports, path)
+ },
+ }
+
+ // Create Values for built-in functions.
+ for _, obj := range types.Universe.Entries {
+ switch obj := obj.(type) {
+ case *types.Func:
+ v := &Builtin{obj}
+ b.globals[obj] = v
+ b.Prog.Builtins[obj] = v
+ }
+ }
+ return b
+}
+
+// isPackageRef returns the identity of the object if sel is a
+// package-qualified reference to a named const, var, func or type.
+// Otherwise it returns nil.
+//
+// It's unfortunate that this is a method of builder, but even the
+// small amount of name resolution required here is no longer
+// available on the AST.
+//
+func (b *Builder) isPackageRef(sel *ast.SelectorExpr) types.Object {
+ if id, ok := sel.X.(*ast.Ident); ok {
+ if obj := b.obj(id); objKind(obj) == ast.Pkg {
+ return obj.(*types.Package).Scope.Lookup(sel.Sel.Name)
+ }
+ }
+ return nil
+}
+
+// isType returns true iff expression e denotes a type.
+//
+// It's unfortunate that this is a method of builder, but even the
+// small amount of name resolution required here is no longer
+// available on the AST.
+//
+func (b *Builder) isType(e ast.Expr) bool {
+ switch e := e.(type) {
+ case *ast.SelectorExpr: // pkg.Type
+ if obj := b.isPackageRef(e); obj != nil {
+ return objKind(obj) == ast.Typ
+ }
+ case *ast.StarExpr: // *T
+ return b.isType(e.X)
+ case *ast.Ident:
+ return objKind(b.obj(e)) == ast.Typ
+ case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
+ return true
+ case *ast.ParenExpr:
+ return b.isType(e.X)
+ }
+ return false
+}
+
+// lookup returns the package-level *Function or *Global (or universal
+// *Builtin) for the named object obj, causing its initialization code
+// to be emitted into v.Package.Init if not already done.
+//
+func (b *Builder) lookup(obj types.Object) (v Value, ok bool) {
+ v, ok = b.globals[obj]
+ if ok {
+ // TODO(adonovan): the build phase should only
+ // propagate to v if it's in the same package as the
+ // caller of lookup if we want to make this
+ // concurrent.
+ switch v := v.(type) {
+ case *Function:
+ b.buildFunction(v)
+ case *Global:
+ b.buildGlobal(v, obj)
+ }
+ }
+ return
+}
+
+// exprType(e) returns the type of expression e.
+// Callers should not access b.types directly since it may lie if
+// called from within a typeswitch.
+//
+func (b *Builder) exprType(e ast.Expr) types.Type {
+ // For Ident, b.types may be more specific than
+ // b.obj(id.(*ast.Ident)).GetType(),
+ // e.g. in the case of typeswitch.
+ if t, ok := b.types[e]; ok {
+ return t
+ }
+ // The typechecker doesn't notify us of all Idents,
+ // e.g. s.Key and s.Value in a RangeStmt.
+ // So we have this fallback.
+ // TODO(gri): Is this a typechecker bug? If so, eliminate
+ // this case and panic.
+ if id, ok := e.(*ast.Ident); ok {
+ return b.obj(id).GetType()
+ }
+ panic("no type for expression")
+}
+
+// obj returns the typechecker object denoted by the specified
+// identifier. Panic ensues if there is none.
+//
+func (b *Builder) obj(id *ast.Ident) types.Object {
+ if obj, ok := b.idents[id]; ok {
+ return obj
+ }
+ panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %p", id.Name, id))
+}
+
+// cond emits to fn code to evaluate boolean condition e and jump
+// to t or f depending on its value, performing various simplifications.
+//
+// Postcondition: fn.currentBlock is nil.
+//
+func (b *Builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) {
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ b.cond(fn, e.X, t, f)
+ return
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND:
+ ltrue := fn.newBasicBlock("cond.true")
+ b.cond(fn, e.X, ltrue, f)
+ fn.currentBlock = ltrue
+ b.cond(fn, e.Y, t, f)
+ return
+
+ case token.LOR:
+ lfalse := fn.newBasicBlock("cond.false")
+ b.cond(fn, e.X, t, lfalse)
+ fn.currentBlock = lfalse
+ b.cond(fn, e.Y, t, f)
+ return
+ }
+
+ case *ast.UnaryExpr:
+ if e.Op == token.NOT {
+ b.cond(fn, e.X, f, t)
+ return
+ }
+ }
+
+ switch cond := b.expr(fn, e).(type) {
+ case *Literal:
+ // Dispatch constant conditions statically.
+ if cond.Value.(bool) {
+ emitJump(fn, t)
+ } else {
+ emitJump(fn, f)
+ }
+ default:
+ emitIf(fn, cond, t, f)
+ }
+}
+
+// logicalBinop emits code to fn to evaluate e, a &&- or
+// ||-expression whose reified boolean value is wanted.
+// The value is returned.
+//
+func (b *Builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
+ rhs := fn.newBasicBlock("binop.rhs")
+ done := fn.newBasicBlock("binop.done")
+
+ var short Value // value of the short-circuit path
+ switch e.Op {
+ case token.LAND:
+ b.cond(fn, e.X, rhs, done)
+ short = vFalse
+ case token.LOR:
+ b.cond(fn, e.X, done, rhs)
+ short = vTrue
+ }
+
+ // Is rhs unreachable?
+ if rhs.Preds == nil {
+ // Simplify false&&y to false, true||y to true.
+ fn.currentBlock = done
+ return short
+ }
+
+ // Is done unreachable?
+ if done.Preds == nil {
+ // Simplify true&&y (or false||y) to y.
+ fn.currentBlock = rhs
+ return b.expr(fn, e.Y)
+ }
+
+ // All edges from e.X to done carry the short-circuit value.
+ var edges []Value
+ for _ = range done.Preds {
+ edges = append(edges, short)
+ }
+
+ // The edge from e.Y to done carries the value of e.Y.
+ fn.currentBlock = rhs
+ edges = append(edges, b.expr(fn, e.Y))
+ emitJump(fn, done)
+ fn.currentBlock = done
+
+ // TODO(adonovan): do we need emitConv on each edge?
+ // Test with named boolean types.
+ phi := &Phi{Edges: edges}
+ phi.Type_ = phi.Edges[0].Type()
+ return done.emit(phi)
+}
+
+// exprN lowers a multi-result expression e to SSA form, emitting code
+// to fn and returning a single Value whose type is a *types.Results
+// (tuple). The caller must access the components via Extract.
+//
+// Multi-result expressions include CallExprs in a multi-value
+// assignment or return statement, and "value,ok" uses of
+// TypeAssertExpr, IndexExpr (when X is a map), and UnaryExpr (when Op
+// is token.ARROW).
+//
+func (b *Builder) exprN(fn *Function, e ast.Expr) Value {
+ var typ types.Type
+ var tuple Value
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ return b.exprN(fn, e.X)
+
+ case *ast.CallExpr:
+ // Currently, no built-in function nor type conversion
+ // has multiple results, so we can avoid some of the
+ // cases for single-valued CallExpr.
+ var c Call
+ b.setCall(fn, e, &c.CallCommon)
+ c.Type_ = b.exprType(e)
+ return fn.emit(&c)
+
+ case *ast.IndexExpr:
+ mapt := underlyingType(b.exprType(e.X)).(*types.Map)
+ typ = mapt.Elt
+ tuple = fn.emit(&Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key),
+ CommaOk: true,
+ })
+
+ case *ast.TypeAssertExpr:
+ typ = b.exprType(e)
+ tuple = fn.emit(&TypeAssert{
+ X: b.expr(fn, e.X),
+ AssertedType: typ,
+ CommaOk: true,
+ })
+
+ case *ast.UnaryExpr: // must be receive <-
+ typ = underlyingType(b.exprType(e.X)).(*types.Chan).Elt
+ tuple = fn.emit(&UnOp{
+ Op: token.ARROW,
+ X: b.expr(fn, e.X),
+ CommaOk: true,
+ })
+
+ default:
+ panic(fmt.Sprintf("unexpected exprN: %T", e))
+ }
+
+ // The typechecker sets the type of the expression to just the
+ // asserted type in the "value, ok" form, not to *types.Result
+ // (though it includes the valueOk operand in its error messages).
+
+ tuple.(interface {
+ setType(types.Type)
+ }).setType(&types.Result{Values: []*types.Var{
+ {Name: "value", Type: typ},
+ varOk,
+ }})
+ return tuple
+}
+
+// builtin emits to fn SSA instructions to implement a call to the
+// built-in function called name with the specified arguments
+// and return type. It returns the value defined by the result.
+//
+// The result is nil if no special handling was required; in this case
+// the caller should treat this like an ordinary library function
+// call.
+//
+func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.Type) Value {
+ switch name {
+ case "make":
+ switch underlyingType(typ).(type) {
+ case *types.Slice:
+ n := b.expr(fn, args[1])
+ m := n
+ if len(args) == 3 {
+ m = b.expr(fn, args[2])
+ }
+ v := &MakeSlice{
+ Len: n,
+ Cap: m,
+ }
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Map:
+ var res Value
+ if len(args) == 2 {
+ res = b.expr(fn, args[1])
+ }
+ v := &MakeMap{Reserve: res}
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Chan:
+ var sz Value = vZero
+ if len(args) == 2 {
+ sz = b.expr(fn, args[1])
+ }
+ v := &MakeChan{Size: sz}
+ v.setType(typ)
+ return fn.emit(v)
+ }
+
+ case "new":
+ return emitNew(fn, indirectType(underlyingType(typ)))
+
+ case "len", "cap":
+ // Special case: len or cap of an array or *array is
+ // based on the type, not the value which may be nil.
+ // We must still evaluate the value, though. (If it
+ // was side-effect free, the whole call would have
+ // been constant-folded.)
+ t := underlyingType(deref(b.exprType(args[0])))
+ if at, ok := t.(*types.Array); ok {
+ b.expr(fn, args[0]) // for effects only
+ return intLiteral(at.Len)
+ }
+ // Otherwise treat as normal.
+ }
+ return nil // treat all others as a regular function call
+}
+
+// demoteSelector returns a SelectorExpr syntax tree that is
+// equivalent to sel but contains no selections of promoted fields.
+// It returns the field index of the explicit (=outermost) selection.
+//
+// pkg is the package in which the reference occurs. This is
+// significant because a non-exported field is considered distinct
+// from a field of that name in any other package.
+//
+// This is a rather clunky and inefficient implementation, but it
+// (a) is simple and hopefully self-evidently correct and
+// (b) permits us to decouple the demotion from the code generation,
+// the latter being performed in two modes: addr() for lvalues,
+// expr() for rvalues.
+// It does require mutation of Builder.types though; if we want to
+// make the Builder concurrent we'll have to avoid that.
+// TODO(adonovan): emit code directly rather than desugaring the AST.
+//
+func (b *Builder) demoteSelector(sel *ast.SelectorExpr, pkg *Package) (sel2 *ast.SelectorExpr, index int) {
+ id := makeId(sel.Sel.Name, pkg.Types)
+ xtype := b.exprType(sel.X)
+ // fmt.Fprintln(os.Stderr, xtype, id) // debugging
+ st := underlyingType(deref(xtype)).(*types.Struct)
+ for i, f := range st.Fields {
+ if IdFromQualifiedName(f.QualifiedName) == id {
+ return sel, i
+ }
+ }
+ // Not a named field. Use breadth-first algorithm.
+ path, index := findPromotedField(st, id)
+ if path == nil {
+ panic("field not found, even with promotion: " + sel.Sel.Name)
+ }
+
+ // makeSelector(e, [C,B,A]) returns (((e.A).B).C).
+ // e is the original selector's base.
+ // This function has no free variables.
+ var makeSelector func(b *Builder, e ast.Expr, path *anonFieldPath) *ast.SelectorExpr
+ makeSelector = func(b *Builder, e ast.Expr, path *anonFieldPath) *ast.SelectorExpr {
+ x := e
+ if path.tail != nil {
+ x = makeSelector(b, e, path.tail)
+ }
+ sel := &ast.SelectorExpr{
+ X: x,
+ Sel: &ast.Ident{Name: path.field.Name},
+ }
+ b.types[sel] = path.field.Type // TODO(adonovan): fix: not thread-safe
+ return sel
+ }
+
+ // Construct new SelectorExpr, bottom up.
+ sel2 = &ast.SelectorExpr{
+ X: makeSelector(b, sel.X, path),
+ Sel: sel.Sel,
+ }
+ b.types[sel2] = b.exprType(sel) // TODO(adonovan): fix: not thread-safe
+ return
+}
+
+// addr lowers a single-result addressable expression e to SSA form,
+// emitting code to fn and returning the location (an lvalue) defined
+// by the expression.
+//
+// If escaping is true, addr marks the base variable of the
+// addressable expression e as being a potentially escaping pointer
+// value. For example, in this code:
+//
+// a := A{
+// b: [1]B{B{c: 1}}
+// }
+// return &a.b[0].c
+//
+// the application of & causes a.b[0].c to have its address taken,
+// which means that ultimately the local variable a must be
+// heap-allocated. This is a simple but very conservative escape
+// analysis.
+//
+// Operations forming potentially escaping pointers include:
+// - &x
+// - a[:] iff a is an array (not *array)
+// - references to variables in lexically enclosing functions.
+//
+func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
+ switch e := e.(type) {
+ case *ast.Ident:
+ obj := b.obj(e)
+ v, ok := b.lookup(obj) // var (address)
+ if !ok {
+ v = fn.lookup(obj, escaping)
+ }
+ return address{v}
+
+ case *ast.CompositeLit:
+ t := deref(b.exprType(e))
+ var v Value
+ if escaping {
+ v = emitNew(fn, t)
+ } else {
+ v = fn.addLocal(t)
+ }
+ b.compLit(fn, v, e, t) // initialize in place
+ return address{v}
+
+ case *ast.ParenExpr:
+ return b.addr(fn, e.X, escaping)
+
+ case *ast.SelectorExpr:
+ // p.M where p is a package.
+ if obj := b.isPackageRef(e); obj != nil {
+ if v, ok := b.lookup(obj); ok {
+ return address{v}
+ }
+ panic("undefined package-qualified name: " + obj.GetName())
+ }
+
+ // e.f where e is an expression.
+ e, index := b.demoteSelector(e, fn.Pkg)
+ var x Value
+ switch underlyingType(b.exprType(e.X)).(type) {
+ case *types.Struct:
+ x = b.addr(fn, e.X, escaping).(address).addr
+ case *types.Pointer:
+ x = b.expr(fn, e.X)
+ }
+ v := &FieldAddr{
+ X: x,
+ Field: index,
+ }
+ v.setType(pointer(b.exprType(e)))
+ return address{fn.emit(v)}
+
+ case *ast.IndexExpr:
+ var x Value
+ var et types.Type
+ switch t := underlyingType(b.exprType(e.X)).(type) {
+ case *types.Array:
+ x = b.addr(fn, e.X, escaping).(address).addr
+ et = pointer(t.Elt)
+ case *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ et = pointer(underlyingType(t.Base).(*types.Array).Elt)
+ case *types.Slice:
+ x = b.expr(fn, e.X)
+ et = pointer(t.Elt)
+ case *types.Map:
+ return &element{
+ m: b.expr(fn, e.X),
+ k: emitConv(fn, b.expr(fn, e.Index), t.Key),
+ t: t.Elt,
+ }
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+ v := &IndexAddr{
+ X: x,
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setType(et)
+ return address{fn.emit(v)}
+
+ case *ast.StarExpr:
+ return address{b.expr(fn, e.X)}
+ }
+
+ panic(fmt.Sprintf("unexpected address expression: %T", e))
+}
+
+// exprInPlace emits to fn code to initialize the lvalue loc with the
+// value of expression e.
+//
+// typ is the type of the lvalue, which may be provided by the caller
+// since it is sometimes only an inherited attribute (e.g. within in
+// composite literals).
+//
+// This is equivalent to loc.store(fn, b.expr(fn, e)) but may
+// generate better code in some cases, e.g. for composite literals
+// in an addressable location.
+//
+func (b *Builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
+ if addr, ok := loc.(address); ok {
+ if e, ok := e.(*ast.CompositeLit); ok {
+ typ := addr.typ()
+ switch underlyingType(typ).(type) {
+ case *types.Pointer: // implicit & -- possibly escaping
+ ptr := b.addr(fn, e, true).(address).addr
+ addr.store(fn, ptr) // copy address
+ return
+
+ case *types.Interface:
+ // e.g. var x interface{} = T{...}
+ // Can't in-place initialize an interface value.
+ // Fall back to copying.
+
+ default:
+ b.compLit(fn, addr.addr, e, typ) // in place
+ return
+ }
+ }
+ }
+ loc.store(fn, b.expr(fn, e)) // copy value
+}
+
+// expr lowers a single-result expression e to SSA form, emitting code
+// to fn and returning the Value defined by the expression.
+//
+func (b *Builder) expr(fn *Function, e ast.Expr) Value {
+ if lit := b.constants[e]; lit != nil {
+ return lit
+ }
+
+ switch e := e.(type) {
+ case *ast.BasicLit:
+ panic("non-constant BasicLit") // unreachable
+
+ case *ast.FuncLit:
+ posn := b.Prog.Files.Position(e.Type.Func)
+ fn2 := &Function{
+ Name_: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column),
+ Signature: underlyingType(b.exprType(e.Type)).(*types.Signature),
+ Pos: e.Type.Func,
+ Enclosing: fn,
+ Pkg: fn.Pkg,
+ Prog: b.Prog,
+ syntax: &funcSyntax{
+ paramFields: e.Type.Params,
+ resultFields: e.Type.Results,
+ body: e.Body,
+ },
+ }
+ fn.Pkg.AnonFuncs = append(fn.Pkg.AnonFuncs, fn2)
+ b.buildFunction(fn2)
+ if fn2.FreeVars == nil {
+ return fn2
+ }
+ v := &MakeClosure{Fn: fn2}
+ v.setType(b.exprType(e))
+ for _, fv := range fn2.FreeVars {
+ v.Bindings = append(v.Bindings, fv.Outer)
+ }
+ return fn.emit(v)
+
+ case *ast.ParenExpr:
+ return b.expr(fn, e.X)
+
+ case *ast.TypeAssertExpr: // single-result form only
+ v := &TypeAssert{
+ X: b.expr(fn, e.X),
+ AssertedType: b.exprType(e),
+ }
+ v.setType(v.AssertedType)
+ return fn.emit(v)
+
+ case *ast.CallExpr:
+ typ := b.exprType(e)
+ if b.isType(e.Fun) {
+ // Type conversion, e.g. string(x) or big.Int(x)
+ return emitConv(fn, b.expr(fn, e.Args[0]), typ)
+ }
+ // Call to "intrinsic" built-ins, e.g. new, make.
+ wasPanic := false
+ if id, ok := e.Fun.(*ast.Ident); ok {
+ obj := b.obj(id)
+ if _, ok := fn.Prog.Builtins[obj]; ok {
+ if v := b.builtin(fn, id.Name, e.Args, typ); v != nil {
+ return v
+ }
+ wasPanic = id.Name == "panic"
+ }
+ }
+ // Regular function call.
+ var v Call
+ b.setCall(fn, e, &v.CallCommon)
+ v.setType(typ)
+ fn.emit(&v)
+
+ // Compile panic as if followed by for{} so that its
+ // successor is unreachable.
+ // TODO(adonovan): consider a dedicated Panic instruction
+ // (in which case, don't forget Go and Defer).
+ if wasPanic {
+ emitSelfLoop(fn)
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ }
+ return &v
+
+ case *ast.UnaryExpr:
+ switch e.Op {
+ case token.AND: // &X --- potentially escaping.
+ return b.addr(fn, e.X, true).(address).addr
+ case token.ADD:
+ return b.expr(fn, e.X)
+ case token.NOT, token.ARROW, token.SUB, token.XOR: // ! <- - ^
+ v := &UnOp{
+ Op: e.Op,
+ X: b.expr(fn, e.X),
+ }
+ v.setType(b.exprType(e))
+ return fn.emit(v)
+ default:
+ panic(e.Op)
+ }
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND, token.LOR:
+ return b.logicalBinop(fn, e)
+ case token.SHL, token.SHR:
+ fallthrough
+ case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), b.exprType(e))
+
+ case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
+ return emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y))
+ default:
+ panic("illegal op in BinaryExpr: " + e.Op.String())
+ }
+
+ case *ast.SliceExpr:
+ var low, high Value
+ var x Value
+ switch underlyingType(b.exprType(e.X)).(type) {
+ case *types.Array:
+ // Potentially escaping.
+ x = b.addr(fn, e.X, true).(address).addr
+ case *types.Basic, *types.Slice, *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ default:
+ unreachable()
+ }
+ if e.High != nil {
+ high = b.expr(fn, e.High)
+ }
+ if e.Low != nil {
+ low = b.expr(fn, e.Low)
+ }
+ v := &Slice{
+ X: x,
+ Low: low,
+ High: high,
+ }
+ v.setType(b.exprType(e))
+ return fn.emit(v)
+
+ case *ast.Ident:
+ obj := b.obj(e)
+ // Global or universal?
+ if v, ok := b.lookup(obj); ok {
+ if objKind(obj) == ast.Var {
+ v = emitLoad(fn, v) // var (address)
+ }
+ return v
+ }
+ // Local?
+ return emitLoad(fn, fn.lookup(obj, false)) // var (address)
+
+ case *ast.SelectorExpr:
+ // p.M where p is a package.
+ if obj := b.isPackageRef(e); obj != nil {
+ return b.expr(fn, e.Sel)
+ }
+
+ // (*T).f or T.f, the method f from the method-set of type T.
+ if b.isType(e.X) {
+ id := makeId(e.Sel.Name, fn.Pkg.Types)
+ typ := b.exprType(e.X)
+ if m := b.Prog.MethodSet(typ)[id]; m != nil {
+ return m
+ }
+
+ // T must be an interface; return method thunk.
+ return makeImethodThunk(b.Prog, typ, id)
+ }
+
+ // e.f where e is an expression.
+ e, index := b.demoteSelector(e, fn.Pkg)
+ switch underlyingType(b.exprType(e.X)).(type) {
+ case *types.Struct:
+ // Non-addressable struct in a register.
+ v := &Field{
+ X: b.expr(fn, e.X),
+ Field: index,
+ }
+ v.setType(b.exprType(e))
+ return fn.emit(v)
+
+ case *types.Pointer: // *struct
+ // Addressable structs; use FieldAddr and Load.
+ return b.addr(fn, e, false).load(fn)
+ }
+
+ case *ast.IndexExpr:
+ switch t := underlyingType(b.exprType(e.X)).(type) {
+ case *types.Array:
+ // Non-addressable array (in a register).
+ v := &Index{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setType(t.Elt)
+ return fn.emit(v)
+
+ case *types.Map:
+ // Maps are not addressable.
+ mapt := underlyingType(b.exprType(e.X)).(*types.Map)
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key),
+ }
+ v.setType(mapt.Elt)
+ return fn.emit(v)
+
+ case *types.Basic: // => string
+ // Strings are not addressable.
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: b.expr(fn, e.Index),
+ }
+ v.setType(tByte)
+ return fn.emit(v)
+
+ case *types.Slice, *types.Pointer: // *array
+ // Addressable slice/array; use IndexAddr and Load.
+ return b.addr(fn, e, false).load(fn)
+
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+
+ case *ast.CompositeLit, *ast.StarExpr:
+ // Addressable types (lvalues)
+ return b.addr(fn, e, false).load(fn)
+ }
+
+ panic(fmt.Sprintf("unexpected expr: %T", e))
+}
+
+// stmtList emits to fn code for all statements in list.
+func (b *Builder) stmtList(fn *Function, list []ast.Stmt) {
+ for _, s := range list {
+ b.stmt(fn, s)
+ }
+}
+
+// setCallFunc populates the function parts of a CallCommon structure
+// (Func, Method, Recv, Args[0]) based on the kind of invocation
+// occurring in e.
+//
+func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ c.Pos = e.Lparen
+
+ // Is the call of the form x.f()?
+ sel, ok := noparens(e.Fun).(*ast.SelectorExpr)
+
+ // Case 0: e.Fun evaluates normally to a function.
+ if !ok {
+ c.Func = b.expr(fn, e.Fun)
+ return
+ }
+
+ // Case 1: call of form x.F() where x is a package name.
+ if obj := b.isPackageRef(sel); obj != nil {
+ // This is a specialization of expr(ast.Ident(obj)).
+ if v, ok := b.lookup(obj); ok {
+ if _, ok := v.(*Function); !ok {
+ v = emitLoad(fn, v) // var (address)
+ }
+ c.Func = v
+ return
+ }
+ panic("undefined package-qualified name: " + obj.GetName())
+ }
+
+ // Case 2a: X.f() or (*X).f(): a statically dipatched call to
+ // the method f in the method-set of X or *X. X may be
+ // an interface. Treat like case 0.
+ // TODO(adonovan): inline expr() here, to make the call static
+ // and to avoid generation of a stub for an interface method.
+ if b.isType(sel.X) {
+ c.Func = b.expr(fn, e.Fun)
+ return
+ }
+
+ // Let X be the type of x.
+ typ := b.exprType(sel.X)
+
+ // Case 2: x.f(): a statically dispatched call to a method
+ // from the method-set of X or perhaps *X (if x is addressable
+ // but not a pointer).
+ id := makeId(sel.Sel.Name, fn.Pkg.Types)
+ // Consult method-set of X.
+ if m := b.Prog.MethodSet(typ)[id]; m != nil {
+ var recv Value
+ aptr := isPointer(typ)
+ fptr := isPointer(m.Signature.Recv.Type)
+ if aptr == fptr {
+ // Actual's and formal's "pointerness" match.
+ recv = b.expr(fn, sel.X)
+ } else {
+ // Actual is a pointer, formal is not.
+ // Load a copy.
+ recv = emitLoad(fn, b.expr(fn, sel.X))
+ }
+ c.Func = m
+ c.Args = append(c.Args, recv)
+ return
+ }
+ if !isPointer(typ) {
+ // Consult method-set of *X.
+ if m := b.Prog.MethodSet(pointer(typ))[id]; m != nil {
+ // A method found only in MS(*X) must have a
+ // pointer formal receiver; but the actual
+ // value is not a pointer.
+ // Implicit & -- possibly escaping.
+ recv := b.addr(fn, sel.X, true).(address).addr
+ c.Func = m
+ c.Args = append(c.Args, recv)
+ return
+ }
+ }
+
+ switch t := underlyingType(typ).(type) {
+ case *types.Struct, *types.Pointer:
+ // Case 3: x.f() where x.f is a function value in a
+ // struct field f; not a method call. f is a 'var'
+ // (of function type) in the Fields of types.Struct X.
+ // Treat like case 0.
+ c.Func = b.expr(fn, e.Fun)
+
+ case *types.Interface:
+ // Case 4: x.f() where a dynamically dispatched call
+ // to an interface method f. f is a 'func' object in
+ // the Methods of types.Interface X
+ c.Method, _ = methodIndex(t, t.Methods, id)
+ c.Recv = b.expr(fn, sel.X)
+
+ default:
+ panic(fmt.Sprintf("illegal (%s).%s() call; X:%T", t, sel.Sel.Name, sel.X))
+ }
+}
+
+// setCall emits to fn code to evaluate all the parameters of a function
+// call e, and populates *c with those values.
+//
+func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ // First deal with the f(...) part.
+ b.setCallFunc(fn, e, c)
+
+ // Argument passing. There are 4 cases to consider:
+ // 1. Ordinary call to non-variadic function.
+ // All args are treated in the usual manner.
+ // 2. Ordinary call to variadic function, f(a, b, v1, ..., vn).
+ // Caller constructs a slice from the varargs arguments v_i.
+ // 3. Ellipsis call f(a, b, rest...) to variadic function.
+ // 'rest' is already a slice; all args treated in the usual manner.
+ // 4. f(g()) where g has >1 return parameters. f may also be variadic.
+ // TODO(adonovan): implement.
+
+ var args, varargs []ast.Expr = e.Args, nil
+ c.HasEllipsis = e.Ellipsis != 0
+
+ // Determine which suffix (if any) of Args is a varargs slice.
+ var vt types.Type // element type of the variadic slice
+ switch typ := underlyingType(b.exprType(e.Fun)).(type) {
+ case *types.Signature:
+ np := len(typ.Params)
+ if !c.HasEllipsis {
+ if typ.IsVariadic && len(args) > np-1 {
+ // case 2: ordinary call of variadic function.
+ vt = typ.Params[np-1].Type
+ args, varargs = args[:np-1], args[np-1:]
+ }
+
+ // TODO(adonovan): fix: not disjoint with case above!
+ // Consider f(...int) called with g() (int,int)
+ // Incomplete.
+ if len(args) == 1 && (np > 1 || typ.IsVariadic) {
+ if res, ok := b.exprType(args[0]).(*types.Result); ok {
+ res = res
+ // case 4: f(g()) where g is a multi.
+ // TODO(adonovan): generate:
+ // tuple := g()
+ // args = [extract 0, ... extract n]
+ // f(args)
+ }
+ }
+ }
+
+ // Non-varargs.
+ for i, arg := range args {
+ // TODO(gri): annoyingly Signature.Params
+ // doesn't reflect the slice type for a final
+ // ...T param.
+ t := typ.Params[i].Type
+ if typ.IsVariadic && c.HasEllipsis && i == len(args)-1 {
+ t = &types.Slice{Elt: t}
+ }
+ v := emitConv(fn, b.expr(fn, arg), t)
+ c.Args = append(c.Args, v)
+ }
+
+ default:
+ // builtin: ad-hoc typing rules are required for all
+ // variadic (append, print, println) and polymorphic
+ // (append, copy, delete, close) built-ins.
+ //
+ // TODO(adonovan): it would so much cleaner if the
+ // typechecker would ascribe a unique Signature type
+ // to each e.Fun expression calling a built-in.
+ // Then we could use the same logic as above.
+ //
+ // TODO(adonovan): fix: support case 4. But know that the
+ // other tools don't support it for built-ins yet
+ // (http://code.google.com/p/go/issues/detail?id=4573),
+ // so there's no rush.
+
+ var bptypes []types.Type // formal parameter types of builtins
+ switch builtin := e.Fun.(*ast.Ident).Name; builtin {
+ case "append":
+ // append([]T, ...T) []T
+ // append([]byte, string...) []byte // TODO(adonovan): fix: support.
+ // Infer arg types from result type:
+ rt := b.exprType(e)
+ vt = underlyingType(rt).(*types.Slice).Elt // variadic
+ if !c.HasEllipsis {
+ args, varargs = args[:1], args[1:]
+ }
+ bptypes = append(bptypes, rt)
+ case "close":
+ bptypes = append(bptypes, nil) // no conv
+ case "copy":
+ // copy([]T, []T) int
+ // Infer arg types from each other. Sleazy.
+ if st, ok := underlyingType(b.exprType(args[0])).(*types.Slice); ok {
+ bptypes = append(bptypes, st, st)
+ } else if st, ok := underlyingType(b.exprType(args[1])).(*types.Slice); ok {
+ bptypes = append(bptypes, st, st)
+ } else {
+ panic("cannot infer types in call to copy()")
+ }
+ case "delete":
+ // delete(map[K]V, K)
+ // TODO(adonovan): fix: this is incorrect.
+ bptypes = append(bptypes, nil) // map
+ bptypes = append(bptypes, nil) // key
+ case "print", "println": // print{,ln}(any, ...any)
+ vt = new(types.Interface) // variadic
+ if !c.HasEllipsis {
+ args, varargs = args[:1], args[1:]
+ }
+ case "len":
+ bptypes = append(bptypes, nil) // no conv
+ case "cap":
+ bptypes = append(bptypes, nil) // no conv
+ case "real", "imag":
+ // TODO(adonovan): fix: apply reverse conversion
+ // to "complex" case below.
+ bptypes = append(bptypes, nil)
+ case "complex":
+ // Typechecker, help us out. :(
+ var argType types.Type
+ switch b.exprType(e).(*types.Basic).Kind {
+ case types.UntypedComplex:
+ argType = types.Typ[types.UntypedFloat]
+ case types.Complex128:
+ argType = tFloat64
+ case types.Complex64:
+ argType = tFloat32
+ default:
+ unreachable()
+ }
+ bptypes = append(bptypes, argType, argType)
+ case "panic":
+ bptypes = append(bptypes, new(types.Interface))
+ case "recover":
+ // no-op
+ default:
+ panic("unknown builtin: " + builtin)
+ }
+
+ // Non-varargs.
+ for i, arg := range args {
+ v := b.expr(fn, arg)
+ if i < len(bptypes) && bptypes[i] != nil {
+ v = emitConv(fn, v, bptypes[i])
+ }
+ c.Args = append(c.Args, v)
+ }
+ }
+
+ // Common code for varargs.
+ if len(varargs) > 0 { // case 2
+ at := &types.Array{
+ Elt: vt,
+ Len: int64(len(varargs)),
+ }
+ a := emitNew(fn, at)
+ for i, arg := range varargs {
+ iaddr := &IndexAddr{
+ X: a,
+ Index: intLiteral(int64(i)),
+ }
+ iaddr.setType(pointer(vt))
+ fn.emit(iaddr)
+ emitStore(fn, iaddr, b.expr(fn, arg))
+ }
+ s := &Slice{X: a}
+ s.setType(&types.Slice{Elt: vt})
+ c.Args = append(c.Args, fn.emit(s))
+ }
+}
+
+// assignOp emits to fn code to perform loc += incr or loc -= incr.
+func (b *Builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token) {
+ oldv := loc.load(fn)
+ loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ()))
+}
+
+// buildGlobal emits code to the g.Pkg.Init function for the variable
+// definition(s) of g. Effects occur out of lexical order; see
+// explanation at globalValueSpec.
+// Precondition: g == b.globals[obj]
+//
+func (b *Builder) buildGlobal(g *Global, obj types.Object) {
+ spec := g.spec
+ if spec == nil {
+ return // already built (or in progress)
+ }
+ b.globalValueSpec(g.Pkg.Init, spec, g, obj)
+}
+
+// globalValueSpec emits to init code to define one or all of the vars
+// in the package-level ValueSpec spec.
+//
+// It implements the build phase for a ValueSpec, ensuring that all
+// vars are initialized if not already visited by buildGlobal during
+// the reference graph traversal.
+//
+// This function may be called in two modes:
+// A) with g and obj non-nil, to initialize just a single global.
+// This occurs during the reference graph traversal.
+// B) with g and obj nil, to initialize all globals in the same ValueSpec.
+// This occurs during the left-to-right traversal over the ast.File.
+//
+// Precondition: g == b.globals[obj]
+//
+// Package-level var initialization order is quite subtle.
+// The side effects of:
+// var a, b = f(), g()
+// are not observed left-to-right if b is referenced before a in the
+// reference graph traversal. So, we track which Globals have been
+// initialized by setting Global.spec=nil.
+//
+// Blank identifiers make things more complex since they don't have
+// associated types.Objects or ssa.Globals yet we must still ensure
+// that their corresponding side effects are observed at the right
+// moment. Consider:
+// var a, _, b = f(), g(), h()
+// Here, the relative ordering of the call to g() is unspecified but
+// it must occur exactly once, during mode B. So globalValueSpec for
+// blanks must special-case n:n assigments and just evaluate the RHS
+// g() for effect.
+//
+// In a n:1 assignment:
+// var a, _, b = f()
+// a reference to either a or b causes both globals to be initialized
+// at the same time. Furthermore, no further work is required to
+// ensure that the effects of the blank assignment occur. We must
+// keep track of which n:1 specs have been evaluated, independent of
+// which Globals are on the LHS (possibly none, if all are blank).
+//
+// See also localValueSpec.
+//
+func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global, obj types.Object) {
+ switch {
+ case len(spec.Values) == len(spec.Names):
+ // e.g. var x, y = 0, 1
+ // 1:1 assignment.
+ // Only the first time for a given GLOBAL has any effect.
+ for i, id := range spec.Names {
+ var lval lvalue = blank{}
+ if g != nil {
+ // Mode A: initialized only a single global, g
+ if isBlankIdent(id) || b.obj(id) != obj {
+ continue
+ }
+ g.spec = nil
+ lval = address{g}
+ } else {
+ // Mode B: initialize all globals.
+ if !isBlankIdent(id) {
+ g2 := b.globals[b.obj(id)].(*Global)
+ if g2.spec == nil {
+ continue // already done
+ }
+ g2.spec = nil
+ lval = address{g2}
+ }
+ }
+ if b.mode&LogSource != 0 {
+ fmt.Fprintln(os.Stderr, "build global", id.Name)
+ }
+ b.exprInPlace(init, lval, spec.Values[i])
+ if g != nil {
+ break
+ }
+ }
+
+ case len(spec.Values) == 0:
+ // e.g. var x, y int
+ // Globals are implicitly zero-initialized.
+
+ default:
+ // e.g. var x, _, y = f()
+ // n:1 assignment.
+ // Only the first time for a given SPEC has any effect.
+ if !b.nTo1Vars[spec] {
+ b.nTo1Vars[spec] = true
+ if b.mode&LogSource != 0 {
+ fmt.Fprintln(os.Stderr, "build globals", spec.Names) // ugly...
+ }
+ tuple := b.exprN(init, spec.Values[0])
+ rtypes := tuple.Type().(*types.Result).Values
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ g := b.globals[b.obj(id)].(*Global)
+ g.spec = nil // just an optimisation
+ emitStore(init, g,
+ emitExtract(init, tuple, i, rtypes[i].Type))
+ }
+ }
+ }
+ }
+}
+
+// localValueSpec emits to fn code to define all of the vars in the
+// function-local ValueSpec, spec.
+//
+// See also globalValueSpec: the two routines are similar but local
+// ValueSpecs are much simpler since they are encountered once only,
+// in their entirety, in lexical order.
+//
+func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
+ switch {
+ case len(spec.Values) == len(spec.Names):
+ // e.g. var x, y = 0, 1
+ // 1:1 assignment
+ for i, id := range spec.Names {
+ var lval lvalue = blank{}
+ if !isBlankIdent(id) {
+ lval = address{fn.addNamedLocal(b.obj(id))}
+ }
+ b.exprInPlace(fn, lval, spec.Values[i])
+ }
+
+ case len(spec.Values) == 0:
+ // e.g. var x, y int
+ // Locals are implicitly zero-initialized.
+ for _, id := range spec.Names {
+ if !isBlankIdent(id) {
+ fn.addNamedLocal(b.obj(id))
+ }
+ }
+
+ default:
+ // e.g. var x, y = pos()
+ tuple := b.exprN(fn, spec.Values[0])
+ rtypes := tuple.Type().(*types.Result).Values
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ lhs := fn.addNamedLocal(b.obj(id))
+ emitStore(fn, lhs, emitExtract(fn, tuple, i, rtypes[i].Type))
+ }
+ }
+ }
+}
+
+// assignStmt emits code to fn for a parallel assignment of rhss to lhss.
+// isDef is true if this is a short variable declaration (:=).
+//
+// Note the similarity with localValueSpec.
+// TODO(adonovan): explain differences.
+//
+func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
+ // Side effects of all LHSs and RHSs must occur in left-to-right order.
+ var lvals []lvalue
+ for _, lhs := range lhss {
+ var lval lvalue = blank{}
+ if !isBlankIdent(lhs) {
+ if isDef {
+ // Local may be "redeclared" in the same
+ // scope, so don't blindly create anew.
+ obj := b.obj(lhs.(*ast.Ident))
+ if _, ok := fn.objects[obj]; !ok {
+ fn.addNamedLocal(obj)
+ }
+ }
+ lval = b.addr(fn, lhs, false) // non-escaping
+ }
+ lvals = append(lvals, lval)
+ }
+ if len(lhss) == len(rhss) {
+ // e.g. x, y = f(), g()
+ if len(lhss) == 1 {
+ // x = type{...}
+ // Optimisation: in-place construction
+ // of composite literals.
+ b.exprInPlace(fn, lvals[0], rhss[0])
+ } else {
+ // Parallel assignment. All reads must occur
+ // before all updates, precluding exprInPlace.
+ // TODO(adonovan): opt: is it sound to
+ // perform exprInPlace if !isDef?
+ var rvals []Value
+ for _, rval := range rhss {
+ rvals = append(rvals, b.expr(fn, rval))
+ }
+ for i, lval := range lvals {
+ lval.store(fn, rvals[i])
+ }
+ }
+ } else {
+ // e.g. x, y = pos()
+ tuple := b.exprN(fn, rhss[0])
+ rtypes := tuple.Type().(*types.Result).Values
+ for i, lval := range lvals {
+ lval.store(fn, emitExtract(fn, tuple, i, rtypes[i].Type))
+ }
+ }
+}
+
+// compLit emits to fn code to initialize a composite literal e at
+// address addr with type typ, typically allocated by Alloc.
+// Nested composite literals are recursively initialized in place
+// where possible.
+//
+func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) {
+ // TODO(adonovan): test whether typ ever differs from
+ // b.exprType(e) and if so document why.
+
+ switch t := underlyingType(typ).(type) {
+ case *types.Struct:
+ for i, e := range e.Elts {
+ // Subtle: field index i is updated in KeyValue case.
+ var sf *types.Field
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ fname := kv.Key.(*ast.Ident).Name
+ for i, sf = range t.Fields {
+ if sf.Name == fname {
+ e = kv.Value
+ break
+ }
+ }
+ } else {
+ sf = t.Fields[i]
+ }
+ faddr := &FieldAddr{
+ X: addr,
+ Field: i,
+ }
+ faddr.setType(pointer(sf.Type))
+ fn.emit(faddr)
+ b.exprInPlace(fn, address{faddr}, e)
+ }
+
+ case *types.Array, *types.Slice:
+ var at *types.Array
+ var array Value
+ switch t := t.(type) {
+ case *types.Slice:
+ at = &types.Array{Elt: t.Elt} // set Len later
+ array = emitNew(fn, at)
+ case *types.Array:
+ at = t
+ array = addr
+ }
+ var idx *Literal
+ var max int64 = -1
+ for _, e := range e.Elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ idx = b.expr(fn, kv.Key).(*Literal)
+ e = kv.Value
+ } else {
+ var idxval int64
+ if idx != nil {
+ idxval = idx.Int64() + 1
+ }
+ idx = intLiteral(idxval)
+ }
+ if idx.Int64() > max {
+ max = idx.Int64()
+ }
+ iaddr := &IndexAddr{
+ X: array,
+ Index: idx,
+ }
+ iaddr.setType(pointer(at.Elt))
+ fn.emit(iaddr)
+ b.exprInPlace(fn, address{iaddr}, e)
+ }
+ if t != at { // slice
+ at.Len = max + 1
+ s := &Slice{X: array}
+ s.setType(t)
+ emitStore(fn, addr, fn.emit(s))
+ }
+
+ case *types.Map:
+ m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))}
+ m.setType(typ)
+ emitStore(fn, addr, fn.emit(m))
+ for _, e := range e.Elts {
+ e := e.(*ast.KeyValueExpr)
+ up := &MapUpdate{
+ Map: m,
+ Key: emitConv(fn, b.expr(fn, e.Key), t.Key),
+ Value: emitConv(fn, b.expr(fn, e.Value), t.Elt),
+ }
+ fn.emit(up)
+ }
+
+ case *types.Pointer:
+ // Pointers can only occur in the recursive case; we
+ // strip them off in addr() before calling compLit
+ // again, so that we allocate space for a T not a *T.
+ panic("compLit(fn, addr, e, *types.Pointer")
+
+ default:
+ panic("unexpected CompositeLit type: " + t.String())
+ }
+}
+
+// switchStmt emits to fn code for the switch statement s, optionally
+// labelled by label.
+//
+func (b *Builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {
+ // We treat SwitchStmt like a sequential if-else chain.
+ // More efficient strategies (e.g. multiway dispatch)
+ // are possible if all cases are free of side effects.
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ var tag Value = vTrue
+ if s.Tag != nil {
+ tag = b.expr(fn, s.Tag)
+ }
+ done := fn.newBasicBlock("switch.done")
+ if label != nil {
+ label._break = done
+ }
+ // We pull the default case (if present) down to the end.
+ // But each fallthrough label must point to the next
+ // body block in source order, so we preallocate a
+ // body block (fallthru) for the next case.
+ // Unfortunately this makes for a confusing block order.
+ var dfltBody *[]ast.Stmt
+ var dfltFallthrough *BasicBlock
+ var fallthru, dfltBlock *BasicBlock
+ ncases := len(s.Body.List)
+ for i, clause := range s.Body.List {
+ body := fallthru
+ if body == nil {
+ body = fn.newBasicBlock("switch.body") // first case only
+ }
+
+ // Preallocate body block for the next case.
+ fallthru = done
+ if i+1 < ncases {
+ fallthru = fn.newBasicBlock("switch.body")
+ }
+
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ // Default case.
+ dfltBody = &cc.Body
+ dfltFallthrough = fallthru
+ dfltBlock = body
+ continue
+ }
+
+ var nextCond *BasicBlock
+ for _, cond := range cc.List {
+ nextCond = fn.newBasicBlock("switch.next")
+ // TODO(adonovan): opt: when tag==vTrue, we'd
+ // get better much code if we use b.cond(cond)
+ // instead of BinOp(EQL, tag, b.expr(cond))
+ // followed by If. Don't forget conversions
+ // though.
+ cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond))
+ emitIf(fn, cond, body, nextCond)
+ fn.currentBlock = nextCond
+ }
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: fallthru,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = nextCond
+ }
+ if dfltBlock != nil {
+ emitJump(fn, dfltBlock)
+ fn.currentBlock = dfltBlock
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: dfltFallthrough,
+ }
+ b.stmtList(fn, *dfltBody)
+ fn.targets = fn.targets.tail
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// typeSwitchStmt emits to fn code for the type switch statement s, optionally
+// labelled by label.
+//
+func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
+ // We treat TypeSwitchStmt like a sequential if-else
+ // chain. More efficient strategies (e.g. multiway
+ // dispatch) are possible.
+
+ // Typeswitch lowering:
+ //
+ // var x X
+ // switch y := x.(type) {
+ // case T1, T2: S1 // >1 (y := x)
+ // default: SD // 0 types (y := x)
+ // case T3: S3 // 1 type (y := x.(T3))
+ // }
+ //
+ // ...s.Init...
+ // x := eval x
+ // y := x
+ // .caseT1:
+ // t1, ok1 := typeswitch,ok x <T1>
+ // if ok1 then goto S1 else goto .caseT2
+ // .caseT2:
+ // t2, ok2 := typeswitch,ok x <T2>
+ // if ok2 then goto S1 else goto .caseT3
+ // .S1:
+ // ...S1...
+ // goto done
+ // .caseT3:
+ // t3, ok3 := typeswitch,ok x <T3>
+ // if ok3 then goto S3 else goto default
+ // .S3:
+ // y' := t3 // Kludge: within scope of S3, y resolves here
+ // ...S3...
+ // goto done
+ // .default:
+ // goto done
+ // .done:
+
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+
+ var x, y Value
+ var id *ast.Ident
+ switch ass := s.Assign.(type) {
+ case *ast.ExprStmt: // x.(type)
+ x = b.expr(fn, noparens(ass.X).(*ast.TypeAssertExpr).X)
+ case *ast.AssignStmt: // y := x.(type)
+ x = b.expr(fn, noparens(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+ id = ass.Lhs[0].(*ast.Ident)
+ y = fn.addNamedLocal(b.obj(id))
+ emitStore(fn, y, x)
+ }
+
+ done := fn.newBasicBlock("typeswitch.done")
+ if label != nil {
+ label._break = done
+ }
+ var dfltBody []ast.Stmt
+ for _, clause := range s.Body.List {
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ dfltBody = cc.Body
+ continue
+ }
+ body := fn.newBasicBlock("typeswitch.body")
+ var next *BasicBlock
+ var casetype types.Type
+ var ti Value // t_i, ok := typeassert,ok x <T_i>
+ for _, cond := range cc.List {
+ next = fn.newBasicBlock("typeswitch.next")
+ casetype = b.exprType(cond)
+ var condv Value
+ if casetype == tUntypedNil {
+ condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()))
+ } else {
+ yok := &TypeAssert{
+ X: x,
+ AssertedType: casetype,
+ CommaOk: true,
+ }
+ yok.setType(&types.Result{Values: []*types.Var{
+ {Name: "value", Type: casetype},
+ varOk,
+ }})
+ fn.emit(yok)
+ ti = emitExtract(fn, yok, 0, casetype)
+ condv = emitExtract(fn, yok, 1, tBool)
+ }
+ emitIf(fn, condv, body, next)
+ fn.currentBlock = next
+ }
+ fn.currentBlock = body
+ if id != nil && len(cc.List) == 1 && casetype != tUntypedNil {
+ // Declare a new shadow local variable of the
+ // same name but a more specific type.
+ // Side effect: reassociates binding for y's object.
+ y2 := fn.addNamedLocal(b.obj(id))
+ y2.Name_ += "'" // debugging aid
+ y2.Type_ = pointer(casetype)
+ emitStore(fn, y2, ti)
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ if id != nil {
+ fn.objects[b.obj(id)] = y // restore previous y binding
+ }
+ emitJump(fn, done)
+ fn.currentBlock = next
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, dfltBody)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// selectStmt emits to fn code for the select statement s, optionally
+// labelled by label.
+//
+func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
+ // A blocking select of a single case degenerates to a
+ // simple send or receive.
+ // TODO(adonovan): is this optimisation worth its weight?
+ if len(s.Body.List) == 1 {
+ clause := s.Body.List[0].(*ast.CommClause)
+ if clause.Comm != nil {
+ b.stmt(fn, clause.Comm)
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = done
+ return
+ }
+ }
+
+ // First evaluate all channels in all cases, and find
+ // the directions of each state.
+ var states []SelectState
+ blocking := true
+ for _, clause := range s.Body.List {
+ switch comm := clause.(*ast.CommClause).Comm.(type) {
+ case nil: // default case
+ blocking = false
+
+ case *ast.SendStmt: // ch<- i
+ ch := b.expr(fn, comm.Chan)
+ states = append(states, SelectState{
+ Dir: ast.SEND,
+ Chan: ch,
+ Send: emitConv(fn, b.expr(fn, comm.Value),
+ underlyingType(ch.Type()).(*types.Chan).Elt),
+ })
+
+ case *ast.AssignStmt: // x := <-ch
+ states = append(states, SelectState{
+ Dir: ast.RECV,
+ Chan: b.expr(fn, noparens(comm.Rhs[0]).(*ast.UnaryExpr).X),
+ })
+
+ case *ast.ExprStmt: // <-ch
+ states = append(states, SelectState{
+ Dir: ast.RECV,
+ Chan: b.expr(fn, noparens(comm.X).(*ast.UnaryExpr).X),
+ })
+ }
+ }
+
+ // We dispatch on the (fair) result of Select using a
+ // sequential if-else chain, in effect:
+ //
+ // idx, recv, recvOk := select(...)
+ // if idx == 0 { // receive on channel 0
+ // x, ok := recv, recvOk
+ // ...state0...
+ // } else if v == 1 { // send on channel 1
+ // ...state1...
+ // } else {
+ // ...default...
+ // }
+ //
+ // TODO(adonovan): opt: define and use a multiway dispatch instr.
+ pair := &Select{
+ States: states,
+ Blocking: blocking,
+ }
+ pair.setType(tSelect)
+ fn.emit(pair)
+ idx := emitExtract(fn, pair, 0, tInt)
+
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+
+ var dfltBody *[]ast.Stmt
+ state := 0
+ for _, cc := range s.Body.List {
+ clause := cc.(*ast.CommClause)
+ if clause.Comm == nil {
+ dfltBody = &clause.Body
+ continue
+ }
+ body := fn.newBasicBlock("select.body")
+ next := fn.newBasicBlock("select.next")
+ emitIf(fn, emitCompare(fn, token.EQL, idx, intLiteral(int64(state))), body, next)
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ switch comm := clause.Comm.(type) {
+ case *ast.AssignStmt: // x := <-states[state].Chan
+ xdecl := fn.addNamedLocal(b.obj(comm.Lhs[0].(*ast.Ident)))
+
+ emitStore(fn, xdecl, emitExtract(fn, pair, 1, indirectType(xdecl.Type())))
+ if len(comm.Lhs) == 2 { // x, ok := ...
+ okdecl := fn.addNamedLocal(b.obj(comm.Lhs[1].(*ast.Ident)))
+ emitStore(fn, okdecl, emitExtract(fn, pair, 2, indirectType(okdecl.Type())))
+ }
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = next
+ state++
+ }
+ if dfltBody != nil {
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, *dfltBody)
+ fn.targets = fn.targets.tail
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// forStmt emits to fn code for the for statement s, optionally
+// labelled by label.
+//
+func (b *Builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {
+ // ...init...
+ // jump loop
+ // loop:
+ // if cond goto body else done
+ // body:
+ // ...body...
+ // jump post
+ // post: (target of continue)
+ // ...post...
+ // jump loop
+ // done: (target of break)
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ body := fn.newBasicBlock("for.body")
+ done := fn.newBasicBlock("for.done") // target of 'break'
+ loop := body // target of back-edge
+ if s.Cond != nil {
+ loop = fn.newBasicBlock("for.loop")
+ }
+ cont := loop // target of 'continue'
+ if s.Post != nil {
+ cont = fn.newBasicBlock("for.post")
+ }
+ if label != nil {
+ label._break = done
+ label._continue = cont
+ }
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+ if loop != body {
+ b.cond(fn, s.Cond, body, done)
+ fn.currentBlock = body
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: cont,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, cont)
+
+ if s.Post != nil {
+ fn.currentBlock = cont
+ b.stmt(fn, s.Post)
+ emitJump(fn, loop) // back-edge
+ }
+ fn.currentBlock = done
+}
+
+// rangeStmt emits to fn code for the range statement s, optionally
+// labelled by label.
+//
+func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
+ // it := range x
+ // jump loop
+ // loop: (target of continue)
+ // okv := next it (ok, key, value?)
+ // ok = extract okv #0
+ // if ok goto body else done
+ // body:
+ // t0 = extract okv #1
+ // k = *t0
+ // t1 = extract okv #2
+ // v = *t1
+ // ...body...
+ // jump loop
+ // done: (target of break)
+
+ hasK := !isBlankIdent(s.Key)
+ hasV := s.Value != nil && !isBlankIdent(s.Value)
+
+ // Ranging over just the keys of a pointer to an array
+ // doesn't (need to) evaluate the array:
+ // for i := range (*[10]int)(nil) {...}
+ // Instead it is transformed into a simple loop:
+ // i = -1
+ // jump loop
+ // loop: (target of continue)
+ // increment i
+ // if i < 10 goto body else done
+ // body:
+ // k = i
+ // ...body...
+ // jump loop
+ // done: (target of break)
+ var arrayLen int64 = -1
+ if !hasV {
+ if ptr, ok := underlyingType(b.exprType(s.X)).(*types.Pointer); ok {
+ if arr, ok := underlyingType(ptr.Base).(*types.Array); ok {
+ arrayLen = arr.Len
+ }
+ }
+ }
+
+ // If iteration variables are defined (:=), this
+ // occurs once outside the loop.
+ //
+ // Unlike a short variable declaration, a RangeStmt
+ // using := never redeclares an existing variable; it
+ // always creates a new one.
+ if s.Tok == token.DEFINE {
+ if hasK {
+ fn.addNamedLocal(b.obj(s.Key.(*ast.Ident)))
+ }
+ if hasV {
+ fn.addNamedLocal(b.obj(s.Value.(*ast.Ident)))
+ }
+ }
+
+ var ok Value
+ var okv *Next
+ var okvVars []*types.Var
+ var index *Alloc // *array index loops only
+ loop := fn.newBasicBlock("range.loop")
+ var body, done *BasicBlock
+ if arrayLen == -1 {
+ rng := &Range{X: b.expr(fn, s.X)}
+ rng.setType(tRangeIter)
+ it := fn.emit(rng)
+
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ okv = &Next{Iter: it}
+ okvVars = []*types.Var{
+ varOk,
+ {Name: "k", Type: tInvalid}, // mutated below
+ {Name: "v", Type: tInvalid}, // mutated below
+ }
+ okv.setType(&types.Result{Values: okvVars})
+ fn.emit(okv)
+ ok = emitExtract(fn, okv, 0, tBool)
+ } else {
+ index = fn.addLocal(tInt)
+ emitStore(fn, index, intLiteral(-1))
+
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ // TODO use emitArith here and elsewhere?
+ incr := &BinOp{
+ Op: token.ADD,
+ X: emitLoad(fn, index),
+ Y: intLiteral(1),
+ }
+ incr.setType(tInt)
+ emitStore(fn, index, fn.emit(incr))
+ ok = emitCompare(fn, token.LSS, incr, intLiteral(arrayLen))
+ }
+
+ body = fn.newBasicBlock("range.body")
+ done = fn.newBasicBlock("range.done")
+
+ emitIf(fn, ok, body, done)
+ fn.currentBlock = body
+
+ if label != nil {
+ label._break = done
+ label._continue = loop
+ }
+
+ if arrayLen == -1 {
+ // Evaluate both LHS expressions before we update either.
+ var k, v lvalue
+ if hasK {
+ k = b.addr(fn, s.Key, false) // non-escaping
+ okvVars[1].Type = b.exprType(s.Key)
+ }
+ if hasV {
+ v = b.addr(fn, s.Value, false) // non-escaping
+ okvVars[2].Type = b.exprType(s.Value)
+ }
+ if hasK {
+ k.store(fn, emitExtract(fn, okv, 1, okvVars[1].Type))
+ }
+ if hasV {
+ v.store(fn, emitExtract(fn, okv, 2, okvVars[2].Type))
+ }
+ } else {
+ // Store a copy of the index variable to k.
+ if hasK {
+ k := b.addr(fn, s.Key, false) // non-escaping
+ k.store(fn, emitLoad(fn, index))
+ }
+ }
+
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: loop,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, loop) // back-edge
+ fn.currentBlock = done
+}
+
+// stmt lowers statement s to SSA form, emitting code to fn.
+func (b *Builder) stmt(fn *Function, _s ast.Stmt) {
+ // The label of the current statement. If non-nil, its _goto
+ // target is always set; its _break and _continue are set only
+ // within the body of switch/typeswitch/select/for/range.
+ // It is effectively an additional default-nil parameter of stmt().
+ // TODO(adonovan): fix: handle multiple labels on the same stmt.
+ var label *lblock
+start:
+ switch s := _s.(type) {
+ case *ast.EmptyStmt:
+ // ignore. (Usually removed by gofmt.)
+
+ case *ast.DeclStmt: // Con, Var or Typ
+ d := s.Decl.(*ast.GenDecl)
+ for _, spec := range d.Specs {
+ if vs, ok := spec.(*ast.ValueSpec); ok {
+ b.localValueSpec(fn, vs)
+ }
+ }
+
+ case *ast.LabeledStmt:
+ label = fn.labelledBlock(s.Label)
+ emitJump(fn, label._goto)
+ fn.currentBlock = label._goto
+ _s = s.Stmt
+ goto start // effectively: tailcall stmt(fn, s.Stmt, label)
+
+ case *ast.ExprStmt:
+ b.expr(fn, s.X)
+
+ case *ast.SendStmt:
+ fn.emit(&Send{
+ Chan: b.expr(fn, s.Chan),
+ X: emitConv(fn, b.expr(fn, s.Value),
+ underlyingType(b.exprType(s.Chan)).(*types.Chan).Elt),
+ })
+
+ case *ast.IncDecStmt:
+ op := token.ADD
+ if s.Tok == token.DEC {
+ op = token.SUB
+ }
+ b.assignOp(fn, b.addr(fn, s.X, false), vOne, op)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE)
+
+ default: // +=, etc.
+ op := s.Tok + token.ADD - token.ADD_ASSIGN
+ b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op)
+ }
+
+ case *ast.GoStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic() is not forbidden, but is not (yet) an intrinsic.
+ var v Go
+ b.setCall(fn, s.Call, &v.CallCommon)
+ fn.emit(&v)
+
+ case *ast.DeferStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic() is not forbidden, but is not (yet) an intrinsic.
+ var v Defer
+ b.setCall(fn, s.Call, &v.CallCommon)
+ fn.emit(&v)
+
+ case *ast.ReturnStmt:
+ if fn == fn.Pkg.Init {
+ // A "return" within an init block is treated
+ // like a "goto" to the next init block. We
+ // use the outermost BREAK target for this purpose.
+ var block *BasicBlock
+ for t := fn.targets; t != nil; t = t.tail {
+ if t._break != nil {
+ block = t._break
+ }
+ }
+ emitJump(fn, block)
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ return
+ }
+ var results []Value
+ // Per the spec, there are three distinct cases of return.
+ switch {
+ case len(s.Results) == 0:
+ // Return with no arguments.
+ // Prior assigns to named result params are
+ // reloaded into results tuple.
+ // A void function is a degenerate case of this.
+ for _, r := range fn.results {
+ results = append(results, emitLoad(fn, r))
+ }
+
+ case len(s.Results) == 1 && len(fn.Signature.Results) > 1:
+ // Return of one expression in a multi-valued function.
+ tuple := b.exprN(fn, s.Results[0])
+ for i, v := range tuple.Type().(*types.Result).Values {
+ results = append(results, emitExtract(fn, tuple, i, v.Type))
+ }
+
+ default:
+ // Return one or more single-valued expressions.
+ // These become the scalar or tuple result.
+ for _, r := range s.Results {
+ results = append(results, b.expr(fn, r))
+ }
+ }
+ // Perform implicit conversions.
+ for i := range results {
+ results[i] = emitConv(fn, results[i], fn.Signature.Results[i].Type)
+ }
+ fn.emit(&Ret{Results: results})
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+
+ case *ast.BranchStmt:
+ var block *BasicBlock
+ switch s.Tok {
+ case token.BREAK:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._break
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._break
+ }
+ }
+
+ case token.CONTINUE:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._continue
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._continue
+ }
+ }
+
+ case token.FALLTHROUGH:
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._fallthrough
+ }
+
+ case token.GOTO:
+ block = fn.labelledBlock(s.Label)._goto
+ }
+ if block == nil {
+ // TODO(gri): fix: catch these in the typechecker.
+ fmt.Printf("ignoring illegal branch: %s %s\n", s.Tok, s.Label)
+ } else {
+ emitJump(fn, block)
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ }
+
+ case *ast.BlockStmt:
+ b.stmtList(fn, s.List)
+
+ case *ast.IfStmt:
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ then := fn.newBasicBlock("if.then")
+ done := fn.newBasicBlock("if.done")
+ els := done
+ if s.Else != nil {
+ els = fn.newBasicBlock("if.else")
+ }
+ b.cond(fn, s.Cond, then, els)
+ fn.currentBlock = then
+ b.stmt(fn, s.Body)
+ emitJump(fn, done)
+
+ if s.Else != nil {
+ fn.currentBlock = els
+ b.stmt(fn, s.Else)
+ emitJump(fn, done)
+ }
+
+ fn.currentBlock = done
+
+ case *ast.SwitchStmt:
+ b.switchStmt(fn, s, label)
+
+ case *ast.TypeSwitchStmt:
+ b.typeSwitchStmt(fn, s, label)
+
+ case *ast.SelectStmt:
+ b.selectStmt(fn, s, label)
+
+ case *ast.ForStmt:
+ b.forStmt(fn, s, label)
+
+ case *ast.RangeStmt:
+ b.rangeStmt(fn, s, label)
+
+ default:
+ panic(fmt.Sprintf("unexpected statement kind: %T", s))
+ }
+}
+
+// buildFunction builds SSA code for the body of function fn. Idempotent.
+func (b *Builder) buildFunction(fn *Function) {
+ if fn.Blocks != nil {
+ return // building already started
+ }
+ if fn.syntax == nil {
+ return // not a Go source function
+ }
+ if fn.syntax.body == nil {
+ return // Go source function with no body (external)
+ }
+ fn.start(b.idents)
+ b.stmt(fn, fn.syntax.body)
+ if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
+ // We fell off the end: an implicit no-arg return statement.
+ fn.emit(new(Ret))
+ }
+ fn.finish()
+}
+
+// memberFromObject populates package pkg with a member for the
+// typechecker object obj.
+//
+// For objects from Go source code, syntax is the associated syntax
+// tree (for funcs and vars only); it will be used during the build
+// phase.
+//
+func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
+ name := obj.GetName()
+ switch obj := obj.(type) {
+ case *types.TypeName:
+ pkg.Members[name] = &Type{NamedType: obj.Type.(*types.NamedType)}
+
+ case *types.Const:
+ pkg.Members[name] = newLiteral(obj.Val, obj.Type)
+
+ case *types.Var:
+ spec, _ := syntax.(*ast.ValueSpec)
+ g := &Global{
+ Pkg: pkg,
+ Name_: name,
+ Type_: pointer(obj.Type), // address
+ spec: spec,
+ }
+ b.globals[obj] = g
+ pkg.Members[name] = g
+
+ case *types.Func:
+ var fs *funcSyntax
+ var pos token.Pos
+ if decl, ok := syntax.(*ast.FuncDecl); ok {
+ fs = &funcSyntax{
+ recvField: decl.Recv,
+ paramFields: decl.Type.Params,
+ resultFields: decl.Type.Results,
+ body: decl.Body,
+ }
+ // TODO(gri): make GcImported types.Object
+ // implement the full object interface
+ // including Pos(). Or at least not crash.
+ pos = obj.GetPos()
+ }
+ sig := obj.Type.(*types.Signature)
+ fn := &Function{
+ Name_: name,
+ Signature: sig,
+ Pos: pos,
+ Pkg: pkg,
+ Prog: b.Prog,
+ syntax: fs,
+ }
+ if sig.Recv == nil {
+ // Function declaration.
+ b.globals[obj] = fn
+ pkg.Members[name] = fn
+ } else {
+ // Method declaration.
+ nt := deref(sig.Recv.Type).(*types.NamedType)
+ _, method := methodIndex(nt, nt.Methods, makeId(name, pkg.Types))
+ b.Prog.concreteMethods[method] = fn
+ }
+
+ default: // (incl. *types.Package)
+ panic(fmt.Sprintf("unexpected Object type: %T", obj))
+ }
+}
+
+// membersFromDecl populates package pkg with members for each
+// typechecker object (var, func, const or type) associated with the
+// specified decl.
+//
+func (b *Builder) membersFromDecl(pkg *Package, decl ast.Decl) {
+ switch decl := decl.(type) {
+ case *ast.GenDecl: // import, const, type or var
+ switch decl.Tok {
+ case token.CONST:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ b.memberFromObject(pkg, b.obj(id), nil)
+ }
+ }
+ }
+
+ case token.VAR:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ b.memberFromObject(pkg, b.obj(id), spec)
+ }
+ }
+ }
+
+ case token.TYPE:
+ for _, spec := range decl.Specs {
+ id := spec.(*ast.TypeSpec).Name
+ if !isBlankIdent(id) {
+ b.memberFromObject(pkg, b.obj(id), nil)
+ }
+ }
+ }
+
+ case *ast.FuncDecl:
+ id := decl.Name
+ if decl.Recv == nil && id.Name == "init" {
+ return // init blocks aren't functions
+ }
+ if !isBlankIdent(id) {
+ b.memberFromObject(pkg, b.obj(id), decl)
+ }
+ }
+}
+
+// CreatePackage creates a package from the specified set of files,
+// performs type-checking, and allocates all global SSA Values for the
+// package. It returns a new SSA Package providing access to these
+// values.
+//
+// importPath is the full name under which this package is known, such
+// as appears in an import declaration. e.g. "sync/atomic".
+//
+// The ParseFiles() utility may be helpful for parsing a set of Go
+// source files.
+//
+func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, error) {
+ typkg, firstErr := b.typechecker.Check(b.Prog.Files, files)
+ if firstErr != nil {
+ return nil, firstErr
+ }
+ return b.createPackageImpl(typkg, importPath, files), nil
+}
+
+// createPackageImpl constructs an SSA Package from an error-free
+// types.Package typkg and populates its Members mapping. It returns
+// the newly constructed ssa.Package.
+//
+// The real work of building SSA form for each function is not done
+// until a subsequent call to BuildPackage.
+//
+// If files is non-nil, its declarations will be used to generate code
+// for functions, methods and init blocks in a subsequent call to
+// BuildPackage. Otherwise, typkg is assumed to have been imported
+// from the gc compiler's object files; no code will be available.
+//
+func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, files []*ast.File) *Package {
+ // TODO(gri): make this an invariant and eliminate importPath
+ // param and Package field.
+ // if importPath != p.Types.Path {
+ // panic(importPath + " != " + p.Types.Path)
+ // }
+
+ p := &Package{
+ Prog: b.Prog,
+ Types: typkg,
+ ImportPath: importPath,
+ Members: make(map[string]Member),
+ files: files,
+ }
+
+ b.packages[typkg] = p
+ b.Prog.Packages[importPath] = p
+
+ // CREATE phase.
+ // Allocate all package members: vars, funcs and consts and types.
+ if len(files) > 0 {
+ // Go source package.
+
+ p.Pos = files[0].Package // arbitrary file
+
+ // TODO(gri): make it a typechecker error for there to
+ // be duplicate (e.g.) main functions in the same package.
+ for _, file := range p.files {
+ // ast.Print(b.Prog.Files, file) // debugging
+ for _, decl := range file.Decls {
+ b.membersFromDecl(p, decl)
+ }
+ }
+ } else {
+ // GC-compiled binary package.
+ // No code.
+ // No position information.
+
+ for _, obj := range p.Types.Scope.Entries {
+ b.memberFromObject(p, obj, nil)
+ }
+ }
+
+ // Compute the method sets
+ for _, mem := range p.Members {
+ switch t := mem.(type) {
+ case *Type:
+ t.Methods = b.Prog.MethodSet(t.NamedType)
+ t.PtrMethods = b.Prog.MethodSet(pointer(t.NamedType))
+ }
+ }
+
+ // Add init() function (but not to Members since it can't be referenced).
+ p.Init = &Function{
+ Name_: "init",
+ Signature: new(types.Signature),
+ Pos: p.Pos,
+ Pkg: p,
+ Prog: b.Prog,
+ }
+
+ // Add initializer guard variable.
+ initguard := &Global{
+ Pkg: p,
+ Name_: "init·guard",
+ Type_: pointer(tBool),
+ }
+ p.Members[initguard.Name()] = initguard
+
+ if b.mode&LogPackages != 0 {
+ p.DumpTo(os.Stderr)
+ }
+
+ return p
+}
+
+// buildDecl builds SSA code for all globals, functions or methods
+// declared by decl in package pkg.
+//
+func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) {
+ switch decl := decl.(type) {
+ case *ast.GenDecl:
+ switch decl.Tok {
+ // Nothing to do for CONST, IMPORT.
+ case token.VAR:
+ for _, spec := range decl.Specs {
+ b.globalValueSpec(pkg.Init, spec.(*ast.ValueSpec), nil, nil)
+ }
+ case token.TYPE:
+ for _, spec := range decl.Specs {
+ id := spec.(*ast.TypeSpec).Name
+ if isBlankIdent(id) {
+ continue
+ }
+ obj := b.obj(id).(*types.TypeName)
+ for _, method := range obj.Type.(*types.NamedType).Methods {
+ b.buildFunction(b.Prog.concreteMethods[method])
+ }
+ }
+ }
+
+ case *ast.FuncDecl:
+ id := decl.Name
+ if isBlankIdent(id) {
+ // no-op
+
+ } else if decl.Recv == nil && id.Name == "init" {
+ // init() block
+ if b.mode&LogSource != 0 {
+ fmt.Fprintln(os.Stderr, "build init block @", b.Prog.Files.Position(decl.Pos()))
+ }
+ init := pkg.Init
+
+ // A return statement within an init block is
+ // treated like a "goto" to the the next init
+ // block, which we stuff in the outermost
+ // break label.
+ next := init.newBasicBlock("init.next")
+ init.targets = &targets{
+ tail: init.targets,
+ _break: next,
+ }
+ b.stmt(init, decl.Body)
+ emitJump(init, next)
+ init.targets = init.targets.tail
+ init.currentBlock = next
+
+ } else if m, ok := b.globals[b.obj(id)]; ok {
+ // Package-level function.
+ b.buildFunction(m.(*Function))
+ }
+ }
+
+}
+
+// BuildPackage builds SSA code for all functions and vars in package p.
+//
+// BuildPackage is idempotent.
+//
+func (b *Builder) BuildPackage(p *Package) {
+ if p.files == nil {
+ return // already done (or nothing to do)
+ }
+ if b.mode&LogSource != 0 {
+ fmt.Fprintln(os.Stderr, "build package", p.ImportPath)
+ }
+ init := p.Init
+ init.start(b.idents)
+
+ // Make init() skip if package is already initialized.
+ initguard := p.Var("init·guard")
+ doinit := init.newBasicBlock("init.start")
+ done := init.newBasicBlock("init.done")
+ emitIf(init, emitLoad(init, initguard), done, doinit)
+ init.currentBlock = doinit
+ emitStore(init, initguard, vTrue)
+
+ // TODO(gri): fix: the types.Package.Imports map may contains
+ // entries for other package's import statements, if produced
+ // by GcImport. Project it down to just the ones for us.
+ imports := make(map[string]*types.Package)
+ for _, file := range p.files {
+ for _, imp := range file.Imports {
+ path, _ := strconv.Unquote(imp.Path.Value)
+ if path != "unsafe" {
+ imports[path] = p.Types.Imports[path]
+ }
+ }
+ }
+
+ // Call the init() function of each package we import.
+ // Order is unspecified (and is in fact nondeterministic).
+ for name, imported := range imports {
+ p2 := b.packages[imported]
+ if p2 == nil {
+ panic("Building " + p.Name() + ": CreatePackage has not been called for package " + name)
+ }
+ // TODO(adonovan): opt: BuildPackage should be
+ // package-local, so we can run it for all packages in
+ // parallel once CreatePackage has been called for all
+ // prerequisites. Until then, ensure all import
+ // dependencies are completely built before we are.
+ b.BuildPackage(p2)
+
+ var v Call
+ v.Func = p2.Init
+ v.Pos = init.Pos
+ v.setType(new(types.Result))
+ init.emit(&v)
+ }
+
+ // Visit the package's var decls and init funcs in source
+ // order. This causes init() code to be generated in
+ // topological order. We visit them transitively through
+ // functions of the same package, but we don't treat functions
+ // as roots. TODO(adonovan): fix: don't visit through other
+ // packages.
+ //
+ // We also ensure all functions and methods are built, even if
+ // they are unreachable.
+ //
+ // The order between files is unspecified (and is in fact
+ // nondeterministic).
+ //
+ // TODO(adonovan): the partial order of initialization is
+ // underspecified. Discuss this with gri.
+ for _, file := range p.files {
+ for _, decl := range file.Decls {
+ b.buildDecl(p, decl)
+ }
+ }
+ p.files = nil
+
+ // Finish up.
+ emitJump(init, done)
+ init.currentBlock = done
+ init.emit(new(Ret))
+ init.finish()
+}