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

go.tools/ssa: some refactorings

ssa:
- Prog.CreatePackages inlined into all callers.
- Prog.CreatePackage is now exposed; idempotent; and checks for errors.
- '*address' not 'address' now implements lvalue (since it's 6 words).
- removed types.Method case from createMemberFromObject.

importer:
- added importer.PackageInfo.String method.
- simplifed importer.PackageInfo by putting types.Info in it.
- removed obsolete precondition from IsType.

R=gri
CC=golang-dev
https://golang.org/cl/11408045
This commit is contained in:
Alan Donovan 2013-07-18 16:59:06 -04:00
parent 6d85cc17dd
commit 69ce87a6c1
12 changed files with 119 additions and 118 deletions

View File

@ -131,7 +131,11 @@ func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
if err != nil {
return nil, err
}
return imp.CreateSourcePackage(importPath, files)
info := imp.CreateSourcePackage(importPath, files)
if info.Err != nil {
return nil, info.Err
}
return info, nil
}
// CreateSourcePackage invokes the type-checker on files and returns a
@ -146,33 +150,20 @@ func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) (*PackageInfo, error) {
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
pkgInfo := &PackageInfo{
Files: files,
types: make(map[ast.Expr]types.Type),
idents: make(map[*ast.Ident]types.Object),
constants: make(map[ast.Expr]exact.Value),
typecases: make(map[*ast.CaseClause]*types.Var),
Files: files,
Info: types.Info{
Types: make(map[ast.Expr]types.Type),
Values: make(map[ast.Expr]exact.Value),
Objects: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
},
}
info := &types.Info{
Types: pkgInfo.types,
Values: pkgInfo.constants,
Objects: pkgInfo.idents,
Implicits: make(map[ast.Node]types.Object),
}
var firstErr error
pkgInfo.Pkg, firstErr = imp.context.TypeChecker.Check(importPath, imp.Fset, files, info)
if firstErr != nil {
return nil, firstErr
}
for node, obj := range info.Implicits {
if cc, ok := node.(*ast.CaseClause); ok {
pkgInfo.typecases[cc] = obj.(*types.Var)
}
}
pkgInfo.Pkg, pkgInfo.Err = imp.context.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
imp.Packages[importPath] = pkgInfo
return pkgInfo, nil
return pkgInfo
}

View File

