diff --git a/importer/pkginfo.go b/importer/pkginfo.go index 7c87b4c8ce..d956bfdf19 100644 --- a/importer/pkginfo.go +++ b/importer/pkginfo.go @@ -6,6 +6,7 @@ import ( "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" "go/ast" + "strconv" ) // PackageInfo holds the ASTs and facts derived by the type-checker @@ -24,6 +25,33 @@ type PackageInfo struct { typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases } +// Imports returns the set of packages imported by this one, in source +// order. Callers should not mutate the result. +// +func (info *PackageInfo) Imports() []*types.Package { + var imports []*types.Package + + // We iterate over the syntax (info.Files) not the types + // (info.Pkg.Imports()) because the latter may contain the + // transitive closure of dependencies, e.g. when using GcImporter. + seen := make(map[*types.Package]bool) + for _, file := range info.Files { + for _, imp := range file.Imports { + path, _ := strconv.Unquote(imp.Path.Value) + if path == "unsafe" { + continue // not a true package + } + typkg := info.Pkg.Imports()[path] + if seen[typkg] { + continue // already seen + } + seen[typkg] = true + imports = append(imports, typkg) + } + } + return imports +} + // TypeOf returns the type of expression e. // Precondition: e belongs to the package's ASTs. // diff --git a/ssa/builder.go b/ssa/builder.go index 11f0bba308..0979f547a1 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -1,22 +1,23 @@ package ssa -// This file defines the SSA builder. +// This file implements the BUILD phase of SSA construction. // -// The builder has two phases, CREATE and BUILD. In the CREATE -// phase, all packages are constructed and type-checked and +// SSA construction has two phases, CREATE and BUILD. In the CREATE phase +// (create.go), all packages are constructed and type-checked and // definitions of all package members are created, method-sets are // computed, and bridge methods are synthesized. The create phase // proceeds in topological order over the import dependency graph, -// initiated by client calls to CreatePackage. +// initiated by client calls to CreatePackages. // -// In the BUILD phase, the Builder traverses the AST of each Go source -// function and generates SSA instructions for the function body. +// In the BUILD phase (builder.go), 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 intra-package symbol reference graph, whose roots are the set // of package-level declarations in lexical order. The BUILD phases // for distinct packages are independent and are executed in parallel. // -// The Builder's and Program's indices (maps) are populated and +// The builder's and Program's indices (maps) are populated and // mutated during the CREATE phase, but during the BUILD phase they // remain constant. The sole exception is Prog.methodSets, which is // protected by a dedicated mutex. @@ -26,13 +27,11 @@ import ( "go/ast" "go/token" "os" - "strconv" "sync" "sync/atomic" "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" - "code.google.com/p/go.tools/importer" ) type opaqueType struct { @@ -67,96 +66,10 @@ var ( vFalse = newLiteral(exact.MakeBool(false), tBool) ) -// A Context specifies the supporting context for SSA construction. -// -type Context struct { - // Mode is a bitfield of options controlling verbosity, - // logging and additional sanity checks. - Mode BuilderMode -} - -// 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 - NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers - BuildSerially // Build packages serially, not in parallel. -) - -// A Builder builds the SSA representation of a single Go program: a -// Program containing one or more Packages. -// -// Use NewBuilder to create a Builder. -// -type Builder struct { - Prog *Program // the program being built - Context *Context // the client context -} - -// NewBuilder creates and returns a new SSA builder with options -// specified by context. -// -// For each package loaded by imp, a new SSA Package is created for it -// and added to the Program. All such packages must be error-free. -// -// A typical client will create a Builder with NewBuilder; this causes -// all SSA Packages to be created; then call BuildPackage or -// BuildAllPackages to construct SSA-form code for all functions and -// methods in one or more packages. After that, the representation of -// the program (Builder.Prog) is complete and transitively closed, and -// the Builder and Importer objects can be discarded. The client's -// analysis may then begin. -// -func NewBuilder(context *Context, imp *importer.Importer) *Builder { - prog := &Program{ - Files: imp.Fset, - Packages: make(map[string]*Package), - packages: make(map[*types.Package]*Package), - Builtins: make(map[types.Object]*Builtin), - methodSets: make(map[types.Type]MethodSet), - concreteMethods: make(map[*types.Func]*Function), - mode: context.Mode, - } - - // Create Values for built-in functions. - for i, n := 0, types.Universe.NumEntries(); i < n; i++ { - if obj, ok := types.Universe.At(i).(*types.Func); ok { - prog.Builtins[obj] = &Builtin{obj} - } - } - - b := &Builder{ - Prog: prog, - Context: context, - } - - // TODO(adonovan): split the rest off as a separate method of Prog. - - // Create ssa.Package for each types.Package. - // TODO(adonovan): rethink the API to avoid phase ordering issues. - // (What if the Importer was to load more packages later?) - for path, info := range imp.Packages { - p := b.createPackage(info) - prog.Packages[path] = p - prog.packages[p.Types] = p - } - - // Compute the method sets, now that we have all packages' methods. - for _, pkg := range prog.Packages { - for _, mem := range pkg.Members { - switch t := mem.(type) { - case *Type: - t.Methods = prog.MethodSet(t.NamedType) - t.PtrMethods = prog.MethodSet(pointer(t.NamedType)) - } - } - } - - return b +// builder holds state associated with the package currently being built. +// Its methods contain all the logic for AST-to-SSA conversion. +type builder struct { + nTo1Vars map[*ast.ValueSpec]bool // set of n:1 ValueSpecs already built } // lookup returns the package-level *Function or *Global for the named @@ -167,8 +80,8 @@ func NewBuilder(context *Context, imp *importer.Importer) *Builder { // 'from', the package on whose behalf this lookup occurs, then lookup // emits initialization code into from.Init if not already done. // -func (b *Builder) lookup(from *Package, obj types.Object) Value { - v := b.Prog.Value(obj) +func (b *builder) lookup(from *Package, obj types.Object) Value { + v := from.Prog.Value(obj) switch v := v.(type) { case *Function: if from == v.Pkg { @@ -187,7 +100,7 @@ func (b *Builder) lookup(from *Package, obj types.Object) Value { // // Postcondition: fn.currentBlock is nil. // -func (b *Builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) { +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) @@ -234,7 +147,7 @@ func (b *Builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) { // ||-expression whose reified boolean value is wanted. // The value is returned. // -func (b *Builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { +func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { rhs := fn.newBasicBlock("binop.rhs") done := fn.newBasicBlock("binop.done") @@ -289,7 +202,7 @@ func (b *Builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { // TypeAssertExpr, IndexExpr (when X is a map), and UnaryExpr (when Op // is token.ARROW). // -func (b *Builder) exprN(fn *Function, e ast.Expr) Value { +func (b *builder) exprN(fn *Function, e ast.Expr) Value { var typ types.Type var tuple Value switch e := e.(type) { @@ -354,7 +267,7 @@ func (b *Builder) exprN(fn *Function, e ast.Expr) Value { // 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, pos token.Pos) Value { +func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.Type, pos token.Pos) Value { switch name { case "make": switch typ.Underlying().(type) { @@ -425,14 +338,14 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types. // indicates whether the caller intends to use the resulting pointer // in a potentially escaping way. // -func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value { +func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value { id := MakeId(e.Sel.Name, fn.Pkg.Types) // Bound method closure? (e.m where m is a method) if !wantAddr { if m, recv := b.findMethod(fn, e.X, id); m != nil { c := &MakeClosure{ - Fn: makeBoundMethodThunk(b.Prog, m, recv), + Fn: makeBoundMethodThunk(fn.Prog, m, recv), Bindings: []Value{recv}, } c.setPos(e.Sel.Pos()) @@ -473,7 +386,7 @@ func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping // // (fieldType can be derived from base+index.) // -func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos, escaping bool) Value { +func (b *builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos, escaping bool) Value { var x Value if path != nil { switch path.field.Type.Underlying().(type) { @@ -506,7 +419,7 @@ func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, in // // (fieldType can be derived from base+index.) // -func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos) Value { +func (b *builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos) Value { var x Value if path != nil { x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos) @@ -558,7 +471,7 @@ func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, in // - 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 { +func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { switch e := e.(type) { case *ast.Ident: obj := fn.Pkg.objectOf(e) @@ -637,7 +550,7 @@ func (b *Builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { // 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) { +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() @@ -664,7 +577,7 @@ func (b *Builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) { // 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 { +func (b *builder) expr(fn *Function, e ast.Expr) Value { if v := fn.Pkg.info.ValueOf(e); v != nil { return newLiteral(v, fn.Pkg.typeOf(e)) } @@ -674,14 +587,14 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { panic("non-constant BasicLit") // unreachable case *ast.FuncLit: - posn := b.Prog.Files.Position(e.Type.Func) + posn := fn.Prog.Files.Position(e.Type.Func) fn2 := &Function{ name: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column), Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature), pos: e.Type.Func, Enclosing: fn, Pkg: fn.Pkg, - Prog: b.Prog, + Prog: fn.Prog, syntax: &funcSyntax{ paramFields: e.Type.Params, resultFields: e.Type.Results, @@ -803,7 +716,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { obj := fn.Pkg.objectOf(e) // Universal built-in? if obj.Pkg() == nil { - return b.Prog.Builtins[obj] + return fn.Prog.Builtins[obj] } // Package-level func or var? if v := b.lookup(fn.Pkg, obj); v != nil { @@ -825,12 +738,12 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { if fn.Pkg.info.IsType(e.X) { id := MakeId(e.Sel.Name, fn.Pkg.Types) typ := fn.Pkg.typeOf(e.X) - if m := b.Prog.MethodSet(typ)[id]; m != nil { + if m := fn.Prog.MethodSet(typ)[id]; m != nil { return m } // T must be an interface; return method thunk. - return makeImethodThunk(b.Prog, typ, id) + return makeImethodThunk(fn.Prog, typ, id) } // e.f where e is an expression. f may be a method. @@ -885,7 +798,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value { } // stmtList emits to fn code for all statements in list. -func (b *Builder) stmtList(fn *Function, list []ast.Stmt) { +func (b *builder) stmtList(fn *Function, list []ast.Stmt) { for _, s := range list { b.stmt(fn, s) } @@ -898,11 +811,11 @@ func (b *Builder) stmtList(fn *Function, list []ast.Stmt) { // // findMethod returns (nil, nil) if no such method was found. // -func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) { +func (b *builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) { typ := fn.Pkg.typeOf(base) // Consult method-set of X. - if m := b.Prog.MethodSet(typ)[id]; m != nil { + if m := fn.Prog.MethodSet(typ)[id]; m != nil { aptr := isPointer(typ) fptr := isPointer(m.Signature.Recv().Type()) if aptr == fptr { @@ -915,7 +828,7 @@ func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Val } if !isPointer(typ) { // Consult method-set of *X. - if m := b.Prog.MethodSet(pointer(typ))[id]; m != nil { + if m := fn.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. @@ -930,7 +843,7 @@ func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Val // (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) { +func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { c.pos = e.Lparen c.HasEllipsis = e.Ellipsis != 0 @@ -1003,7 +916,7 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { // a (possibly built-in) function of effective type sig. // The argument values are appended to args, which is then returned. // -func (b *Builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value { +func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value { // f(x, y, z...): pass slice z straight through. if e.Ellipsis != 0 { for i, arg := range e.Args { @@ -1078,7 +991,7 @@ func (b *Builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx // 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) { +func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { // First deal with the f(...) part and optional receiver. b.setCallFunc(fn, e, c) @@ -1091,7 +1004,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { } // 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) { +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(), token.NoPos)) } @@ -1099,9 +1012,9 @@ func (b *Builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token) // 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.Prog.Value(obj) +// Precondition: g == g.Prog.Value(obj) // -func (b *Builder) buildGlobal(g *Global, obj types.Object) { +func (b *builder) buildGlobal(g *Global, obj types.Object) { spec := g.spec if spec == nil { return // already built (or in progress) @@ -1122,7 +1035,7 @@ func (b *Builder) buildGlobal(g *Global, obj types.Object) { // 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.Prog.Value(obj) +// Precondition: g == g.Prog.Value(obj) // // Package-level var initialization order is quite subtle. // The side effects of: @@ -1151,7 +1064,7 @@ func (b *Builder) buildGlobal(g *Global, obj types.Object) { // // See also localValueSpec. // -func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global, obj types.Object) { +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 @@ -1169,7 +1082,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global } else { // Mode B: initialize all globals. if !isBlankIdent(id) { - g2 := b.Prog.Value(init.Pkg.objectOf(id)).(*Global) + g2 := init.Prog.Value(init.Pkg.objectOf(id)).(*Global) if g2.spec == nil { continue // already done } @@ -1177,7 +1090,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global lval = address{addr: g2} } } - if b.Context.Mode&LogSource != 0 { + if init.Prog.mode&LogSource != 0 { fmt.Fprintln(os.Stderr, "build global", id.Name) } b.exprInPlace(init, lval, spec.Values[i]) @@ -1194,16 +1107,16 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global // e.g. var x, _, y = f() // n:1 assignment. // Only the first time for a given SPEC has any effect. - if !init.Pkg.nTo1Vars[spec] { - init.Pkg.nTo1Vars[spec] = true - if b.Context.Mode&LogSource != 0 { + if !b.nTo1Vars[spec] { + b.nTo1Vars[spec] = true + if init.Prog.mode&LogSource != 0 { defer logStack("build globals %s", spec.Names)() } tuple := b.exprN(init, spec.Values[0]) result := tuple.Type().(*types.Tuple) for i, id := range spec.Names { if !isBlankIdent(id) { - g := b.Prog.Value(init.Pkg.objectOf(id)).(*Global) + g := init.Prog.Value(init.Pkg.objectOf(id)).(*Global) g.spec = nil // just an optimization emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type())) } @@ -1219,7 +1132,7 @@ func (b *Builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global // 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) { +func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { switch { case len(spec.Values) == len(spec.Names): // e.g. var x, y = 0, 1 @@ -1259,7 +1172,7 @@ func (b *Builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { // // Note the similarity with localValueSpec. // -func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) { +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 { @@ -1308,7 +1221,7 @@ func (b *Builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) { } // arrayLen returns the length of the array whose composite literal elements are elts. -func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 { +func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 { var max int64 = -1 var i int64 = -1 for _, e := range elts { @@ -1329,7 +1242,7 @@ func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 { // Nested composite literals are recursively initialized in place // where possible. // -func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) { +func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ types.Type) { // TODO(adonovan): document how and why typ ever differs from // fn.Pkg.typeOf(e). @@ -1427,7 +1340,7 @@ func (b *Builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ // 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) { +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. @@ -1513,7 +1426,7 @@ func (b *Builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) { // 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) { +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. @@ -1629,7 +1542,7 @@ func (b *Builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl // 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) { +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): opt: is this optimization worth its weight? @@ -1759,7 +1672,7 @@ func (b *Builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { // 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) { +func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) { // ...init... // jump loop // loop: @@ -1815,7 +1728,7 @@ func (b *Builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) { // over array, *array or slice value x. // The v result is defined only if tv is non-nil. // -func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value, loop, done *BasicBlock) { +func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value, loop, done *BasicBlock) { // // length = len(x) // index = -1 @@ -1841,7 +1754,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value } else { // length = len(x). var c Call - c.Call.Func = b.Prog.Builtins[types.Universe.Lookup(nil, "len")] + c.Call.Func = fn.Prog.Builtins[types.Universe.Lookup(nil, "len")] c.Call.Args = []Value{x} c.setType(tInt) length = fn.emit(&c) @@ -1906,7 +1819,7 @@ func (b *Builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value // tk and tv are the types of the key/value results k and v, or nil // if the respective component is not wanted. // -func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) { +func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) { // // it = range x // loop: (target of continue) @@ -1969,7 +1882,7 @@ func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token. // tk is the channel's element type, or nil if the k result is // not wanted // -func (b *Builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop, done *BasicBlock) { +func (b *builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop, done *BasicBlock) { // // loop: (target of continue) // ko = <-x (key, ok) @@ -2007,7 +1920,7 @@ func (b *Builder) rangeChan(fn *Function, x Value, tk types.Type) (k Value, loop // 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) { +func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { var tk, tv types.Type if !isBlankIdent(s.Key) { tk = fn.Pkg.typeOf(s.Key) @@ -2081,7 +1994,7 @@ func (b *Builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) { } // stmt lowers statement s to SSA form, emitting code to fn. -func (b *Builder) stmt(fn *Function, _s ast.Stmt) { +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. @@ -2289,7 +2202,7 @@ start: } // buildFunction builds SSA code for the body of function fn. Idempotent. -func (b *Builder) buildFunction(fn *Function) { +func (b *builder) buildFunction(fn *Function) { if fn.Blocks != nil { return // building already started } @@ -2316,7 +2229,7 @@ func (b *Builder) buildFunction(fn *Function) { } return } - if b.Context.Mode&LogSource != 0 { + if fn.Prog.mode&LogSource != 0 { defer logStack("build function %s @ %s", fn.FullName(), fn.Prog.Files.Position(fn.pos))() } @@ -2332,189 +2245,10 @@ func (b *Builder) buildFunction(fn *Function) { fn.finishBody() } -// 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. -// -// (CREATE phase.) -// -func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { - name := obj.Name() - switch obj := obj.(type) { - case *types.TypeName: - pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)} - - case *types.Const: - pkg.Members[name] = &Constant{ - name: name, - Value: newLiteral(obj.Val(), obj.Type()), - pos: obj.Pos(), - } - - case *types.Var: - spec, _ := syntax.(*ast.ValueSpec) - g := &Global{ - Pkg: pkg, - name: name, - typ: pointer(obj.Type()), // address - pos: obj.Pos(), - spec: spec, - } - pkg.values[obj] = g - pkg.Members[name] = g - - case *types.Func: - var fs *funcSyntax - if decl, ok := syntax.(*ast.FuncDecl); ok { - fs = &funcSyntax{ - recvField: decl.Recv, - paramFields: decl.Type.Params, - resultFields: decl.Type.Results, - body: decl.Body, - } - } - sig := obj.Type().(*types.Signature) - fn := &Function{ - name: name, - Signature: sig, - pos: obj.Pos(), // (iff syntax) - Pkg: pkg, - Prog: b.Prog, - syntax: fs, - } - if sig.Recv() == nil { - // Function declaration. - pkg.values[obj] = fn - pkg.Members[name] = fn - } else { - // Method declaration. - nt := sig.Recv().Type().Deref().(*types.Named) - _, method := methodIndex(nt, 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. -// -// (CREATE phase.) -// -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, pkg.objectOf(id), nil) - } - } - } - - case token.VAR: - for _, spec := range decl.Specs { - for _, id := range spec.(*ast.ValueSpec).Names { - if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.objectOf(id), spec) - } - } - } - - case token.TYPE: - for _, spec := range decl.Specs { - id := spec.(*ast.TypeSpec).Name - if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.objectOf(id), nil) - } - } - } - - case *ast.FuncDecl: - id := decl.Name - if decl.Recv == nil && id.Name == "init" { - if !pkg.Init.pos.IsValid() { - pkg.Init.pos = decl.Name.Pos() - } - return // init blocks aren't functions - } - if !isBlankIdent(id) { - b.memberFromObject(pkg, pkg.objectOf(id), decl) - } - } -} - -// createPackage constructs an SSA Package from an error-free -// package described by info, and populates its Members mapping. -// -// The real work of building SSA form for each function is not done -// until a subsequent call to BuildPackage. -// -// (CREATE phase.) -// -func (b *Builder) createPackage(info *importer.PackageInfo) *Package { - p := &Package{ - Prog: b.Prog, - Members: make(map[string]Member), - values: make(map[types.Object]Value), - Types: info.Pkg, - info: info, // transient (CREATE and BUILD phases) - } - - // Add init() function (but not to Members since it can't be referenced). - p.Init = &Function{ - name: "init", - Signature: new(types.Signature), - Pkg: p, - Prog: b.Prog, - } - - // CREATE phase. - // Allocate all package members: vars, funcs and consts and types. - if len(info.Files) > 0 { - // Go source package. - for _, file := range info.Files { - for _, decl := range file.Decls { - b.membersFromDecl(p, decl) - } - } - } else { - // GC-compiled binary package. - // No code. - // No position information. - scope := p.Types.Scope() - for i, n := 0, scope.NumEntries(); i < n; i++ { - b.memberFromObject(p, scope.At(i), nil) - } - } - - // Add initializer guard variable. - initguard := &Global{ - Pkg: p, - name: "init$guard", - typ: pointer(tBool), - } - p.Members[initguard.Name()] = initguard - - if b.Context.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) { +func (b *builder) buildDecl(pkg *Package, decl ast.Decl) { switch decl := decl.(type) { case *ast.GenDecl: switch decl.Tok { @@ -2531,7 +2265,7 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { } nt := pkg.objectOf(id).Type().(*types.Named) for i, n := 0, nt.NumMethods(); i < n; i++ { - b.buildFunction(b.Prog.concreteMethods[nt.Method(i)]) + b.buildFunction(pkg.Prog.concreteMethods[nt.Method(i)]) } } } @@ -2546,8 +2280,8 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { } else if id.Name == "init" { // init() block - if b.Context.Mode&LogSource != 0 { - fmt.Fprintln(os.Stderr, "build init block @", b.Prog.Files.Position(decl.Pos())) + if pkg.Prog.mode&LogSource != 0 { + fmt.Fprintln(os.Stderr, "build init block @", pkg.Prog.Files.Position(decl.Pos())) } init := pkg.Init @@ -2570,27 +2304,26 @@ func (b *Builder) buildDecl(pkg *Package, decl ast.Decl) { } else { // Package-level function. - b.buildFunction(b.Prog.Value(pkg.objectOf(id)).(*Function)) + b.buildFunction(pkg.Prog.Value(pkg.objectOf(id)).(*Function)) } } } -// BuildAllPackages constructs the SSA representation of the bodies of -// all functions in all packages known to the Builder. Construction -// occurs in parallel unless the BuildSerially mode flag was set. +// BuildAll calls Package.Build() for each package in prog. +// Building occurs in parallel unless the BuildSerially mode flag was set. // -// BuildAllPackages is idempotent and thread-safe. +// BuildAll is idempotent and thread-safe. // -func (b *Builder) BuildAllPackages() { +func (prog *Program) BuildAll() { var wg sync.WaitGroup - for _, p := range b.Prog.Packages { - if b.Context.Mode&BuildSerially != 0 { - b.BuildPackage(p) + for _, p := range prog.Packages { + if prog.mode&BuildSerially != 0 { + p.Build() } else { wg.Add(1) go func(p *Package) { - b.BuildPackage(p) + p.Build() wg.Done() }(p) } @@ -2598,11 +2331,11 @@ func (b *Builder) BuildAllPackages() { wg.Wait() } -// BuildPackage builds SSA code for all functions and vars in package p. +// Build builds SSA code for all functions and vars in package p. // -// BuildPackage is idempotent and thread-safe. +// Build is idempotent and thread-safe. // -func (b *Builder) BuildPackage(p *Package) { +func (p *Package) Build() { if !atomic.CompareAndSwapInt32(&p.started, 0, 1) { return // already started } @@ -2610,14 +2343,7 @@ func (b *Builder) BuildPackage(p *Package) { p.info = nil return // nothing to do } - - // TODO(adonovan): consider splitting Builder up into a - // package-specific part (needn't be exported) containing - // info, nto1Vars which actually traverses the AST, plus the - // shared portion (Builder). - p.nTo1Vars = make(map[*ast.ValueSpec]bool) - - if b.Context.Mode&LogSource != 0 { + if p.Prog.mode&LogSource != 0 { defer logStack("build %s", p)() } init := p.Init @@ -2632,34 +2358,16 @@ func (b *Builder) BuildPackage(p *Package) { emitStore(init, initguard, vTrue) // Call the init() function of each package we import. - // We iterate over the syntax (p.Files.Imports) not the types - // (p.Types.Imports()) because the latter may contain the - // transitive closure of dependencies, - // e.g. when using GcImporter. - seen := make(map[*types.Package]bool) - for _, file := range p.info.Files { - for _, imp := range file.Imports { - path, _ := strconv.Unquote(imp.Path.Value) - if path == "unsafe" { - continue - } - typkg := p.Types.Imports()[path] - if seen[typkg] { - continue - } - seen[typkg] = true + for _, typkg := range p.info.Imports() { + var v Call + v.Call.Func = p.Prog.packages[typkg].Init + v.Call.pos = init.pos + v.setType(types.NewTuple()) + init.emit(&v) + } - p2 := b.Prog.packages[typkg] - if p2 == nil { - panic("Building " + p.String() + ": CreatePackage has not been called for package " + path) - } - - var v Call - v.Call.Func = p2.Init - v.Call.pos = init.pos - v.setType(types.NewTuple()) - init.emit(&v) - } + b := &builder{ + nTo1Vars: make(map[*ast.ValueSpec]bool), } // Visit the package's var decls and init funcs in source @@ -2676,9 +2384,7 @@ func (b *Builder) BuildPackage(p *Package) { } } - // We no longer need ASTs or go/types deductions. - p.info = nil - p.nTo1Vars = nil + p.info = nil // We no longer need ASTs or go/types deductions. // Finish up. emitJump(init, done) @@ -2688,7 +2394,7 @@ func (b *Builder) BuildPackage(p *Package) { init.finishBody() } -// Only valid during p's build phase! +// Only valid during p's create and build phases. func (p *Package) objectOf(id *ast.Ident) types.Object { if o := p.info.ObjectOf(id); o != nil { return o @@ -2697,7 +2403,7 @@ func (p *Package) objectOf(id *ast.Ident) types.Object { id.Name, p.Prog.Files.Position(id.Pos()))) } -// Only valid during p's build phase! +// Only valid during p's create and build phases. func (p *Package) typeOf(e ast.Expr) types.Type { return p.info.TypeOf(e) } diff --git a/ssa/create.go b/ssa/create.go new file mode 100644 index 0000000000..e1755fd0b2 --- /dev/null +++ b/ssa/create.go @@ -0,0 +1,263 @@ +package ssa + +// This file implements the CREATE phase of SSA construction. +// See builder.go for explanation. + +import ( + "fmt" + "go/ast" + "go/token" + "os" + + "code.google.com/p/go.tools/go/types" + "code.google.com/p/go.tools/importer" +) + +// 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 + NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers + BuildSerially // Build packages serially, not in parallel. +) + +// NewProgram returns a new SSA Program initially containing no +// packages. +// +// fset specifies the mapping from token positions to source location +// that will be used by all ASTs of this program. +// +// mode controls diagnostics and checking during SSA construction. +// +func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { + prog := &Program{ + Files: fset, + Packages: make(map[string]*Package), + packages: make(map[*types.Package]*Package), + Builtins: make(map[types.Object]*Builtin), + methodSets: make(map[types.Type]MethodSet), + concreteMethods: make(map[*types.Func]*Function), + mode: mode, + } + + // Create Values for built-in functions. + for i, n := 0, types.Universe.NumEntries(); i < n; i++ { + if obj, ok := types.Universe.At(i).(*types.Func); ok { + prog.Builtins[obj] = &Builtin{obj} + } + } + + return prog +} + +// CreatePackages creates an SSA Package for each type-checker package +// held by imp. All such packages must be error-free. +// +// The created packages may be accessed via the Program.Packages field. +// +// A package in the 'created' state has its Members mapping populated, +// but a subsequent call to Package.Build() or Program.BuildAll() is +// required to build SSA code for the bodies of its functions. +// +func (prog *Program) CreatePackages(imp *importer.Importer) { + // TODO(adonovan): make this idempotent, so that a second call + // to CreatePackages creates only the packages that appeared + // in imp since the first. + // + // TODO(adonovan): make it create packages in topological + // order (using info.Imports()) so we can compute method sets + // in postorder (or within createPackage) rather than as a + // second pass. + + for path, info := range imp.Packages { + createPackage(prog, path, info) + } + + // Compute the method sets, now that we have all packages' methods. + for _, pkg := range prog.Packages { + for _, mem := range pkg.Members { + if t, ok := mem.(*Type); ok { + t.Methods = prog.MethodSet(t.NamedType) + t.PtrMethods = prog.MethodSet(pointer(t.NamedType)) + } + } + } +} + +// 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 memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { + name := obj.Name() + switch obj := obj.(type) { + case *types.TypeName: + pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)} + + case *types.Const: + pkg.Members[name] = &Constant{ + name: name, + Value: newLiteral(obj.Val(), obj.Type()), + pos: obj.Pos(), + } + + case *types.Var: + spec, _ := syntax.(*ast.ValueSpec) + g := &Global{ + Pkg: pkg, + name: name, + typ: pointer(obj.Type()), // address + pos: obj.Pos(), + spec: spec, + } + pkg.values[obj] = g + pkg.Members[name] = g + + case *types.Func: + var fs *funcSyntax + if decl, ok := syntax.(*ast.FuncDecl); ok { + fs = &funcSyntax{ + recvField: decl.Recv, + paramFields: decl.Type.Params, + resultFields: decl.Type.Results, + body: decl.Body, + } + } + sig := obj.Type().(*types.Signature) + fn := &Function{ + name: name, + Signature: sig, + pos: obj.Pos(), // (iff syntax) + Pkg: pkg, + Prog: pkg.Prog, + syntax: fs, + } + if recv := sig.Recv(); recv == nil { + // Function declaration. + pkg.values[obj] = fn + pkg.Members[name] = fn + } else { + // Method declaration. + nt := recv.Type().Deref().(*types.Named) + _, method := methodIndex(nt, MakeId(name, pkg.Types)) + pkg.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 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) { + memberFromObject(pkg, pkg.objectOf(id), nil) + } + } + } + + case token.VAR: + for _, spec := range decl.Specs { + for _, id := range spec.(*ast.ValueSpec).Names { + if !isBlankIdent(id) { + memberFromObject(pkg, pkg.objectOf(id), spec) + } + } + } + + case token.TYPE: + for _, spec := range decl.Specs { + id := spec.(*ast.TypeSpec).Name + if !isBlankIdent(id) { + memberFromObject(pkg, pkg.objectOf(id), nil) + } + } + } + + case *ast.FuncDecl: + id := decl.Name + if decl.Recv == nil && id.Name == "init" { + if !pkg.Init.pos.IsValid() { + pkg.Init.pos = decl.Name.Pos() + } + return // init blocks aren't functions + } + if !isBlankIdent(id) { + memberFromObject(pkg, pkg.objectOf(id), decl) + } + } +} + +// createPackage constructs an SSA Package from an error-free +// package described by info, and populates its Members mapping. +// +// The real work of building SSA form for each function is not done +// until a subsequent call to Package.Build(). +// +func createPackage(prog *Program, importPath string, info *importer.PackageInfo) { + p := &Package{ + Prog: prog, + Members: make(map[string]Member), + values: make(map[types.Object]Value), + Types: info.Pkg, + info: info, // transient (CREATE and BUILD phases) + } + + // Add init() function (but not to Members since it can't be referenced). + p.Init = &Function{ + name: "init", + Signature: new(types.Signature), + Pkg: p, + Prog: prog, + } + + // CREATE phase. + // Allocate all package members: vars, funcs and consts and types. + if len(info.Files) > 0 { + // Go source package. + for _, file := range info.Files { + for _, decl := range file.Decls { + membersFromDecl(p, decl) + } + } + } else { + // GC-compiled binary package. + // No code. + // No position information. + scope := p.Types.Scope() + for i, n := 0, scope.NumEntries(); i < n; i++ { + memberFromObject(p, scope.At(i), nil) + } + } + + // Add initializer guard variable. + initguard := &Global{ + Pkg: p, + name: "init$guard", + typ: pointer(tBool), + } + p.Members[initguard.Name()] = initguard + + if prog.mode&LogPackages != 0 { + p.DumpTo(os.Stderr) + } + + prog.Packages[importPath] = p + prog.packages[p.Types] = p +} diff --git a/ssa/example_test.go b/ssa/example_test.go index 0e7bab6a8e..71173c238f 100644 --- a/ssa/example_test.go +++ b/ssa/example_test.go @@ -53,16 +53,18 @@ func main() { return } - // Construct an SSA builder. - builder := ssa.NewBuilder(&ssa.Context{}, imp) - mainPkg := builder.Prog.Package(info.Pkg) + // Create SSA-form program representation. + var mode ssa.BuilderMode + prog := ssa.NewProgram(imp.Fset, mode) + prog.CreatePackages(imp) + mainPkg := prog.Package(info.Pkg) // Print out the package. mainPkg.DumpTo(os.Stdout) fmt.Println() // Build SSA code for bodies of functions in mainPkg. - builder.BuildPackage(mainPkg) + mainPkg.Build() // Print out the package-level functions. mainPkg.Init.DumpTo(os.Stdout) diff --git a/ssa/interp/interp_test.go b/ssa/interp/interp_test.go index d5fa15fcb3..2fd7c524b1 100644 --- a/ssa/interp/interp_test.go +++ b/ssa/interp/interp_test.go @@ -171,14 +171,12 @@ func run(t *testing.T, dir, input string) bool { return false } - b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions}, imp) - mainpkg := b.Prog.Package(info.Pkg) - - b.BuildAllPackages() - b = nil // discard Builder + prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions) + prog.CreatePackages(imp) + prog.BuildAll() hint = fmt.Sprintf("To trace execution, run:\n%% go run exp/ssa/ssadump.go -build=C -run --interp=T %s\n", input) - if exitCode := interp.Interpret(mainpkg, 0, inputs[0], []string{}); exitCode != 0 { + if exitCode := interp.Interpret(prog.Package(info.Pkg), 0, inputs[0], []string{}); exitCode != 0 { t.Errorf("interp.Interpret(%s) exited with code %d, want zero", inputs, exitCode) return false } diff --git a/ssa/promote.go b/ssa/promote.go index 09f457b91d..6cec54e1c0 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -151,7 +151,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet { m := nt.Method(i) concrete := prog.concreteMethods[m] if concrete == nil { - panic(fmt.Sprint("no ssa.Function for mset(%s)[%s]", t, m.Name())) + panic(fmt.Sprintf("no ssa.Function for mset(%s)[%s]", t, m.Name())) } addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node) } diff --git a/ssa/source_test.go b/ssa/source_test.go index d0491ff492..1eead23634 100644 --- a/ssa/source_test.go +++ b/ssa/source_test.go @@ -252,9 +252,10 @@ func TestEnclosingFunction(t *testing.T) { continue } - b := ssa.NewBuilder(new(ssa.Context), imp) - pkg := b.Prog.Package(info.Pkg) - b.BuildPackage(pkg) + prog := ssa.NewProgram(imp.Fset, 0) + prog.CreatePackages(imp) + pkg := prog.Package(info.Pkg) + pkg.Build() name := "(none)" fn := ssa.EnclosingFunction(pkg, path) diff --git a/ssa/ssa.go b/ssa/ssa.go index 4ed48bccba..ad060f1bbc 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -15,8 +15,6 @@ import ( ) // A Program is a partial or complete Go program converted to SSA form. -// Each Builder creates and populates a single Program during its -// lifetime. // type Program struct { Files *token.FileSet // position information for the files of this Program [TODO: rename Fset] @@ -27,7 +25,7 @@ type Program struct { 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.Func]*Function // maps named concrete methods to their code - mode BuilderMode // set of mode bits + mode BuilderMode // set of mode bits for SSA construction } // A Package is a single analyzed Go package containing Members for @@ -44,9 +42,8 @@ type Package struct { // The following fields are set transiently, then cleared // after building. - started int32 // atomically tested and set at start of build phase - info *importer.PackageInfo // package ASTs and type information - nTo1Vars map[*ast.ValueSpec]bool // set of n:1 ValueSpecs already built + started int32 // atomically tested and set at start of build phase + info *importer.PackageInfo // package ASTs and type information } // A Member is a member of a Go package, implemented by *Constant, diff --git a/ssa/ssadump.go b/ssa/ssadump.go index 9e64cc8688..64b14b2beb 100644 --- a/ssa/ssadump.go +++ b/ssa/ssadump.go @@ -109,17 +109,13 @@ func main() { log.Fatal(err.Error()) } - // Build SSA-form program representation. - context := &ssa.Context{ - Mode: mode, - } - b := ssa.NewBuilder(context, imp) - mainpkg := b.PackageFor(info.Pkg) - b.BuildAllPackages() - b = nil // discard Builder + // Create and build SSA-form program representation. + prog := ssa.NewProgram(imp.Fset, mode) + prog.CreatePackages(imp) + prog.BuildAll() // Run the interpreter. if *runFlag { - interp.Interpret(mainpkg, interpMode, info.Pkg.Path(), args) + interp.Interpret(prog.Package(info.Pkg), interpMode, info.Pkg.Path(), args) } }