diff --git a/ssa/builder.go b/ssa/builder.go index 8675fb611b..6474be6bb9 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -514,10 +514,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value { Enclosing: fn, Pkg: fn.Pkg, Prog: fn.Prog, - syntax: &funcSyntax{ - functype: e.Type, - body: e.Body, - }, + syntax: e, } fn.AnonFuncs = append(fn.AnonFuncs, fn2) b.buildFunction(fn2) @@ -2214,10 +2211,25 @@ func (b *builder) buildFunction(fn *Function) { if fn.Blocks != nil { 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.) + 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. if fn.Params == nil { // 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))() } fn.startBody() - fn.createSyntacticParams() - b.stmt(fn, fn.syntax.body) + fn.createSyntacticParams(recvField, functype) + b.stmt(fn, body) if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) { // Run function calls deferred in this function when // 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, Pkg: pkg, Prog: pkg.Prog, - syntax: &funcSyntax{ - functype: decl.Type, - recvField: decl.Recv, - body: decl.Body, - }, + syntax: decl, } var v Call diff --git a/ssa/create.go b/ssa/create.go index 67e730e859..5af1fd977b 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -95,26 +95,19 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { 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, - } - } fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), - Synthetic: synthetic, + syntax: syntax, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, - syntax: fs, } + if syntax == nil { + fn.Synthetic = "loaded from gc object file" + } + pkg.values[obj] = fn if fn.Signature.Recv() == nil { pkg.Members[name] = fn // package-level function diff --git a/ssa/func.go b/ssa/func.go index cf380fa42c..5294ebf8d2 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -147,13 +147,6 @@ type lblock struct { _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 // specified label, creating it if needed. // @@ -221,15 +214,14 @@ func (f *Function) startBody() { // syntax. In addition it populates the f.objects mapping. // // Preconditions: -// f.syntax != nil, i.e. this is a Go source function. // f.startBody() was called. // Postcondition: // 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). - if f.syntax.recvField != nil { - for _, field := range f.syntax.recvField.List { + if recv != nil { + for _, field := range recv.List { for _, n := range field.Names { f.addSpilledParam(f.Pkg.objectOf(n)) } @@ -241,9 +233,9 @@ func (f *Function) createSyntacticParams() { } // Parameters. - if f.syntax.functype.Params != nil { + if functype.Params != nil { 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 { f.addSpilledParam(f.Pkg.objectOf(n)) } @@ -255,8 +247,8 @@ func (f *Function) createSyntacticParams() { } // Named results. - if f.syntax.functype.Results != nil { - for _, field := range f.syntax.functype.Results.List { + if functype.Results != nil { + for _, field := range functype.Results.List { // Implicit "var" decl of locals for named results. for _, n := range field.Names { f.namedResults = append(f.namedResults, f.addLocalForIdent(n)) @@ -308,7 +300,11 @@ func (f *Function) finishBody() { f.objects = nil f.currentBlock = 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. j := 0 @@ -380,15 +376,13 @@ func (pkg *Package) SetDebugMode(debug bool) { // debugInfo reports whether debug info is wanted for this function. 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 // returns it. Its name and type are taken from obj. Subsequent // 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 { l := f.addLocal(obj.Type(), obj.Pos()) 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 { 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 } diff --git a/ssa/sanity.go b/ssa/sanity.go index c364d4e527..81f33516d7 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -339,6 +339,9 @@ func (s *sanity) checkFunction(fn *Function) bool { 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 { if l.Parent() != fn { s.errorf("Local %s at index %d has wrong parent", l.Name(), i) diff --git a/ssa/source.go b/ssa/source.go index 726d80ece4..051d9012ed 100644 --- a/ssa/source.go +++ b/ssa/source.go @@ -26,8 +26,8 @@ import ( // Returns nil if not found; reasons might include: // - the node is not enclosed by any function. // - the node is within an anonymous function (FuncLit) and -// its SSA function has not been created yet (pkg.BuildPackage() -// has not yet been called). +// its SSA function has not been created yet +// (pkg.Build() has not yet been called). // func EnclosingFunction(pkg *Package, path []ast.Node) *Function { // Start with package-level function... diff --git a/ssa/ssa.go b/ssa/ssa.go index e3624d4b75..d958e3aa2e 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -268,6 +268,7 @@ type Function struct { pos token.Pos 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 Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) Prog *Program // enclosing program @@ -283,7 +284,6 @@ type Function struct { currentBlock *BasicBlock // where to emit code objects map[types.Object]Value // addresses of local variables namedResults []*Alloc // tuple of named results - syntax *funcSyntax // abstract syntax trees for Go source functions targets *targets // linked stack of branch targets lblocks map[*ast.Object]*lblock // labelled blocks } @@ -1168,6 +1168,9 @@ type MapUpdate struct { // consistency is maintained during transformation passes by the // ordinary SSA renaming machinery.) // +// DebugRefs are generated only for functions built with debugging +// enabled; see Package.SetDebugMode(). +// type DebugRef struct { anInstruction X Value // the value whose position we're declaring