@ -3,11 +3,13 @@ package importer
// TODO(gri): absorb this into go/types.
import (
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types"
"fmt"
"go/ast"
"go/token"
"strconv"
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types"
)
// PackageInfo holds the ASTs and facts derived by the type-checker
@ -16,14 +18,14 @@ import (
// Not mutated once constructed.
//
type PackageInfo struct {
Pkg *types.Package
Files []*ast.File // abstract syntax for the package's files
Pkg *types.Package
Err error // non-nil if the package had static errors
Files []*ast.File // abstract syntax for the package's files
types.Info // type-checker deductions.
}
// Type-checker deductions.
types map[ast.Expr]types.Type // inferred types of expressions
constants map[ast.Expr]exact.Value // values of constant expressions
idents map[*ast.Ident]types.Object // resolved objects for named entities
typecases map[*ast.CaseClause]*types.Var // implicit vars for single-type typecases
func (info *PackageInfo) String() string {
return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path())
}
// Imports returns the set of packages imported by this one, in source
@ -57,7 +59,7 @@ func (info *PackageInfo) Imports() []*types.Package {
// Precondition: e belongs to the package's ASTs.
//
func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
if t, ok := info.types[e]; ok {
if t, ok := info.Types[e]; ok {
return t
}
// Defining ast.Idents (id := expr) get only Ident callbacks
@ -73,20 +75,18 @@ func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
// Precondition: e belongs to the package's ASTs.
//
func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value {
return info.constants[e]
return info.Values[e]
}
// ObjectOf returns the typechecker object denoted by the specified id.
// Precondition: id belongs to the package's ASTs.
//
func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
return info.idents[id]
return info.Objects[id]
}
// IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs.
// e must be a true expression, not a KeyValueExpr, or an Ident
// appearing in a SelectorExpr or declaration.
//
func (info *PackageInfo) IsType(e ast.Expr) bool {
switch e := e.(type) {
@ -126,7 +126,10 @@ func (info *PackageInfo) IsPackageRef(sel *ast.SelectorExpr) types.Object {
// case clause in a type switch, or nil if not found.
//
func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var {
return info.typecases[cc]
if v := info.Implicits[cc]; v != nil {
return v.(*types.Var)
}
return nil
}
var (
@ -146,9 +149,6 @@ var (
// be moved there and made accessible via an additional types.Context
// callback.
//
// The returned Signature is degenerate and only intended for use by
// emitCallArgs.
//
func (info *PackageInfo) BuiltinCallSignature(e *ast.CallExpr) *types.Signature {
var params []*types.Var
var isVariadic bool

View File

@ -245,14 +245,16 @@ func TestEnclosingFunction(t *testing.T) {
t.Errorf("EnclosingFunction(%q) not exact", test.substr)
continue
}
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
if err != nil {
t.Error(err.Error())
info := imp.CreateSourcePackage("main", []*ast.File{f})
if info.Err != nil {
t.Error(info.Err.Error())
continue
}
prog := ssa.NewProgram(imp.Fset, 0)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
pkg := prog.Package(info.Pkg)
pkg.Build()

View File

@ -38,7 +38,8 @@ func CreatePackageFromArgs(imp *Importer, args []string) (info *PackageInfo, res
files, err = ParseFiles(imp.Fset, ".", args[:i]...)
rest = args[i:]
if err == nil {
info, err = imp.CreateSourcePackage("main", files)
info = imp.CreateSourcePackage("main", files)
err = info.Err
}
default:

View File

@ -7,7 +7,7 @@ package ssa
// definitions of all package members are created, method-sets are
// computed, and wrapper methods are synthesized. The create phase
// proceeds in topological order over the import dependency graph,
// initiated by client calls to CreatePackages.
// initiated by client calls to Program.CreatePackage.
//
// In the BUILD phase (builder.go), the builder traverses the AST of
// each Go source function and generates SSA instructions for the
@ -447,7 +447,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
if v == nil {
v = fn.lookup(obj, escaping)
}
return address{addr: v, id: e, object: obj}
return &address{addr: v, id: e, object: obj}
case *ast.CompositeLit:
t := deref(fn.Pkg.typeOf(e))
@ -458,7 +458,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
v = fn.addLocal(t, e.Lbrace)
}
b.compLit(fn, v, e, t) // initialize in place
return address{addr: v}
return &address{addr: v}
case *ast.ParenExpr:
return b.addr(fn, e.X, escaping)
@ -467,13 +467,13 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
// p.M where p is a package.
if obj := fn.Pkg.info.IsPackageRef(e); obj != nil {
if v := b.lookup(fn.Pkg, obj); v != nil {
return address{addr: v}
return &address{addr: v}
}
panic("undefined package-qualified name: " + obj.Name())
}
// e.f where e is an expression.
return address{addr: b.selectField(fn, e, true, escaping)}
return &address{addr: b.selectField(fn, e, true, escaping)}
case *ast.IndexExpr:
var x Value
@ -502,10 +502,10 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
Index: emitConv(fn, b.expr(fn, e.Index), tInt),
}
v.setType(et)
return address{addr: fn.emit(v)}
return &address{addr: fn.emit(v)}
case *ast.StarExpr:
return address{addr: b.expr(fn, e.X), starPos: e.Star}
return &address{addr: b.expr(fn, e.X), starPos: e.Star}
}
panic(fmt.Sprintf("unexpected address expression: %T", e))
@ -519,7 +519,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
// in an addressable location.
//
func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
if _, ok := loc.(address); ok {
if _, ok := loc.(*address); ok {
if e, ok := e.(*ast.CompositeLit); ok {
typ := loc.typ()
switch typ.Underlying().(type) {
@ -1047,7 +1047,7 @@ func (b *builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
continue
}
g.spec = nil
lval = address{addr: g}
lval = &address{addr: g}
} else {
// Mode B: initialize all globals.
if !isBlankIdent(id) {
@ -1056,7 +1056,7 @@ func (b *builder) globalValueSpec(init *Function, spec *ast.ValueSpec, g *Global
continue // already done
}
g2.spec = nil
lval = address{addr: g2}
lval = &address{addr: g2}
}
}
if init.Prog.mode&LogSource != 0 {
@ -1241,7 +1241,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
}
faddr.setType(types.NewPointer(sf.Type()))
fn.emit(faddr)
b.exprInPlace(fn, address{addr: faddr}, e)
b.exprInPlace(fn, &address{addr: faddr}, e)
}
case *types.Array, *types.Slice:
@ -1274,7 +1274,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
}
iaddr.setType(types.NewPointer(at.Elem()))
fn.emit(iaddr)
b.exprInPlace(fn, address{addr: iaddr}, e)
b.exprInPlace(fn, &address{addr: iaddr}, e)
}
if t != at { // slice
s := &Slice{X: array}

View File

@ -42,14 +42,16 @@ func main() {
return
}
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
if err != nil {
t.Error(err.Error())
info := imp.CreateSourcePackage("main", []*ast.File{f})
if info.Err != nil {
t.Error(info.Err.Error())
return
}
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
mainPkg := prog.Package(info.Pkg)
mainPkg.Build()

View File

@ -57,24 +57,6 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
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.
//
@ -143,10 +125,6 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
pkg.Prog.concreteMethods[method] = fn
}
case *types.Method:
// TODO(adonovan): do something more sensible here?
memberFromObject(pkg, obj.Func, syntax)
default: // (incl. *types.Package)
panic(fmt.Sprintf("unexpected Object type: %T", obj))
}
@ -202,13 +180,23 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
}
}
// createPackage constructs an SSA Package from an error-free
// package described by info, and populates its Members mapping.
// 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 createPackage(prog *Program, importPath string, info *importer.PackageInfo) {
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),
@ -246,11 +234,11 @@ func createPackage(prog *Program, importPath string, info *importer.PackageInfo)
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)
memberFromObject(p, mset.At(i).Func, 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, mset.At(i).Func, nil)
}
}
memberFromObject(p, obj, nil)
@ -269,10 +257,12 @@ func createPackage(prog *Program, importPath string, info *importer.PackageInfo)
p.DumpTo(os.Stderr)
}
prog.PackagesByPath[importPath] = p
prog.PackagesByPath[info.Pkg.Path()] = p
prog.packages[p.Object] = p
if prog.mode&SanityCheckFunctions != 0 {
sanityCheckPackage(p)
}
return p
}

