]> Cypherpunks repositories - gostls13.git/commitdiff
exp/ssa: (#4 of 5): the SSA builder.
authorAlan Donovan <adonovan@google.com>
Mon, 4 Feb 2013 17:22:35 +0000 (12:22 -0500)
committerAlan Donovan <adonovan@google.com>
Mon, 4 Feb 2013 17:22:35 +0000 (12:22 -0500)
R=iant, gri, iant, rogpeppe
CC=golang-dev
https://golang.org/cl/7196053

src/pkg/exp/ssa/builder.go [new file with mode: 0644]
src/pkg/exp/ssa/emit.go [new file with mode: 0644]
src/pkg/exp/ssa/func.go
src/pkg/exp/ssa/importer.go [new file with mode: 0644]
src/pkg/exp/ssa/lvalue.go [new file with mode: 0644]
src/pkg/exp/ssa/print.go
src/pkg/exp/ssa/promote.go [new file with mode: 0644]
src/pkg/exp/ssa/ssa.go
src/pkg/exp/ssa/util.go

diff --git a/src/pkg/exp/ssa/builder.go b/src/pkg/exp/ssa/builder.go
new file mode 100644 (file)
index 0000000..d970654
--- /dev/null
@@ -0,0 +1,2682 @@
+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()
+}
diff --git a/src/pkg/exp/ssa/emit.go b/src/pkg/exp/ssa/emit.go
new file mode 100644 (file)
index 0000000..9a176b4
--- /dev/null
@@ -0,0 +1,261 @@
+package ssa
+
+// Helpers for emitting SSA instructions.
+
+import (
+       "go/token"
+       "go/types"
+)
+
+// emitNew emits to f a new (heap Alloc) instruction allocating an
+// object of type typ.
+//
+func emitNew(f *Function, typ types.Type) Value {
+       return f.emit(&Alloc{
+               Type_: pointer(typ),
+               Heap:  true,
+       })
+}
+
+// emitLoad emits to f an instruction to load the address addr into a
+// new temporary, and returns the value so defined.
+//
+func emitLoad(f *Function, addr Value) Value {
+       v := &UnOp{Op: token.MUL, X: addr}
+       v.setType(indirectType(addr.Type()))
+       return f.emit(v)
+}
+
+// emitArith emits to f code to compute the binary operation op(x, y)
+// where op is an eager shift, logical or arithmetic operation.
+// (Use emitCompare() for comparisons and Builder.logicalBinop() for
+// non-eager operations.)
+//
+func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value {
+       switch op {
+       case token.SHL, token.SHR:
+               // TODO(adonovan): fix: is this correct?
+               x = emitConv(f, x, t)
+               y = emitConv(f, y, types.Typ[types.Uint64])
+
+       case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+               x = emitConv(f, x, t)
+               y = emitConv(f, y, t)
+
+       default:
+               panic("illegal op in emitArith: " + op.String())
+
+       }
+       v := &BinOp{
+               Op: op,
+               X:  x,
+               Y:  y,
+       }
+       v.setType(t)
+       return f.emit(v)
+}
+
+// emitCompare emits to f code compute the boolean result of
+// comparison comparison 'x op y'.
+//
+func emitCompare(f *Function, op token.Token, x, y Value) Value {
+       // TODO(adonovan): fix: this is incomplete.
+       xt := underlyingType(x.Type())
+       yt := underlyingType(y.Type())
+
+       // Special case to optimise a tagless SwitchStmt so that
+       // these are equivalent
+       //   switch { case e: ...}
+       //   switch true { case e: ... }
+       //   if e==true { ... }
+       // even in the case when e's type is an interface.
+       // TODO(adonovan): generalise to x==true, false!=y, etc.
+       if x == vTrue && op == token.EQL {
+               if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 {
+                       return y
+               }
+       }
+
+       if types.IsIdentical(xt, yt) {
+               // no conversion necessary
+       } else if _, ok := xt.(*types.Interface); ok {
+               y = emitConv(f, y, x.Type())
+       } else if _, ok := yt.(*types.Interface); ok {
+               x = emitConv(f, x, y.Type())
+       } else if _, ok := x.(*Literal); ok {
+               x = emitConv(f, x, y.Type())
+       } else if _, ok := y.(*Literal); ok {
+               y = emitConv(f, y, x.Type())
+       } else {
+               // other cases, e.g. channels.  No-op.
+       }
+
+       v := &BinOp{
+               Op: op,
+               X:  x,
+               Y:  y,
+       }
+       v.setType(tBool)
+       return f.emit(v)
+}
+
+// emitConv emits to f code to convert Value val to exactly type typ,
+// and returns the converted value.  Implicit conversions are implied
+// by language assignability rules in the following operations:
+//
+// - from rvalue type to lvalue type in assignments.
+// - from actual- to formal-parameter types in function calls.
+// - from return value type to result type in return statements.
+// - population of struct fields, array and slice elements, and map
+//   keys and values within compoisite literals
+// - from index value to index type in indexing expressions.
+// - for both arguments of comparisons.
+// - from value type to channel type in send expressions.
+//
+func emitConv(f *Function, val Value, typ types.Type) Value {
+       // fmt.Printf("emitConv %s -> %s, %T", val.Type(), typ, val) // debugging
+
+       // Identical types?  Conversion is a no-op.
+       if types.IsIdentical(val.Type(), typ) {
+               return val
+       }
+
+       ut_dst := underlyingType(typ)
+       ut_src := underlyingType(val.Type())
+
+       // Identical underlying types?  Conversion is a name change.
+       if types.IsIdentical(ut_dst, ut_src) {
+               // TODO(adonovan): make this use a distinct
+               // instruction, ChangeType.  This instruction must
+               // also cover the cases of channel type restrictions and
+               // conversions between pointers to identical base
+               // types.
+               c := &Conv{X: val}
+               c.setType(typ)
+               return f.emit(c)
+       }
+
+       // Conversion to, or construction of a value of, an interface type?
+       if _, ok := ut_dst.(*types.Interface); ok {
+
+               // Assignment from one interface type to a different one?
+               if _, ok := ut_src.(*types.Interface); ok {
+                       c := &ChangeInterface{X: val}
+                       c.setType(typ)
+                       return f.emit(c)
+               }
+
+               // Untyped nil literal?  Return interface-typed nil literal.
+               if ut_src == tUntypedNil {
+                       return nilLiteral(typ)
+               }
+
+               // Convert (non-nil) "untyped" literals to their default type.
+               // TODO(gri): expose types.isUntyped().
+               if t, ok := ut_src.(*types.Basic); ok && t.Info&types.IsUntyped != 0 {
+                       val = emitConv(f, val, DefaultType(ut_src))
+               }
+
+               mi := &MakeInterface{
+                       X:       val,
+                       Methods: f.Prog.MethodSet(val.Type()),
+               }
+               mi.setType(typ)
+               return f.emit(mi)
+       }
+
+       // Conversion of a literal to a non-interface type results in
+       // a new literal of the destination type and (initially) the
+       // same abstract value.  We don't compute the representation
+       // change yet; this defers the point at which the number of
+       // possible representations explodes.
+       if l, ok := val.(*Literal); ok {
+               return newLiteral(l.Value, typ)
+       }
+
+       // A representation-changing conversion.
+       c := &Conv{X: val}
+       c.setType(typ)
+       return f.emit(c)
+}
+
+// emitStore emits to f an instruction to store value val at location
+// addr, applying implicit conversions as required by assignabilty rules.
+//
+func emitStore(f *Function, addr, val Value) {
+       f.emit(&Store{
+               Addr: addr,
+               Val:  emitConv(f, val, indirectType(addr.Type())),
+       })
+}
+
+// emitJump emits to f a jump to target, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitJump(f *Function, target *BasicBlock) {
+       b := f.currentBlock
+       b.emit(new(Jump))
+       addEdge(b, target)
+       f.currentBlock = nil
+}
+
+// emitIf emits to f a conditional jump to tblock or fblock based on
+// cond, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
+       b := f.currentBlock
+       b.emit(&If{Cond: cond})
+       addEdge(b, tblock)
+       addEdge(b, fblock)
+       f.currentBlock = nil
+}
+
+// emitExtract emits to f an instruction to extract the index'th
+// component of tuple, ascribing it type typ.  It returns the
+// extracted value.
+//
+func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
+       e := &Extract{Tuple: tuple, Index: index}
+       // In all cases but one (tSelect's recv), typ is redundant w.r.t.
+       // tuple.Type().(*types.Result).Values[index].Type.
+       e.setType(typ)
+       return f.emit(e)
+}
+
+// emitTailCall emits to f a function call in tail position.
+// Postcondition: f.currentBlock is nil.
+//
+func emitTailCall(f *Function, call *Call) {
+       tuple := f.emit(call)
+       var ret Ret
+       switch {
+       case len(f.Signature.Results) > 1:
+               for i, o := range call.Type().(*types.Result).Values {
+                       v := emitExtract(f, tuple, i, o.Type)
+                       // TODO(adonovan): in principle, this is required:
+                       //   v = emitConv(f, o.Type, f.Signature.Results[i].Type)
+                       // but in practice emitTailCall is only used when
+                       // the types exactly match.
+                       ret.Results = append(ret.Results, v)
+               }
+       case len(f.Signature.Results) == 1:
+               ret.Results = []Value{tuple}
+       default:
+               // no-op
+       }
+       f.emit(&ret)
+       f.currentBlock = nil
+}
+
+// emitSelfLoop emits to f a self-loop.
+// This is a defensive measure to ensure control-flow integrity.
+// It should never be reachable.
+// Postcondition: f.currentBlock is nil.
+//
+func emitSelfLoop(f *Function) {
+       loop := f.newBasicBlock("selfloop")
+       emitJump(f, loop)
+       f.currentBlock = loop
+       emitJump(f, loop)
+}
index 6af5e1efcd5495dc4e3640cd38664d1cdcfadf6f..507eb7c329af90142978ce3fe5b84aa57c0b680b 100644 (file)
@@ -10,18 +10,6 @@ import (
        "os"
 )
 
