2013-06-03 14:46:57 -06:00
|
|
|
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{
|
2013-07-01 13:24:50 -06:00
|
|
|
Fset: fset,
|
|
|
|
PackagesByPath: make(map[string]*Package),
|
2013-06-13 12:43:35 -06:00
|
|
|
packages: make(map[*types.Package]*Package),
|
2013-07-01 13:24:50 -06:00
|
|
|
builtins: make(map[types.Object]*Builtin),
|
2013-06-13 12:43:35 -06:00
|
|
|
concreteMethods: make(map[*types.Func]*Function),
|
|
|
|
indirectionWrappers: make(map[*Function]*Function),
|
2013-06-14 13:50:37 -06:00
|
|
|
boundMethodWrappers: make(map[*Function]*Function),
|
|
|
|
ifaceMethodWrappers: make(map[*types.Func]*Function),
|
2013-06-13 12:43:35 -06:00
|
|
|
mode: mode,
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2013-07-01 13:24:50 -06:00
|
|
|
prog.builtins[obj] = &Builtin{obj}
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
for path, info := range imp.Packages {
|
|
|
|
createPackage(prog, path, info)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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:
|
2013-07-11 12:12:30 -06:00
|
|
|
pkg.Members[name] = &Type{object: obj}
|
2013-06-03 14:46:57 -06:00
|
|
|
|
|
|
|
case *types.Const:
|
|
|
|
pkg.Members[name] = &Constant{
|
2013-07-11 12:12:30 -06:00
|
|
|
object: obj,
|
|
|
|
Value: NewLiteral(obj.Val(), obj.Type(), obj.Pos()),
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
case *types.Var:
|
|
|
|
spec, _ := syntax.(*ast.ValueSpec)
|
|
|
|
g := &Global{
|
2013-07-11 12:12:30 -06:00
|
|
|
Pkg: pkg,
|
|
|
|
name: name,
|
|
|
|
object: obj,
|
|
|
|
typ: pointer(obj.Type()), // address
|
|
|
|
pos: obj.Pos(),
|
|
|
|
spec: spec,
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
pkg.values[obj] = g
|
|
|
|
pkg.Members[name] = g
|
|
|
|
|
|
|
|
case *types.Func:
|
|
|
|
var fs *funcSyntax
|
2013-07-03 15:57:20 -06:00
|
|
|
synthetic := "loaded from gc object file"
|
2013-06-03 14:46:57 -06:00
|
|
|
if decl, ok := syntax.(*ast.FuncDecl); ok {
|
2013-07-03 15:57:20 -06:00
|
|
|
synthetic = ""
|
2013-06-03 14:46:57 -06:00
|
|
|
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,
|
2013-07-11 12:12:30 -06:00
|
|
|
object: obj,
|
2013-06-03 14:46:57 -06:00
|
|
|
Signature: sig,
|
2013-07-03 15:57:20 -06:00
|
|
|
Synthetic: synthetic,
|
2013-06-03 14:46:57 -06:00
|
|
|
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.
|
2013-06-14 13:50:37 -06:00
|
|
|
_, method := namedTypeMethodIndex(
|
|
|
|
recv.Type().Deref().(*types.Named),
|
2013-07-01 13:24:50 -06:00
|
|
|
MakeId(name, pkg.Object))
|
2013-06-03 14:46:57 -06:00
|
|
|
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" {
|
2013-07-10 16:37:52 -06:00
|
|
|
if !pkg.init.pos.IsValid() {
|
|
|
|
pkg.init.pos = decl.Name.Pos()
|
|
|
|
pkg.init.Synthetic = ""
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
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),
|
2013-07-01 13:24:50 -06:00
|
|
|
Object: info.Pkg,
|
2013-06-03 14:46:57 -06:00
|
|
|
info: info, // transient (CREATE and BUILD phases)
|
|
|
|
}
|
|
|
|
|
2013-07-10 16:37:52 -06:00
|
|
|
// Add init() function.
|
|
|
|
p.init = &Function{
|
2013-06-03 14:46:57 -06:00
|
|
|
name: "init",
|
|
|
|
Signature: new(types.Signature),
|
2013-07-03 15:57:20 -06:00
|
|
|
Synthetic: "package initializer",
|
2013-06-03 14:46:57 -06:00
|
|
|
Pkg: p,
|
|
|
|
Prog: prog,
|
|
|
|
}
|
2013-07-10 16:37:52 -06:00
|
|
|
p.Members[p.init.name] = p.init
|
2013-06-03 14:46:57 -06:00
|
|
|
|
|
|
|
// 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.
|
2013-07-01 13:24:50 -06:00
|
|
|
scope := p.Object.Scope()
|
2013-06-03 14:46:57 -06:00
|
|
|
for i, n := 0, scope.NumEntries(); i < n; i++ {
|
2013-07-10 16:08:42 -06:00
|
|
|
obj := scope.At(i)
|
|
|
|
if obj, ok := obj.(*types.TypeName); ok {
|
|
|
|
mset := types.NewMethodSet(obj.Type())
|
|
|
|
for i, n := 0, mset.Len(); i < n; i++ {
|
|
|
|
memberFromObject(p, mset.At(i), nil)
|
|
|
|
}
|
|
|
|
mset = types.NewMethodSet(types.NewPointer(obj.Type()))
|
|
|
|
for i, n := 0, mset.Len(); i < n; i++ {
|
|
|
|
memberFromObject(p, mset.At(i), nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memberFromObject(p, obj, nil)
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2013-07-01 13:24:50 -06:00
|
|
|
prog.PackagesByPath[importPath] = p
|
|
|
|
prog.packages[p.Object] = p
|
2013-07-11 12:12:30 -06:00
|
|
|
|
|
|
|
if prog.mode&SanityCheckFunctions != 0 {
|
|
|
|
sanityCheckPackage(p)
|
|
|
|
}
|
2013-06-03 14:46:57 -06:00
|
|
|
}
|