1
0
mirror of https://github.com/golang/go synced 2024-11-19 04:14:45 -07:00
go/ssa/create.go
Alan Donovan 4da31df1c8 go.tools/ssa: (another) major refactoring of method-set logic.
We now use LookupFieldOrMethod for all SelectorExprs, and
simplify the logic to discriminate the various cases.

We inline static calls to promoted/indirected functions,
dramatically reducing the number of functions created.

More tests are needed, but I'd like to submit this as-is.

In this CL, we:
- rely less on Id strings.  Internally we now use
  *types.Method (and its components) almost everywhere.
- stop thinking of types.Methods as objects. They don't
  have stable identities. (Hopefully they will become
  plain-old structs soon.)
- eliminate receiver indirection wrappers:
  indirection and promotion are handled together by makeWrapper.
- Handle the interactions of promotion, indirection and
  abstract methods much more cleanly.
- support receiver-bound interface method closures.
- break up builder.selectField so we can re-use parts
  (emitFieldSelection).
- add importer.PackageInfo.classifySelector utility.
- delete interfaceMethodIndex()
- delete namedTypeMethodIndex()
- delete isSuperInterface() (replaced by types.IsAssignable)
- call memberFromObject on each declared concrete method's
  *types.Func, not on every Method frem each method set, in the
  CREATE phase for packages loaded by gcimporter.

go/types:
- document Func, Signature.Recv() better.
- use fmt in {Package,Label}.String
- reimplement Func.String to be prettier and to include method
  receivers.

API changes:
- Function.method now holds the types.Method (soon to be
  not-an-object) for synthetic wrappers.
- CallCommon.Method now contains an abstract (interface)
  method object; was an abstract method index.
- CallCommon.MethodId() gone.
- Program.LookupMethod now takes a *Method not an Id string.

R=gri
CC=golang-dev
https://golang.org/cl/11674043
2013-07-26 11:22:34 -04:00

262 lines
6.7 KiB
Go

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.
DebugInfo // Include DebugRef instructions [TODO(adonovan): finer grain?]
)
// 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{
Fset: fset,
PackagesByPath: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
builtins: make(map[types.Object]*Builtin),
concreteMethods: make(map[*types.Func]*Function),
boundMethodWrappers: make(map[*types.Func]*Function),
ifaceMethodWrappers: make(map[*types.Func]*Function),
mode: mode,
}
// Create Values for built-in functions.
for _, name := range types.Universe.Names() {
if obj, ok := types.Universe.Lookup(name).(*types.Func); ok {
prog.builtins[obj] = &Builtin{obj}
}
}
return prog
}
// 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{object: obj}
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
}
pkg.values[obj] = c.Value
pkg.Members[name] = c
case *types.Var:
spec, _ := syntax.(*ast.ValueSpec)
g := &Global{
Pkg: pkg,
name: name,
object: obj,
typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(),
spec: spec,
}
pkg.values[obj] = g
pkg.Members[name] = g
case *types.Func:
var fs *funcSyntax
synthetic := "loaded from gc object file"
if decl, ok := syntax.(*ast.FuncDecl); ok {
synthetic = ""
fs = &funcSyntax{
functype: decl.Type,
recvField: decl.Recv,
body: decl.Body,
}
}
sig := obj.Type().(*types.Signature)
fn := &Function{
name: name,
object: obj,
Signature: sig,
Synthetic: synthetic,
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 {
// Concrete method.
_ = deref(recv.Type()).(*types.Named) // assertion
pkg.Prog.concreteMethods[obj] = fn
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String())
}
}
// 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()
pkg.init.Synthetic = ""
}
return // init blocks aren't functions
}
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.objectOf(id), decl)
}
}
}
// CreatePackage constructs and returns an SSA Package from an
// error-free package described by info, and populates its Members
// mapping.
//
// Repeated calls with the same info returns the same Package.
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build().
//
func (prog *Program) CreatePackage(info *importer.PackageInfo) *Package {
if info.Err != nil {
panic(fmt.Sprintf("package %s has errors: %s", info, info.Err))
}
if p := prog.packages[info.Pkg]; p != nil {
return p // already loaded
}
p := &Package{
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
Object: info.Pkg,
info: info, // transient (CREATE and BUILD phases)
}
// Add init() function.
p.init = &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: p,
Prog: prog,
}
p.Members[p.init.name] = p.init
// CREATE phase.
// Allocate all package members: vars, funcs, 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.Object.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil)
if obj, ok := obj.(*types.TypeName); ok {
named := obj.Type().(*types.Named)
for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil)
}
}
}
}
// Add initializer guard variable.
initguard := &Global{
Pkg: p,
name: "init$guard",
typ: types.NewPointer(tBool),
}
p.Members[initguard.Name()] = initguard
if prog.mode&LogPackages != 0 {
p.DumpTo(os.Stderr)
}
prog.PackagesByPath[info.Pkg.Path()] = p
prog.packages[p.Object] = p
if prog.mode&SanityCheckFunctions != 0 {
sanityCheckPackage(p)
}
return p
}