diff --git a/go/pointer/gen.go b/go/pointer/gen.go index f0ac1aeb..69eda20a 100644 --- a/go/pointer/gen.go +++ b/go/pointer/gen.go @@ -1104,7 +1104,6 @@ func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite func (a *analysis) genRootCalls() *cgnode { r := ssa.NewFunction("", new(types.Signature), "root of callgraph") r.Prog = a.prog // hack. - r.Enclosing = r // hack, so Function.String() doesn't crash r.String() // (asserts that it doesn't crash) root := a.makeCGNode(r, 0, nil) diff --git a/go/ssa/builder.go b/go/ssa/builder.go index 9d7d9f37..be55bdb6 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -472,7 +472,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value { name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)), Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature), pos: e.Type.Func, - Enclosing: fn, + parent: fn, Pkg: fn.Pkg, Prog: fn.Prog, syntax: e, diff --git a/go/ssa/const.go b/go/ssa/const.go index 36ae99ea..1a4a4d0c 100644 --- a/go/ssa/const.go +++ b/go/ssa/const.go @@ -100,6 +100,8 @@ func (c *Const) Referrers() *[]Instruction { return nil } +func (c *Const) Parent() *Function { return nil } + func (c *Const) Pos() token.Pos { return token.NoPos } diff --git a/go/ssa/doc.go b/go/ssa/doc.go index c919823a..9cb00e38 100644 --- a/go/ssa/doc.go +++ b/go/ssa/doc.go @@ -42,6 +42,7 @@ // - Member: a named member of a Go package. // - Value: an expression that yields a value. // - Instruction: a statement that consumes values and performs computation. +// - Node: a Value or Instruction (emphasizing its membership in the SSA value graph) // // A computation that yields a result implements both the Value and // Instruction interfaces. The following table shows for each diff --git a/go/ssa/func.go b/go/ssa/func.go index 5f8d216f..1bd24ec0 100644 --- a/go/ssa/func.go +++ b/go/ssa/func.go @@ -423,10 +423,10 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value { // Definition must be in an enclosing function; // plumb it through intervening closures. - if f.Enclosing == nil { + if f.parent == nil { panic("no Value for type.Object " + obj.Name()) } - outer := f.Enclosing.lookup(obj, true) // escaping + outer := f.parent.lookup(obj, true) // escaping v := &Capture{ name: obj.Name(), typ: outer.Type(), @@ -466,7 +466,7 @@ func (f *Function) emit(instr Instruction) Value { // func (f *Function) RelString(from *types.Package) string { // Anonymous? - if f.Enclosing != nil { + if f.parent != nil { return f.name } @@ -544,8 +544,8 @@ func WriteFunction(buf *bytes.Buffer, f *Function) { fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos)) } - if f.Enclosing != nil { - fmt.Fprintf(buf, "# Parent: %s\n", f.Enclosing.Name()) + if f.parent != nil { + fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name()) } if f.Recover != nil { diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go index 53acb0db..60da061a 100644 --- a/go/ssa/interp/interp.go +++ b/go/ssa/interp/interp.go @@ -489,7 +489,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, caller: caller, // for panic/recover fn: fn, } - if fn.Enclosing == nil { + if fn.Parent() == nil { name := fn.String() if ext := externals[name]; ext != nil { if i.mode&EnableTracing != 0 { diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go index dd80b5dd..48e967a5 100644 --- a/go/ssa/sanity.go +++ b/go/ssa/sanity.go @@ -357,11 +357,13 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { case *Const, *Global, *Builtin: continue // not local case *Function: - if val.Enclosing == nil { + if val.parent == nil { continue // only anon functions are local } } + // TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined. + if refs := val.Referrers(); refs != nil { for _, ref := range *refs { if ref == instr { @@ -442,8 +444,8 @@ func (s *sanity) checkFunction(fn *Function) bool { s.block = nil for i, anon := range fn.AnonFuncs { - if anon.Enclosing != fn { - s.errorf("AnonFuncs[%d]=%s but %s.Enclosing=%s", i, anon, anon, anon.Enclosing) + if anon.Parent() != fn { + s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent()) } } s.fn = nil diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go index 9151c97b..b940255b 100644 --- a/go/ssa/ssa.go +++ b/go/ssa/ssa.go @@ -123,6 +123,10 @@ type Value interface { // types of their operands. Type() types.Type + // Parent returns the function to which this Value belongs. + // It returns nil for named Functions, Builtin, Const and Global. + Parent() *Function + // Referrers returns the list of instructions that have this // value as one of their operands; it may contain duplicates // if an instruction has a repeated operand. @@ -130,9 +134,9 @@ type Value interface { // Referrers actually returns a pointer through which the // caller may perform mutations to the object's state. // - // Referrers is currently only defined for the function-local - // values Capture, Parameter, Functions (iff anonymous) and - // all value-defining instructions. + // Referrers is currently only defined if Parent()!=nil, + // i.e. for the function-local values Capture, Parameter, + // Functions (iff anonymous) and all value-defining instructions. // It returns nil for named Functions, Builtin, Const and Global. // // Instruction.Operands contains the inverse of this relation. @@ -199,9 +203,10 @@ type Instruction interface { // user-provided slice, and returns the resulting slice, // permitting avoidance of memory allocation. // - // The operands are appended in undefined order; the addresses - // are always non-nil but may point to a nil Value. Clients - // may store through the pointers, e.g. to effect a value + // The operands are appended in undefined order, but the order + // is consistent for a given Instruction; the addresses are + // always non-nil but may point to a nil Value. Clients may + // store through the pointers, e.g. to effect a value // renaming. // // Value.Referrers is a subset of the inverse of this @@ -229,6 +234,28 @@ type Instruction interface { Pos() token.Pos } +// A Node is a node in the SSA value graph. Every concrete type that +// implements Node is also either a Value, an Instruction, or both. +// +// Node contains the methods common to Value and Instruction, plus the +// Operands and Referrers methods generalized to return nil for +// non-Instructions and non-Values, respectively. +// +// Node is provided to simplify SSA graph algorithms. Clients should +// use the more specific and informative Value or Instruction +// interfaces where appropriate. +// +type Node interface { + // Common methods: + String() string + Pos() token.Pos + Parent() *Function + + // Partial methods: + Operands(rands []*Value) []*Value // nil for non-Instructions + Referrers() *[]Instruction // nil for non-Values +} + // Function represents the parameters, results and code of a function // or method. // @@ -250,11 +277,11 @@ type Instruction interface { // of the function's named return parameters followed by a return of // the loaded values. // -// A nested function that refers to one or more lexically enclosing -// local variables ("free variables") has Capture parameters. Such -// functions cannot be called directly but require a value created by -// MakeClosure which, via its Bindings, supplies values for these -// parameters. +// A nested function (Parent()!=nil) that refers to one or more +// lexically enclosing local variables ("free variables") has Capture +// parameters. Such functions cannot be called directly but require a +// value created by MakeClosure which, via its Bindings, supplies +// values for these parameters. // // If the function is a method (Signature.Recv() != nil) then the first // element of Params is the receiver parameter. @@ -275,7 +302,7 @@ type Function struct { 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 + parent *Function // enclosing function if anon; nil if global Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) Prog *Program // enclosing program Params []*Parameter // function parameters; for methods, includes receiver @@ -284,7 +311,7 @@ type Function struct { Blocks []*BasicBlock // basic blocks of the function; nil => external Recover *BasicBlock // optional; control transfers here after recovered panic AnonFuncs []*Function // anonymous functions directly beneath this one - referrers []Instruction // referring instructions (iff Enclosing != nil) + referrers []Instruction // referring instructions (iff Parent() != nil) // The following fields are set transiently during building, // then cleared. @@ -1359,6 +1386,7 @@ func (v *Builtin) Name() string { return v.object.Name() } func (*Builtin) Referrers() *[]Instruction { return nil } func (v *Builtin) Pos() token.Pos { return token.NoPos } func (v *Builtin) Object() types.Object { return v.object } +func (v *Builtin) Parent() *Function { return nil } func (v *Capture) Type() types.Type { return v.typ } func (v *Capture) Name() string { return v.name } @@ -1368,6 +1396,7 @@ func (v *Capture) Parent() *Function { return v.parent } func (v *Global) Type() types.Type { return v.typ } func (v *Global) Name() string { return v.name } +func (v *Global) Parent() *Function { return nil } func (v *Global) Pos() token.Pos { return v.pos } func (v *Global) Referrers() *[]Instruction { return nil } func (v *Global) Token() token.Token { return token.VAR } @@ -1383,8 +1412,9 @@ func (v *Function) Token() token.Token { return token.FUNC } func (v *Function) Object() types.Object { return v.object } func (v *Function) String() string { return v.RelString(nil) } func (v *Function) Package() *Package { return v.Pkg } +func (v *Function) Parent() *Function { return v.parent } func (v *Function) Referrers() *[]Instruction { - if v.Enclosing != nil { + if v.parent != nil { return &v.referrers } return nil @@ -1412,6 +1442,7 @@ func (v *register) setPos(pos token.Pos) { v.pos = pos } func (v *anInstruction) Parent() *Function { return v.block.parent } func (v *anInstruction) Block() *BasicBlock { return v.block } func (v *anInstruction) setBlock(block *BasicBlock) { v.block = block } +func (v *anInstruction) Referrers() *[]Instruction { return nil } func (t *Type) Name() string { return t.object.Name() } func (t *Type) Pos() token.Pos { return t.object.Pos() } @@ -1638,3 +1669,11 @@ func (v *TypeAssert) Operands(rands []*Value) []*Value { func (v *UnOp) Operands(rands []*Value) []*Value { return append(rands, &v.X) } + +// Non-Instruction Values: +func (v *Builtin) Operands(rands []*Value) []*Value { return rands } +func (v *Capture) Operands(rands []*Value) []*Value { return rands } +func (v *Const) Operands(rands []*Value) []*Value { return rands } +func (v *Function) Operands(rands []*Value) []*Value { return rands } +func (v *Global) Operands(rands []*Value) []*Value { return rands } +func (v *Parameter) Operands(rands []*Value) []*Value { return rands } diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go index 51c8d4ed..a5d8a826 100644 --- a/go/ssa/testmain.go +++ b/go/ssa/testmain.go @@ -104,7 +104,7 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", - Enclosing: main, + parent: main, Pkg: testmain, Prog: prog, } diff --git a/godoc/analysis/callgraph.go b/godoc/analysis/callgraph.go index 4db50c7b..a6f1dba2 100644 --- a/godoc/analysis/callgraph.go +++ b/godoc/analysis/callgraph.go @@ -235,10 +235,10 @@ func funcToken(fn *ssa.Function) token.Pos { // prettyFunc pretty-prints fn for the user interface. // TODO(adonovan): return HTML so we have more markup freedom. func prettyFunc(this *types.Package, fn *ssa.Function) string { - if fn.Enclosing != nil { + if fn.Parent() != nil { return fmt.Sprintf("%s in %s", types.TypeString(this, fn.Signature), - prettyFunc(this, fn.Enclosing)) + prettyFunc(this, fn.Parent())) } if fn.Synthetic != "" && fn.Name() == "init" { // (This is the actual initializer, not a declared 'func init').