go.tools/ssa: new Function.Syntax() returns the declaring AST (debug mode) or just the Pos/End of the function's extent (otherwise).

R=gri
CC=golang-dev
https://golang.org/cl/16980043
This commit is contained in:
Alan Donovan 2013-10-27 10:55:21 -04:00
parent 3eb9e504e3
commit aa2386290b
6 changed files with 64 additions and 47 deletions

View File

@ -514,10 +514,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
Enclosing: fn, Enclosing: fn,
Pkg: fn.Pkg, Pkg: fn.Pkg,
Prog: fn.Prog, Prog: fn.Prog,
syntax: &funcSyntax{ syntax: e,
functype: e.Type,
body: e.Body,
},
} }
fn.AnonFuncs = append(fn.AnonFuncs, fn2) fn.AnonFuncs = append(fn.AnonFuncs, fn2)
b.buildFunction(fn2) b.buildFunction(fn2)
@ -2214,10 +2211,25 @@ func (b *builder) buildFunction(fn *Function) {
if fn.Blocks != nil { if fn.Blocks != nil {
return // building already started return // building already started
} }
if fn.syntax == nil {
var recvField *ast.FieldList
var body *ast.BlockStmt
var functype *ast.FuncType
switch n := fn.syntax.(type) {
case nil:
return // not a Go source function. (Synthetic, or from object file.) return // not a Go source function. (Synthetic, or from object file.)
case *ast.FuncDecl:
functype = n.Type
recvField = n.Recv
body = n.Body
case *ast.FuncLit:
functype = n.Type
body = n.Body
default:
panic(n)
} }
if fn.syntax.body == nil {
if body == nil {
// External function. // External function.
if fn.Params == nil { if fn.Params == nil {
// This condition ensures we add a non-empty // This condition ensures we add a non-empty
@ -2241,8 +2253,8 @@ func (b *builder) buildFunction(fn *Function) {
defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))() defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))()
} }
fn.startBody() fn.startBody()
fn.createSyntacticParams() fn.createSyntacticParams(recvField, functype)
b.stmt(fn, fn.syntax.body) b.stmt(fn, body)
if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) { if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
// Run function calls deferred in this function when // Run function calls deferred in this function when
// falling off the end of the body block. // falling off the end of the body block.
@ -2269,11 +2281,7 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
pos: decl.Name.NamePos, pos: decl.Name.NamePos,
Pkg: pkg, Pkg: pkg,
Prog: pkg.Prog, Prog: pkg.Prog,
syntax: &funcSyntax{ syntax: decl,
functype: decl.Type,
recvField: decl.Recv,
body: decl.Body,
},
} }
var v Call var v Call

View File

