1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:04:44 -07:00

go.tools/ssa: refactoring: eliminate Builder from API.

Details:
- builder is now un-exported and is now a per-package entity.
- Package.nTo1Vars is now part of builder, where it belongs.
- CREATE phase code split out into its own file, create.go
- Context type is gone; it had become trivial after the
  Importer refactoring.
- importer.PackageInfo.Imports() now encapsulates iteration
  over imports.

Typical usage is now:
  prog := ssa.NewProgram(imp.Fset, mode)
  prog.CreatePackages(imp)
  prog.BuildAll()

Builder.BuildPackage(Package) is now Package.Build()
Builder.BuildAllPackages() is now Program.BuildAll()

R=iant, gri
CC=golang-dev
https://golang.org/cl/9970044
This commit is contained in:
Alan Donovan 2013-06-03 16:46:57 -04:00
parent 4d628a0312
commit fc4c97d1f1
9 changed files with 407 additions and 416 deletions

View File

@ -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.
//

View File

@ -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)
}

263
ssa/create.go Normal file
View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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,

View File

@ -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)
}
}