-// Mode bits for additional diagnostics and checking.
-// TODO(adonovan): move these to builder.go once submitted.
-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
-)
-
 // addEdge adds a control-flow graph edge from from to to.
 func addEdge(from, to *BasicBlock) {
        from.Succs = append(from.Succs, to)
@@ -182,8 +170,8 @@ func (f *Function) addSpilledParam(obj types.Object) {
 // Otherwise, idents is ignored and the usual set-up for Go source
 // functions is skipped.
 //
-func (f *Function) start(mode BuilderMode, idents map[*ast.Ident]types.Object) {
-       if mode&LogSource != 0 {
+func (f *Function) start(idents map[*ast.Ident]types.Object) {
+       if f.Prog.mode&LogSource != 0 {
                fmt.Fprintf(os.Stderr, "build function %s @ %s\n", f.FullName(), f.Prog.Files.Position(f.Pos))
        }
        f.currentBlock = f.newBasicBlock("entry")
@@ -226,7 +214,7 @@ func (f *Function) start(mode BuilderMode, idents map[*ast.Ident]types.Object) {
 }
 
 // finish() finalizes the function after SSA code generation of its body.
-func (f *Function) finish(mode BuilderMode) {
+func (f *Function) finish() {
        f.objects = nil
        f.results = nil
        f.currentBlock = nil
@@ -269,13 +257,13 @@ func (f *Function) finish(mode BuilderMode) {
        }
        optimizeBlocks(f)
 
-       if mode&LogFunctions != 0 {
+       if f.Prog.mode&LogFunctions != 0 {
                f.DumpTo(os.Stderr)
        }
-       if mode&SanityCheckFunctions != 0 {
+       if f.Prog.mode&SanityCheckFunctions != 0 {
                MustSanityCheck(f, nil)
        }
-       if mode&LogSource != 0 {
+       if f.Prog.mode&LogSource != 0 {
                fmt.Fprintf(os.Stderr, "build function %s done\n", f.FullName())
        }
 }
@@ -345,6 +333,46 @@ func (f *Function) emit(instr Instruction) Value {
        return f.currentBlock.emit(instr)
 }
 
+// FullName returns the full name of this function, qualified by
+// package name, receiver type, etc.
+//
+// Examples:
+//      "math.IsNaN"                // a package-level function
+//      "(*sync.WaitGroup).Add"     // a declared method
+//      "(*exp/ssa.Ret).Block"      // a bridge method
+//      "(ssa.Instruction).Block"   // an interface method thunk
+//      "func@5.32"                 // an anonymous function
+//
+func (f *Function) FullName() string {
+       // Anonymous?
+       if f.Enclosing != nil {
+               return f.Name_
+       }
+
+       recv := f.Signature.Recv
+
+       // Synthetic?
+       if f.Pkg == nil {
+               if recv != nil {
+                       // TODO(adonovan): print type package-qualified, if NamedType.
+                       return fmt.Sprintf("(%s).%s", recv.Type, f.Name_) // bridge method
+               }
+               return fmt.Sprintf("(%s).%s", f.Params[0].Type(), f.Name_) // interface method thunk
+       }
+
+       // Declared method?
+       if recv != nil {
+               star := ""
+               if isPointer(recv.Type) {
+                       star = "*"
+               }
+               return fmt.Sprintf("(%s%s.%s).%s", star, f.Pkg.ImportPath, deref(recv.Type), f.Name_)
+       }
+
+       // Package-level function.
+       return fmt.Sprintf("%s.%s", f.Pkg.ImportPath, f.Name_)
+}
+
 // DumpTo prints to w a human readable "disassembly" of the SSA code of
 // all basic blocks of function f.
 //
@@ -364,18 +392,21 @@ func (f *Function) DumpTo(w io.Writer) {
                }
        }
 
+       io.WriteString(w, "func ")
        params := f.Params
        if f.Signature.Recv != nil {
-               fmt.Fprintf(w, "func (%s) %s(", params[0].Name(), f.Name())
+               fmt.Fprintf(w, "(%s %s) ", params[0].Name(), params[0].Type())
                params = params[1:]
-       } else {
-               fmt.Fprintf(w, "func %s(", f.Name())
        }
+       io.WriteString(w, f.Name())
+       io.WriteString(w, "(")
        for i, v := range params {
                if i > 0 {
                        io.WriteString(w, ", ")
                }
                io.WriteString(w, v.Name())
+               io.WriteString(w, " ")
+               io.WriteString(w, v.Type().String())
        }
        io.WriteString(w, "):\n")
 
diff --git a/src/pkg/exp/ssa/importer.go b/src/pkg/exp/ssa/importer.go
new file mode 100644 (file)
index 0000000..ce73323
--- /dev/null
@@ -0,0 +1,110 @@
+package ssa
+
+// This file defines an implementation of the types.Importer interface
+// (func) that loads the transitive closure of dependencies of a
+// "main" package.
+
+import (
+       "go/ast"
+       "go/build"
+       "go/parser"
+       "go/token"
+       "go/types"
+       "os"
+       "path/filepath"
+)
+
+// Prototype of a function that locates, reads and parses a set of
+// source files given an import path.
+//
+// fset is the fileset to which the ASTs should be added.
+// path is the imported path, e.g. "sync/atomic".
+//
+// On success, the function returns files, the set of ASTs produced,
+// or the first error encountered.
+//
+type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)
+
+// doImport loads the typechecker package identified by path
+// Implements the types.Importer prototype.
+//
+func (b *Builder) doImport(imports map[string]*types.Package, path string) (typkg *types.Package, err error) {
+       // Package unsafe is handled specially, and has no ssa.Package.
+       if path == "unsafe" {
+               return types.Unsafe, nil
+       }
+
+       if pkg := b.Prog.Packages[path]; pkg != nil {
+               typkg = pkg.Types
+               imports[path] = typkg
+               return // positive cache hit
+       }
+
+       if err = b.importErrs[path]; err != nil {
+               return // negative cache hit
+       }
+       var files []*ast.File
+       if b.mode&UseGCImporter != 0 {
+               typkg, err = types.GcImport(imports, path)
+       } else {
+               files, err = b.loader(b.Prog.Files, path)
+               if err == nil {
+                       typkg, err = b.typechecker.Check(b.Prog.Files, files)
+               }
+       }
+       if err != nil {
+               // Cache failure
+               b.importErrs[path] = err
+               return nil, err
+       }
+
+       // Cache success
+       imports[path] = typkg                                           // cache for just this package.
+       b.Prog.Packages[path] = b.createPackageImpl(typkg, path, files) // cache across all packages
+
+       return typkg, nil
+}
+
+// GorootLoader is an implementation of the SourceLoader function
+// prototype that loads and parses Go source files from the package
+// directory beneath $GOROOT/src/pkg.
+//
+// TODO(adonovan): get rsc and adg (go/build owners) to review this.
+//
+func GorootLoader(fset *token.FileSet, path string) (files []*ast.File, err error) {
+       // TODO(adonovan): fix: Do we need cwd? Shouldn't ImportDir(path) / $GOROOT suffice?
+       srcDir, err := os.Getwd()
+       if err != nil {
+               return // serious misconfiguration
+       }
+       bp, err := build.Import(path, srcDir, 0)
+       if err != nil {
+               return // import failed
+       }
+       files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
+       if err != nil {
+               return nil, err
+       }
+       return
+}
+
+// ParseFiles parses the Go source files files within directory dir
+// and returns their ASTs, or the first parse error if any.
+//
+// This utility function is provided to facilitate implementing a
+// SourceLoader.
+//
+func ParseFiles(fset *token.FileSet, dir string, files ...string) (parsed []*ast.File, err error) {
+       for _, file := range files {
+               var f *ast.File
+               if !filepath.IsAbs(file) {
+                       file = filepath.Join(dir, file)
+               }
+               f, err = parser.ParseFile(fset, file, nil, parser.DeclarationErrors)
+               if err != nil {
+                       return // parsing failed
+               }
+               parsed = append(parsed, f)
+       }
+       return
+}
diff --git a/src/pkg/exp/ssa/lvalue.go b/src/pkg/exp/ssa/lvalue.go
new file mode 100644 (file)
index 0000000..9ca9f68
--- /dev/null
@@ -0,0 +1,86 @@
+package ssa
+
+// lvalues are the union of addressable expressions and map-index
+// expressions.
+
+import (
+       "go/types"
+)
+
+// An lvalue represents an assignable location that may appear on the
+// left-hand side of an assignment.  This is a generalization of a
+// pointer to permit updates to elements of maps.
+//
+type lvalue interface {
+       store(fn *Function, v Value) // stores v into the location
+       load(fn *Function) Value     // loads the contents of the location
+       typ() types.Type             // returns the type of the location
+}
+
+// An address is an lvalue represented by a true pointer.
+type address struct {
+       addr Value
+}
+
+func (a address) load(fn *Function) Value {
+       return emitLoad(fn, a.addr)
+}
+
+func (a address) store(fn *Function, v Value) {
+       emitStore(fn, a.addr, v)
+}
+
+func (a address) typ() types.Type {
+       return indirectType(a.addr.Type())
+}
+
+// An element is an lvalue represented by m[k], the location of an
+// element of a map or string.  These locations are not addressable
+// since pointers cannot be formed from them, but they do support
+// load(), and in the case of maps, store().
+//
+type element struct {
+       m, k Value      // map or string
+       t    types.Type // map element type or string byte type
+}
+
+func (e *element) load(fn *Function) Value {
+       l := &Lookup{
+               X:     e.m,
+               Index: e.k,
+       }
+       l.setType(e.t)
+       return fn.emit(l)
+}
+
+func (e *element) store(fn *Function, v Value) {
+       fn.emit(&MapUpdate{
+               Map:   e.m,
+               Key:   e.k,
+               Value: emitConv(fn, v, e.t),
+       })
+}
+
+func (e *element) typ() types.Type {
+       return e.t
+}
+
+// A blanks is a dummy variable whose name is "_".
+// It is not reified: loads are illegal and stores are ignored.
+//
+type blank struct{}
+
+func (bl blank) load(fn *Function) Value {
+       panic("blank.load is illegal")
+}
+
+func (bl blank) store(fn *Function, v Value) {
+       // no-op
+}
+
+func (bl blank) typ() types.Type {
+       // TODO(adonovan): this should be the type of the blank Ident;
+       // the typechecker doesn't provide this yet, but fortunately,
+       // we don't need it yet either.
+       panic("blank.typ is unimplemented")
+}
index b8708b6ede46a2bf7e0d9238135acb9d50df43d3..4e55dc9ff5170cdfeab22545b0999149a970cf0d 100644 (file)
@@ -8,6 +8,8 @@ import (
        "fmt"
        "go/ast"
        "go/types"
+       "io"
+       "sort"
 )
 
 func (id Id) String() string {
@@ -67,18 +69,6 @@ func (r *Function) String() string {
        return fmt.Sprintf("function %s : %s", r.Name(), r.Type())
 }
 
-// FullName returns the name of this function qualified by the
-// package name, unless it is anonymous or synthetic.
-//
-// TODO(adonovan): move to func.go when it's submitted.
-//
-func (f *Function) FullName() string {
-       if f.Enclosing != nil || f.Pkg == nil {
-               return f.Name_ // anonymous or synthetic
-       }
-       return fmt.Sprintf("%s.%s", f.Pkg.ImportPath, f.Name_)
-}
-
 // FullName returns g's package-qualified name.
 func (g *Global) FullName() string {
        return fmt.Sprintf("%s.%s", g.Pkg.ImportPath, g.Name_)
@@ -340,39 +330,51 @@ func (s *MapUpdate) String() string {
 }
 
 func (p *Package) String() string {
-       // TODO(adonovan): prettify output.
-       var b bytes.Buffer
-       fmt.Fprintf(&b, "Package %s at %s:\n", p.ImportPath, p.Prog.Files.File(p.Pos).Name())
+       return "Package " + p.ImportPath
+}
+
+func (p *Package) DumpTo(w io.Writer) {
+       fmt.Fprintf(w, "Package %s at %s:\n", p.ImportPath, p.Prog.Files.File(p.Pos).Name())
 
-       // TODO(adonovan): make order deterministic.
+       var names []string
        maxname := 0
        for name := range p.Members {
                if l := len(name); l > maxname {
                        maxname = l
                }
+               names = append(names, name)
        }
 
-       for name, mem := range p.Members {
-               switch mem := mem.(type) {
+       sort.Strings(names)
+       for _, name := range names {
+               switch mem := p.Members[name].(type) {
                case *Literal:
-                       fmt.Fprintf(&b, " const %-*s %s\n", maxname, name, mem.Name())
+                       fmt.Fprintf(w, "  const %-*s %s\n", maxname, name, mem.Name())
 
                case *Function:
-                       fmt.Fprintf(&b, " func  %-*s %s\n", maxname, name, mem.Type())
+                       fmt.Fprintf(w, "  func  %-*s %s\n", maxname, name, mem.Type())
 
                case *Type:
-                       fmt.Fprintf(&b, " type  %-*s %s\n", maxname, name, mem.NamedType.Underlying)
-                       // TODO(adonovan): make order deterministic.
-                       for name, method := range mem.Methods {
-                               fmt.Fprintf(&b, "       method %s %s\n", name, method.Signature)
+                       fmt.Fprintf(w, "  type  %-*s %s\n", maxname, name, mem.NamedType.Underlying)
+                       // We display only PtrMethods since its keys
+                       // are a superset of Methods' keys, though the
+                       // methods themselves may differ,
+                       // e.g. different bridge methods.
+                       var keys ids
+                       for id := range mem.PtrMethods {
+                               keys = append(keys, id)
+                       }
+                       sort.Sort(keys)
+                       for _, id := range keys {
+                               method := mem.PtrMethods[id]
+                               fmt.Fprintf(w, "    method %s %s\n", id, method.Signature)
                        }
 
                case *Global:
-                       fmt.Fprintf(&b, " var   %-*s %s\n", maxname, name, mem.Type())
+                       fmt.Fprintf(w, "  var   %-*s %s\n", maxname, name, mem.Type())
 
                }
        }
-       return b.String()
 }
 
 func commaOk(x bool) string {
diff --git a/src/pkg/exp/ssa/promote.go b/src/pkg/exp/ssa/promote.go
new file mode 100644 (file)
index 0000000..7438f4d
--- /dev/null
@@ -0,0 +1,450 @@
+package ssa
+
+// This file defines algorithms related to "promotion" of field and
+// method selector expressions e.x, such as desugaring implicit field
+// and method selections, method-set computation, and construction of
+// synthetic "bridge" methods.
+
+import (
+       "fmt"
+       "go/types"
+       "os"
+)
+
+// anonFieldPath is a linked list of anonymous fields entered by
+// breadth-first traversal has entered, rightmost (outermost) first.
+// e.g. "e.f" denoting "e.A.B.C.f" would have a path [C, B, A].
+// Common tails may be shared.
+//
+// It is used by various "promotion"-related algorithms.
+//
+type anonFieldPath struct {
+       tail  *anonFieldPath
+       index int // index of field within enclosing types.Struct.Fields
+       field *types.Field
+}
+
+func (p *anonFieldPath) contains(f *types.Field) bool {
+       for ; p != nil; p = p.tail {
+               if p.field == f {
+                       return true
+               }
+       }
+       return false
+}
+
+// reverse returns the linked list reversed, as a slice.
+func (p *anonFieldPath) reverse() []*anonFieldPath {
+       n := 0
+       for q := p; q != nil; q = q.tail {
+               n++
+       }
+       s := make([]*anonFieldPath, n)
+       n = 0
+       for ; p != nil; p = p.tail {
+               s[len(s)-1-n] = p
+               n++
+       }
+       return s
+}
+
+// isIndirect returns true if the path indirects a pointer.
+func (p *anonFieldPath) isIndirect() bool {
+       for ; p != nil; p = p.tail {
+               if isPointer(p.field.Type) {
+                       return true
+               }
+       }
+       return false
+}
+
+// Method Set construction ----------------------------------------
+
+// A candidate is a method eligible for promotion: a method of an
+// abstract (interface) or concrete (anonymous struct or named) type,
+// along with the anonymous field path via which it is implicitly
+// reached.  If there is exactly one candidate for a given id, it will
+// be promoted to membership of the original type's method-set.
+//
+// Candidates with path=nil are trivially members of the original
+// type's method-set.
+//
+type candidate struct {
+       method   *types.Method  // method object of abstract or concrete type
+       concrete *Function      // actual method (iff concrete)
+       path     *anonFieldPath // desugared selector path
+}
+
+// For debugging.
+func (c candidate) String() string {
+       s := ""
+       // Inefficient!
+       for p := c.path; p != nil; p = p.tail {
+               s = "." + p.field.Name + s
+       }
+       return "@" + s + "." + c.method.Name
+}
+
+// ptrRecv returns true if this candidate has a pointer receiver.
+func (c candidate) ptrRecv() bool {
+       return c.concrete != nil && isPointer(c.concrete.Signature.Recv.Type)
+}
+
+// MethodSet returns the method set for type typ,
+// building bridge methods as needed for promoted methods.
+// A nil result indicates an empty set.
+//
+// Thread-safe.  TODO(adonovan): explain concurrency invariants in detail.
+func (p *Program) MethodSet(typ types.Type) MethodSet {
+       if !canHaveConcreteMethods(typ, true) {
+               return nil
+       }
+
+       p.methodSetsMu.Lock()
+       defer p.methodSetsMu.Unlock()
+
+       // TODO(adonovan): Using Types as map keys doesn't properly
+       // de-dup.  e.g. *NamedType are canonical but *Struct and
+       // others are not.  Need to de-dup based on using a two-level
+       // hash-table with hash function types.Type.String and
+       // equivalence relation types.IsIdentical.
+       mset := p.methodSets[typ]
+       if mset == nil {
+               mset = buildMethodSet(p, typ)
+               p.methodSets[typ] = mset
+       }
+       return mset
+}
+
+// buildMethodSet computes the concrete method set for type typ.
+// It is the implementation of Program.MethodSet.
+//
+func buildMethodSet(prog *Program, typ types.Type) MethodSet {
+       if prog.mode&LogSource != 0 {
+               // TODO(adonovan): this isn't quite appropriate for LogSource
+               fmt.Fprintf(os.Stderr, "buildMethodSet %s %T\n", typ, typ)
+       }
+
+       // cands maps ids (field and method names) encountered at any
+       // level of of the breadth-first traversal to a unique
+       // promotion candidate.  A nil value indicates a "blocked" id
+       // (i.e. a field or ambiguous method).
+       //
+       // nextcands is the same but carries just the level in progress.
+       cands, nextcands := make(map[Id]*candidate), make(map[Id]*candidate)
+
+       var next, list []*anonFieldPath
+       list = append(list, nil) // hack: nil means "use typ"
+
+       // For each level of the type graph...
+       for len(list) > 0 {
+               // Invariant: next=[], nextcands={}.
+
+               // Collect selectors from one level into 'nextcands'.
+               // Record the next levels into 'next'.
+               for _, node := range list {
+                       t := typ // first time only
+                       if node != nil {
+                               t = node.field.Type
+                       }
+                       t = deref(t)
+
+                       if nt, ok := t.(*types.NamedType); ok {
+                               for _, meth := range nt.Methods {
+                                       addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, prog.concreteMethods[meth], node)
+                               }
+                               t = nt.Underlying
+                       }
+
+                       switch t := t.(type) {
+                       case *types.Interface:
+                               for _, meth := range t.Methods {
+                                       addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, nil, node)
+                               }
+
+                       case *types.Struct:
+                               for i, f := range t.Fields {
+                                       nextcands[IdFromQualifiedName(f.QualifiedName)] = nil // a field: block id
+                                       // Queue up anonymous fields for next iteration.
+                                       // Break cycles to ensure termination.
+                                       if f.IsAnonymous && !node.contains(f) {
+                                               next = append(next, &anonFieldPath{node, i, f})
+                                       }
+                               }
+                       }
+               }
+
+               // Examine collected selectors.
+               // Promote unique, non-blocked ones to cands.
+               for id, cand := range nextcands {
+                       delete(nextcands, id)
+                       if cand == nil {
+                               // Update cands so we ignore it at all deeper levels.
+                               // Don't clobber existing (shallower) binding!
+                               if _, ok := cands[id]; !ok {
+                                       cands[id] = nil // block id
+                               }
+                               continue
+                       }
+                       if _, ok := cands[id]; ok {
+                               // Ignore candidate: a shallower binding exists.
+                       } else {
+                               cands[id] = cand
+                       }
+               }
+               list, next = next, list[:0] // reuse array
+       }
+
+       // Build method sets and bridge methods.
+       mset := make(MethodSet)
+       for id, cand := range cands {
+               if cand == nil {
+                       continue // blocked; ignore
+               }
+               if cand.ptrRecv() && !(isPointer(typ) || cand.path.isIndirect()) {
+                       // A candidate concrete method f with receiver
+                       // *C is promoted into the method set of
+                       // (non-pointer) E iff the implicit path selection
+                       // is indirect, e.g. e.A->B.C.f
+                       continue
+               }
+               var method *Function
+               if cand.path == nil {
+                       // Trivial member of method-set; no bridge needed.
+                       method = cand.concrete
+               } else {
+                       method = makeBridgeMethod(prog, typ, cand)
+               }
+               if method == nil {
+                       panic("unexpected nil method in method set")
+               }
+               mset[id] = method
+       }
+       return mset
+}
+
+// addCandidate adds the promotion candidate (method, node) to m[id].
+// If m[id] already exists (whether nil or not), m[id] is set to nil.
+// If method denotes a concrete method, concrete is its implementation.
+//
+func addCandidate(m map[Id]*candidate, id Id, method *types.Method, concrete *Function, node *anonFieldPath) {
+       prev, found := m[id]
+       switch {
+       case prev != nil:
+               // Two candidates for same selector: ambiguous; block it.
+               m[id] = nil
+       case found:
+               // Already blocked.
+       default:
+               // A viable candidate.
+               m[id] = &candidate{method, concrete, node}
+       }
+}
+
+// makeBridgeMethod creates a synthetic Function that delegates to a
+// "promoted" method.  For example, given these decls:
+//
+//    type A struct {B}
+//    type B struct {*C}
+//    type C ...
+//    func (*C) f()
+//
+// then makeBridgeMethod(typ=A, cand={method:(*C).f, path:[B,*C]}) will
+// synthesize this bridge method:
+//
+//    func (a A) f() { return a.B.C->f() }
+//
+// prog is the program to which the synthesized method will belong.
+// typ is the receiver type of the bridge method.  cand is the
+// candidate method to be promoted; it may be concrete or an interface
+// method.
+//
+func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function {
+       sig := *cand.method.Type // make a copy, sharing underlying Values
+       sig.Recv = &types.Var{Name: "recv", Type: typ}
+
+       if prog.mode&LogSource != 0 {
+               fmt.Fprintf(os.Stderr, "makeBridgeMethod %s, %s, type %s\n", typ, cand, &sig)
+       }
+
+       fn := &Function{
+               Name_:     cand.method.Name,
+               Signature: &sig,
+               Prog:      prog,
+       }
+       fn.start(nil)
+       fn.addSpilledParam(sig.Recv)
+       // TODO(adonovan): fix: test variadic case---careful with types.
+       for _, p := range fn.Signature.Params {
+               fn.addParam(p.Name, p.Type)
+       }
+
+       // Each bridge method performs a sequence of selections,
+       // then tailcalls the promoted method.
+       // We use pointer arithmetic (FieldAddr possibly followed by
+       // Load) in preference to value extraction (Field possibly
+       // preceded by Load).
+       var v Value = fn.Locals[0] // spilled receiver
+       if isPointer(typ) {
+               v = emitLoad(fn, v)
+       }
+       // Iterate over selections e.A.B.C.f in the natural order [A,B,C].
+       for _, p := range cand.path.reverse() {
+               // Loop invariant: v holds a pointer to a struct.
+               if _, ok := underlyingType(indirectType(v.Type())).(*types.Struct); !ok {
+                       panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type))
+               }
+               sel := &FieldAddr{
+                       X:     v,
+                       Field: p.index,
+               }
+               sel.setType(pointer(p.field.Type))
+               v = fn.emit(sel)
+               if isPointer(p.field.Type) {
+                       v = emitLoad(fn, v)
+               }
+       }
+       if !cand.ptrRecv() {
+               v = emitLoad(fn, v)
+       }
+
+       var c Call
+       if cand.concrete != nil {
+               c.Func = cand.concrete
+               fn.Pos = c.Func.(*Function).Pos // TODO(adonovan): fix: wrong.
+               c.Pos = fn.Pos                  // TODO(adonovan): fix: wrong.
+               c.Args = append(c.Args, v)
+               for _, arg := range fn.Params[1:] {
+                       c.Args = append(c.Args, arg)
+               }
+       } else {
+               c.Recv = v
+               c.Method = 0
+               for _, arg := range fn.Params {
+                       c.Args = append(c.Args, arg)
+               }
+       }
+       c.Type_ = &types.Result{Values: sig.Results}
+       emitTailCall(fn, &c)
+       fn.finish()
+       return fn
+}
+
+// Thunks for standalone interface methods ----------------------------------------
+
+// makeImethodThunk returns a synthetic thunk function permitting an
+// method id of interface typ to be called like a standalone function,
+// e.g.:
+//
+//   type I interface { f(x int) R }
+//   m := I.f  // thunk
+//   var i I
+//   m(i, 0)
+//
+// The thunk is defined as if by:
+//
+//   func I.f(i I, x int, ...) R {
+//     return i.f(x, ...)
+//   }
+//
+// The generated thunks do not belong to any package.  (Arguably they
+// belong in the package that defines the interface, but we have no
+// way to determine that on demand; we'd have to create all possible
+// thunks a priori.)
+//
+// TODO(adonovan): opt: currently the stub is created even when used
+// in call position: I.f(i, 0).  Clearly this is suboptimal.
+//
+// TODO(adonovan): memoize creation of these functions in the Program.
+//
+func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
+       if prog.mode&LogSource != 0 {
+               fmt.Fprintf(os.Stderr, "makeImethodThunk %s.%s\n", typ, id)
+       }
+       itf := underlyingType(typ).(*types.Interface)
+       index, meth := methodIndex(itf, itf.Methods, id)
+       sig := *meth.Type // copy; shared Values
+       fn := &Function{
+               Name_:     meth.Name,
+               Signature: &sig,
+               Prog:      prog,
+       }
+       // TODO(adonovan): set fn.Pos to location of interface method ast.Field.
+       fn.start(nil)
+       fn.addParam("recv", typ)
+       // TODO(adonovan): fix: test variadic case---careful with types.
+       for _, p := range fn.Signature.Params {
+               fn.addParam(p.Name, p.Type)
+       }
+       var c Call
+       c.Method = index
+       c.Recv = fn.Params[0]
+       for _, arg := range fn.Params[1:] {
+               c.Args = append(c.Args, arg)
+       }
+       c.Type_ = &types.Result{Values: sig.Results}
+       emitTailCall(fn, &c)
+       fn.finish()
+       return fn
+}
+
+// Implicit field promotion ----------------------------------------
+
+// For a given struct type and (promoted) field Id, findEmbeddedField
+// returns the path of implicit anonymous field selections, and the
+// field index of the explicit (=outermost) selection.
+//
+// TODO(gri): if go/types/operand.go's lookupFieldBreadthFirst were to
+// record (e.g. call a client-provided callback) the implicit field
+// selection path discovered for a particular ast.SelectorExpr, we could
+// eliminate this function.
+//
+func findPromotedField(st *types.Struct, id Id) (*anonFieldPath, int) {
+       // visited records the types that have been searched already.
+       // Invariant: keys are all *types.NamedType.
+       // (types.Type is not a sound map key in general.)
+       visited := make(map[types.Type]bool)
+
+       var list, next []*anonFieldPath
+       for i, f := range st.Fields {
+               if f.IsAnonymous {
+                       list = append(next, &anonFieldPath{nil, i, f})
+               }
+       }
+
+       // Search the current level if there is any work to do and collect
+       // embedded types of the next lower level in the next list.
+       for {
+               // look for name in all types at this level
+               for _, node := range list {
+                       typ := deref(node.field.Type).(*types.NamedType)
+                       if visited[typ] {
+                               continue
+                       }
+                       visited[typ] = true
+
+                       switch typ := typ.Underlying.(type) {
+                       case *types.Struct:
+                               for i, f := range typ.Fields {
+                                       if IdFromQualifiedName(f.QualifiedName) == id {
+                                               return node, i
+                                       }
+                               }
+                               for i, f := range typ.Fields {
+                                       if f.IsAnonymous {
+                                               next = append(next, &anonFieldPath{node, i, f})
+                                       }
+                               }
+
+                       }
+               }
+
+               if len(next) == 0 {
+                       panic("field not found: " + id.String())
+               }
+
+               // No match so far.
+               list, next = next, list[:0] // reuse arrays
+       }
+       panic("unreachable")
+}
index eb0f7fc0b07904dee3fa9a801cd8116f8f2291bd..2b0b049f3a1dcfae76593aebe0de2deb0bbf555c 100644 (file)
@@ -8,6 +8,7 @@ import (
        "go/ast"
        "go/token"
        "go/types"
+       "sync"
 )
 
 // A Program is a partial or complete Go program converted to SSA form.
@@ -16,18 +17,17 @@ import (
 //
 // TODO(adonovan): synthetic methods for promoted methods and for
 // standalone interface methods do not belong to any package.  Make
-// them enumerable here.
-//
-// TODO(adonovan): MethodSets of types other than named types
-// (i.e. anon structs) are not currently accessible, nor are they
-// memoized.  Add a method: MethodSetForType() which looks in the
-// appropriate Package (for methods of named types) or in
-// Program.AnonStructMethods (for methods of anon structs).
+// them enumerable here so clients can (e.g.) generate code for them.
 //
 type Program struct {
        Files    *token.FileSet            // position information for the files of this Program
        Packages map[string]*Package       // all loaded Packages, keyed by import path
        Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
+
+       methodSets      map[types.Type]MethodSet    // concrete method sets for all needed types  [TODO(adonovan): de-dup]
+       methodSetsMu    sync.Mutex                  // serializes all accesses to methodSets
+       concreteMethods map[*types.Method]*Function // maps named concrete methods to their code
+       mode            BuilderMode                 // set of mode bits
 }
 
 // A Package is a single analyzed Go package, containing Members for
@@ -80,27 +80,18 @@ type Id struct {
        Name string
 }
 
-// A MethodSet contains all the methods whose receiver is either T or
-// *T, for some named or struct type T.
-//
-// TODO(adonovan): the client is required to adapt T<=>*T, e.g. when
-// invoking an interface method.  (This could be simplified for the
-// client by having distinct method sets for T and *T, with the SSA
-// Builder generating wrappers as needed, but probably the client is
-// able to do a better job.)  Document the precise rules the client
-// must follow.
+// A MethodSet contains all the methods for a particular type.
+// The method sets for T and *T are distinct entities.
 //
 type MethodSet map[Id]*Function
 
 // A Type is a Member of a Package representing the name, underlying
 // type and method set of a named type declared at package scope.
 //
-// The method set contains only concrete methods; it is empty for
-// interface types.
-//
 type Type struct {
-       NamedType *types.NamedType
-       Methods   MethodSet
+       NamedType  *types.NamedType
+       Methods    MethodSet // concrete method set of N
+       PtrMethods MethodSet // concrete method set of (*N)
 }
 
 // An SSA value that can be referenced by an instruction.
@@ -479,7 +470,7 @@ type ChangeInterface struct {
 type MakeInterface struct {
        Register
        X       Value
-       Methods MethodSet // method set of (non-interface) X iff converting to interface
+       Methods MethodSet // method set of (non-interface) X
 }
 
 // A MakeClosure instruction yields an anonymous function value whose
index 0d2ebde268a3bfdb3b51bea7f2754e520269da87..a335e94e9262ac5321a27975c13353142a00eff9 100644 (file)
@@ -6,6 +6,7 @@ import (
        "fmt"
        "go/ast"
        "go/types"
+       "reflect"
 )
 
 func unreachable() {
@@ -118,6 +119,26 @@ func objKind(obj types.Object) ast.ObjKind {
        panic(fmt.Sprintf("unexpected Object type: %T", obj))
 }
 
+// canHaveConcreteMethods returns true iff typ may have concrete
+// methods associated with it.  Callers must supply allowPtr=true.
+//
+// TODO(gri): consider putting this in go/types.  It's surprisingly subtle.
+func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool {
+       switch typ := typ.(type) {
+       case *types.Pointer:
+               return allowPtr && canHaveConcreteMethods(typ.Base, false)
+       case *types.NamedType:
+               switch typ.Underlying.(type) {
+               case *types.Pointer, *types.Interface:
+                       return false
+               }
+               return true
+       case *types.Struct:
+               return true
+       }
+       return false
+}
+
 // DefaultType returns the default "typed" type for an "untyped" type;
 // it returns the incoming type for all other types. If there is no
 // corresponding untyped type, the result is types.Typ[types.Invalid].
@@ -158,6 +179,10 @@ func makeId(name string, pkg *types.Package) (id Id) {
        id.Name = name
        if !ast.IsExported(name) {
                id.Pkg = pkg
+               // TODO(gri): fix
+               // if pkg.Path == "" {
+               //      panic("Package " + pkg.Name + "has empty Path")
+               // }
        }
        return
 }
@@ -170,3 +195,16 @@ func makeId(name string, pkg *types.Package) (id Id) {
 func IdFromQualifiedName(qn types.QualifiedName) Id {
        return makeId(qn.Name, qn.Pkg)
 }
+
+type ids []Id // a sortable slice of Id
+
+func (p ids) Len() int { return len(p) }
+func (p ids) Less(i, j int) bool {
+       x, y := p[i], p[j]
+       // *Package pointers are canonical so order by them.
+       // Don't use x.Pkg.ImportPath because sometimes it's empty.
+       // (TODO(gri): fix that.)
+       return reflect.ValueOf(x.Pkg).Pointer() < reflect.ValueOf(y.Pkg).Pointer() ||
+               x.Pkg == y.Pkg && x.Name < y.Name
+}
+func (p ids) Swap(i, j int) { p[i], p[j] = p[j], p[i] }