@ -95,26 +95,19 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
pkg.Members[name] = g pkg.Members[name] = g
case *types.Func: 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,
}
}
fn := &Function{ fn := &Function{
name: name, name: name,
object: obj, object: obj,
Signature: obj.Type().(*types.Signature), Signature: obj.Type().(*types.Signature),
Synthetic: synthetic, syntax: syntax,
pos: obj.Pos(), // (iff syntax) pos: obj.Pos(), // (iff syntax)
Pkg: pkg, Pkg: pkg,
Prog: pkg.Prog, Prog: pkg.Prog,
syntax: fs,
} }
if syntax == nil {
fn.Synthetic = "loaded from gc object file"
}
pkg.values[obj] = fn pkg.values[obj] = fn
if fn.Signature.Recv() == nil { if fn.Signature.Recv() == nil {
pkg.Members[name] = fn // package-level function pkg.Members[name] = fn // package-level function

View File

@ -147,13 +147,6 @@ type lblock struct {
_continue *BasicBlock _continue *BasicBlock
} }
// funcSyntax holds the syntax tree for the function declaration and body.
type funcSyntax struct {
recvField *ast.FieldList
body *ast.BlockStmt
functype *ast.FuncType
}
// labelledBlock returns the branch target associated with the // labelledBlock returns the branch target associated with the
// specified label, creating it if needed. // specified label, creating it if needed.
// //
@ -221,15 +214,14 @@ func (f *Function) startBody() {
// syntax. In addition it populates the f.objects mapping. // syntax. In addition it populates the f.objects mapping.
// //
// Preconditions: // Preconditions:
// f.syntax != nil, i.e. this is a Go source function.
// f.startBody() was called. // f.startBody() was called.
// Postcondition: // Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0) // len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
// //
func (f *Function) createSyntacticParams() { func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration). // Receiver (at most one inner iteration).
if f.syntax.recvField != nil { if recv != nil {
for _, field := range f.syntax.recvField.List { for _, field := range recv.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n)) f.addSpilledParam(f.Pkg.objectOf(n))
} }
@ -241,9 +233,9 @@ func (f *Function) createSyntacticParams() {
} }
// Parameters. // Parameters.
if f.syntax.functype.Params != nil { if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range f.syntax.functype.Params.List { for _, field := range functype.Params.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n)) f.addSpilledParam(f.Pkg.objectOf(n))
} }
@ -255,8 +247,8 @@ func (f *Function) createSyntacticParams() {
} }
// Named results. // Named results.
if f.syntax.functype.Results != nil { if functype.Results != nil {
for _, field := range f.syntax.functype.Results.List { for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results. // Implicit "var" decl of locals for named results.
for _, n := range field.Names { for _, n := range field.Names {
f.namedResults = append(f.namedResults, f.addLocalForIdent(n)) f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
@ -308,7 +300,11 @@ func (f *Function) finishBody() {
f.objects = nil f.objects = nil
f.currentBlock = nil f.currentBlock = nil
f.lblocks = nil f.lblocks = nil
f.syntax = nil
// Don't pin the AST in memory (except in debug mode).
if n := f.syntax; n != nil && !f.debugInfo() {
f.syntax = extentNode{n.Pos(), n.End()}
}
// Remove any f.Locals that are now heap-allocated. // Remove any f.Locals that are now heap-allocated.
j := 0 j := 0
@ -380,15 +376,13 @@ func (pkg *Package) SetDebugMode(debug bool) {
// debugInfo reports whether debug info is wanted for this function. // debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool { func (f *Function) debugInfo() bool {
return f.Pkg.debug return f.Pkg != nil && f.Pkg.debug
} }
// addNamedLocal creates a local variable, adds it to function f and // addNamedLocal creates a local variable, adds it to function f and
// returns it. Its name and type are taken from obj. Subsequent // returns it. Its name and type are taken from obj. Subsequent
// calls to f.lookup(obj) will return the same local. // calls to f.lookup(obj) will return the same local.
// //
// Precondition: f.syntax != nil (i.e. a Go source function).
//
func (f *Function) addNamedLocal(obj types.Object) *Alloc { func (f *Function) addNamedLocal(obj types.Object) *Alloc {
l := f.addLocal(obj.Type(), obj.Pos()) l := f.addLocal(obj.Type(), obj.Pos())
l.Comment = obj.Name() l.Comment = obj.Name()
@ -660,3 +654,19 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock {
func NewFunction(name string, sig *types.Signature, provenance string) *Function { func NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{name: name, Signature: sig, Synthetic: provenance} return &Function{name: name, Signature: sig, Synthetic: provenance}
} }
type extentNode [2]token.Pos
func (n extentNode) Pos() token.Pos { return n[0] }
func (n extentNode) End() token.Pos { return n[1] }
// Syntax returns an ast.Node whose Pos/End methods provide the
// lexical extent of the function if it was defined by Go source code
// (f.Synthetic==""), or nil otherwise.
//
// If f was built with debug information (see Package.SetDebugRef),
// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
// function. Otherwise, it is an opaque Node providing only position
// information; this avoids pinning the AST in memory.
//
func (f *Function) Syntax() ast.Node { return f.syntax }

View File

@ -339,6 +339,9 @@ func (s *sanity) checkFunction(fn *Function) bool {
s.errorf("nil Pkg") s.errorf("nil Pkg")
} }
} }
if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
}
for i, l := range fn.Locals { for i, l := range fn.Locals {
if l.Parent() != fn { if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i) s.errorf("Local %s at index %d has wrong parent", l.Name(), i)

View File

@ -26,8 +26,8 @@ import (
// Returns nil if not found; reasons might include: // Returns nil if not found; reasons might include:
// - the node is not enclosed by any function. // - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and // - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet (pkg.BuildPackage() // its SSA function has not been created yet
// has not yet been called). // (pkg.Build() has not yet been called).
// //
func EnclosingFunction(pkg *Package, path []ast.Node) *Function { func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function... // Start with package-level function...

View File

@ -268,6 +268,7 @@ type Function struct {
pos token.Pos pos token.Pos
Synthetic string // provenance of synthetic function; "" for true source functions Synthetic string // provenance of synthetic function; "" for true source functions
syntax ast.Node // *ast.Func{Decl,Lit}; replaced with simple ast.Node after build, unless debug mode
Enclosing *Function // enclosing function if anon; nil if global Enclosing *Function // enclosing function if anon; nil if global
Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error)
Prog *Program // enclosing program Prog *Program // enclosing program
@ -283,7 +284,6 @@ type Function struct {
currentBlock *BasicBlock // where to emit code currentBlock *BasicBlock // where to emit code
objects map[types.Object]Value // addresses of local variables objects map[types.Object]Value // addresses of local variables
namedResults []*Alloc // tuple of named results namedResults []*Alloc // tuple of named results
syntax *funcSyntax // abstract syntax trees for Go source functions
targets *targets // linked stack of branch targets targets *targets // linked stack of branch targets
lblocks map[*ast.Object]*lblock // labelled blocks lblocks map[*ast.Object]*lblock // labelled blocks
} }
@ -1168,6 +1168,9 @@ type MapUpdate struct {
// consistency is maintained during transformation passes by the // consistency is maintained during transformation passes by the
// ordinary SSA renaming machinery.) // ordinary SSA renaming machinery.)
// //
// DebugRefs are generated only for functions built with debugging
// enabled; see Package.SetDebugMode().
//
type DebugRef struct { type DebugRef struct {
anInstruction anInstruction
X Value // the value whose position we're declaring X Value // the value whose position we're declaring