View File

@ -47,16 +47,18 @@ func main() {
}
// Create a "main" package containing one file.
info, err := imp.CreateSourcePackage("main", []*ast.File{file})
if err != nil {
fmt.Printf(err.Error()) // type error
info := imp.CreateSourcePackage("main", []*ast.File{file})
if info.Err != nil {
fmt.Printf(info.Err.Error()) // type error
return
}
// Create SSA-form program representation.
var mode ssa.BuilderMode
prog := ssa.NewProgram(imp.Fset, mode)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
mainPkg := prog.Package(info.Pkg)
// Print out the package.

View File

@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"code.google.com/p/go.tools/importer"
"code.google.com/p/go.tools/ssa"
@ -79,22 +80,22 @@ var gorootTests = []string{
"ddd.go",
"blank.go", // partly disabled; TODO(adonovan): skip blank fields in struct{_} equivalence.
"map.go",
"bom.go",
"closedchan.go",
"divide.go",
"rename.go",
"const3.go",
"nil.go",
"recover.go", // partly disabled; TODO(adonovan): fix.
// Slow tests follow.
"cmplxdivide.go cmplxdivide1.go",
"append.go",
"crlf.go", // doesn't actually assert anything
"typeswitch1.go",
"floatcmp.go",
"gc1.go",
"crlf.go", // doesn't actually assert anything
// Slow tests follow.
"bom.go", // ~1.7s
"gc1.go", // ~1.7s
"cmplxdivide.go cmplxdivide1.go", // ~2.4s
// Working, but not worth enabling:
// "append.go", // works, but slow (15s).
// "gc2.go", // works, but slow, and cheats on the memory check.
// "sigchld.go", // works, but only on POSIX.
// "peano.go", // works only up to n=9, and slow even then.
@ -111,7 +112,6 @@ var gorootTests = []string{
// Typechecker failures:
// "switch.go", // https://code.google.com/p/go/issues/detail?id=5505
// "rune.go", // https://code.google.com/p/go/issues/detail?id=5895
// Broken. TODO(adonovan): fix.
// copy.go // very slow; but with N=4 quickly crashes, slice index out of range.
@ -139,6 +139,8 @@ var testdataTests = []string{
func run(t *testing.T, dir, input string) bool {
fmt.Printf("Input: %s\n", input)
start := time.Now()
var inputs []string
for _, i := range strings.Split(input, " ") {
inputs = append(inputs, dir+i)
@ -166,14 +168,16 @@ func run(t *testing.T, dir, input string) bool {
}()
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go run src/code.google.com/p/go.tools/ssa/ssadump.go -build=CFP %s\n", input)
info, err := imp.CreateSourcePackage("main", files)
if err != nil {
t.Errorf("ssa.Builder.CreatePackage(%s) failed: %s", inputs, err.Error())
info := imp.CreateSourcePackage("main", files)
if info.Err != nil {
t.Errorf("importer.CreateSourcePackage(%s) failed: %s", inputs, info.Err.Error())
return false
}
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
prog.BuildAll()
hint = fmt.Sprintf("To trace execution, run:\n%% go run src/code.google.com/p/go.tools/ssa/ssadump.go -build=C -run --interp=T %s\n", input)
@ -183,6 +187,11 @@ func run(t *testing.T, dir, input string) bool {
}
hint = "" // call off the hounds
if false {
fmt.Println(input, time.Since(start)) // test profiling
}
return true
}

View File

@ -29,13 +29,13 @@ type address struct {
object types.Object // source var, if from *ast.Ident
}
func (a address) load(fn *Function) Value {
func (a *address) load(fn *Function) Value {
load := emitLoad(fn, a.addr)
load.pos = a.starPos
return load
}
func (a address) store(fn *Function, v Value) {
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v)
store.pos = a.starPos
if a.id != nil {
@ -44,7 +44,7 @@ func (a address) store(fn *Function, v Value) {
}
}
func (a address) address(fn *Function) Value {
func (a *address) address(fn *Function) Value {
if a.id != nil {
// NB: this kind of DebugRef yields the object's address.
emitDebugRef(fn, a.id, a.addr)
@ -52,7 +52,7 @@ func (a address) address(fn *Function) Value {
return a.addr
}
func (a address) typ() types.Type {
func (a *address) typ() types.Type {
return deref(a.addr.Type())
}

View File

@ -40,14 +40,16 @@ func TestObjValueLookup(t *testing.T) {
}
}
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
if err != nil {
t.Error(err.Error())
info := imp.CreateSourcePackage("main", []*ast.File{f})
if info.Err != nil {
t.Error(info.Err.Error())
return
}
prog := ssa.NewProgram(imp.Fset, ssa.DebugInfo /*|ssa.LogFunctions*/)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
pkg := prog.Package(info.Pkg)
pkg.Build()

View File

@ -114,7 +114,9 @@ func main() {
// Create and build SSA-form program representation.
prog := ssa.NewProgram(imp.Fset, mode)
prog.CreatePackages(imp)
for _, info := range imp.Packages {
prog.CreatePackage(info)
}
prog.BuildAll()
// Run the interpreter.