mirror of
https://github.com/golang/go
synced 2024-11-18 15:24:41 -07:00
go/ssa: add Node interface: common parts of Value+Instruction, plus Operands/Referrers.
Also: - extend Parent() to all Values and add to interface: (Builtin/Const/Global => nil; Function => Enclosing) - hide Function.Enclosing since it's now redundant wrt Parent() - make (*Function).String robust for synthetics without pkg object LGTM=gri R=gri CC=golang-codereviews, khr https://golang.org/cl/87580044
This commit is contained in:
parent
74117bcfd8
commit
04427c85cf
@ -1104,7 +1104,6 @@ func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite
|
|||||||
func (a *analysis) genRootCalls() *cgnode {
|
func (a *analysis) genRootCalls() *cgnode {
|
||||||
r := ssa.NewFunction("<root>", new(types.Signature), "root of callgraph")
|
r := ssa.NewFunction("<root>", new(types.Signature), "root of callgraph")
|
||||||
r.Prog = a.prog // hack.
|
r.Prog = a.prog // hack.
|
||||||
r.Enclosing = r // hack, so Function.String() doesn't crash
|
|
||||||
r.String() // (asserts that it doesn't crash)
|
r.String() // (asserts that it doesn't crash)
|
||||||
root := a.makeCGNode(r, 0, nil)
|
root := a.makeCGNode(r, 0, nil)
|
||||||
|
|
||||||
|
@ -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)),
|
name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
|
||||||
Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
|
Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
|
||||||
pos: e.Type.Func,
|
pos: e.Type.Func,
|
||||||
Enclosing: fn,
|
parent: fn,
|
||||||
Pkg: fn.Pkg,
|
Pkg: fn.Pkg,
|
||||||
Prog: fn.Prog,
|
Prog: fn.Prog,
|
||||||
syntax: e,
|
syntax: e,
|
||||||
|
@ -100,6 +100,8 @@ func (c *Const) Referrers() *[]Instruction {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Const) Parent() *Function { return nil }
|
||||||
|
|
||||||
func (c *Const) Pos() token.Pos {
|
func (c *Const) Pos() token.Pos {
|
||||||
return token.NoPos
|
return token.NoPos
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
// - Member: a named member of a Go package.
|
// - Member: a named member of a Go package.
|
||||||
// - Value: an expression that yields a value.
|
// - Value: an expression that yields a value.
|
||||||
// - Instruction: a statement that consumes values and performs computation.
|
// - 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
|
// A computation that yields a result implements both the Value and
|
||||||
// Instruction interfaces. The following table shows for each
|
// Instruction interfaces. The following table shows for each
|
||||||
|
@ -423,10 +423,10 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
|
|||||||
|
|
||||||
// Definition must be in an enclosing function;
|
// Definition must be in an enclosing function;
|
||||||
// plumb it through intervening closures.
|
// plumb it through intervening closures.
|
||||||
if f.Enclosing == nil {
|
if f.parent == nil {
|
||||||
panic("no Value for type.Object " + obj.Name())
|
panic("no Value for type.Object " + obj.Name())
|
||||||
}
|
}
|
||||||
outer := f.Enclosing.lookup(obj, true) // escaping
|
outer := f.parent.lookup(obj, true) // escaping
|
||||||
v := &Capture{
|
v := &Capture{
|
||||||
name: obj.Name(),
|
name: obj.Name(),
|
||||||
typ: outer.Type(),
|
typ: outer.Type(),
|
||||||
@ -466,7 +466,7 @@ func (f *Function) emit(instr Instruction) Value {
|
|||||||
//
|
//
|
||||||
func (f *Function) RelString(from *types.Package) string {
|
func (f *Function) RelString(from *types.Package) string {
|
||||||
// Anonymous?
|
// Anonymous?
|
||||||
if f.Enclosing != nil {
|
if f.parent != nil {
|
||||||
return f.name
|
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))
|
fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Enclosing != nil {
|
if f.parent != nil {
|
||||||
fmt.Fprintf(buf, "# Parent: %s\n", f.Enclosing.Name())
|
fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Recover != nil {
|
if f.Recover != nil {
|
||||||
|
@ -489,7 +489,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
|
|||||||
caller: caller, // for panic/recover
|
caller: caller, // for panic/recover
|
||||||
fn: fn,
|
fn: fn,
|
||||||
}
|
}
|
||||||
if fn.Enclosing == nil {
|
if fn.Parent() == nil {
|
||||||
name := fn.String()
|
name := fn.String()
|
||||||
if ext := externals[name]; ext != nil {
|
if ext := externals[name]; ext != nil {
|
||||||
if i.mode&EnableTracing != 0 {
|
if i.mode&EnableTracing != 0 {
|
||||||
|
@ -357,11 +357,13 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
|||||||
case *Const, *Global, *Builtin:
|
case *Const, *Global, *Builtin:
|
||||||
continue // not local
|
continue // not local
|
||||||
case *Function:
|
case *Function:
|
||||||
if val.Enclosing == nil {
|
if val.parent == nil {
|
||||||
continue // only anon functions are local
|
continue // only anon functions are local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
|
||||||
|
|
||||||
if refs := val.Referrers(); refs != nil {
|
if refs := val.Referrers(); refs != nil {
|
||||||
for _, ref := range *refs {
|
for _, ref := range *refs {
|
||||||
if ref == instr {
|
if ref == instr {
|
||||||
@ -442,8 +444,8 @@ func (s *sanity) checkFunction(fn *Function) bool {
|
|||||||
|
|
||||||
s.block = nil
|
s.block = nil
|
||||||
for i, anon := range fn.AnonFuncs {
|
for i, anon := range fn.AnonFuncs {
|
||||||
if anon.Enclosing != fn {
|
if anon.Parent() != fn {
|
||||||
s.errorf("AnonFuncs[%d]=%s but %s.Enclosing=%s", i, anon, anon, anon.Enclosing)
|
s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.fn = nil
|
s.fn = nil
|
||||||
|
@ -123,6 +123,10 @@ type Value interface {
|
|||||||
// types of their operands.
|
// types of their operands.
|
||||||
Type() types.Type
|
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
|
// Referrers returns the list of instructions that have this
|
||||||
// value as one of their operands; it may contain duplicates
|
// value as one of their operands; it may contain duplicates
|
||||||
// if an instruction has a repeated operand.
|
// if an instruction has a repeated operand.
|
||||||
@ -130,9 +134,9 @@ type Value interface {
|
|||||||
// Referrers actually returns a pointer through which the
|
// Referrers actually returns a pointer through which the
|
||||||
// caller may perform mutations to the object's state.
|
// caller may perform mutations to the object's state.
|
||||||
//
|
//
|
||||||
// Referrers is currently only defined for the function-local
|
// Referrers is currently only defined if Parent()!=nil,
|
||||||
// values Capture, Parameter, Functions (iff anonymous) and
|
// i.e. for the function-local values Capture, Parameter,
|
||||||
// all value-defining instructions.
|
// Functions (iff anonymous) and all value-defining instructions.
|
||||||
// It returns nil for named Functions, Builtin, Const and Global.
|
// It returns nil for named Functions, Builtin, Const and Global.
|
||||||
//
|
//
|
||||||
// Instruction.Operands contains the inverse of this relation.
|
// Instruction.Operands contains the inverse of this relation.
|
||||||
@ -199,9 +203,10 @@ type Instruction interface {
|
|||||||
// user-provided slice, and returns the resulting slice,
|
// user-provided slice, and returns the resulting slice,
|
||||||
// permitting avoidance of memory allocation.
|
// permitting avoidance of memory allocation.
|
||||||
//
|
//
|
||||||
// The operands are appended in undefined order; the addresses
|
// The operands are appended in undefined order, but the order
|
||||||
// are always non-nil but may point to a nil Value. Clients
|
// is consistent for a given Instruction; the addresses are
|
||||||
// may store through the pointers, e.g. to effect a value
|
// always non-nil but may point to a nil Value. Clients may
|
||||||
|
// store through the pointers, e.g. to effect a value
|
||||||
// renaming.
|
// renaming.
|
||||||
//
|
//
|
||||||
// Value.Referrers is a subset of the inverse of this
|
// Value.Referrers is a subset of the inverse of this
|
||||||
@ -229,6 +234,28 @@ type Instruction interface {
|
|||||||
Pos() token.Pos
|
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
|
// Function represents the parameters, results and code of a function
|
||||||
// or method.
|
// or method.
|
||||||
//
|
//
|
||||||
@ -250,11 +277,11 @@ type Instruction interface {
|
|||||||
// of the function's named return parameters followed by a return of
|
// of the function's named return parameters followed by a return of
|
||||||
// the loaded values.
|
// the loaded values.
|
||||||
//
|
//
|
||||||
// A nested function that refers to one or more lexically enclosing
|
// A nested function (Parent()!=nil) that refers to one or more
|
||||||
// local variables ("free variables") has Capture parameters. Such
|
// lexically enclosing local variables ("free variables") has Capture
|
||||||
// functions cannot be called directly but require a value created by
|
// parameters. Such functions cannot be called directly but require a
|
||||||
// MakeClosure which, via its Bindings, supplies values for these
|
// value created by MakeClosure which, via its Bindings, supplies
|
||||||
// parameters.
|
// values for these parameters.
|
||||||
//
|
//
|
||||||
// If the function is a method (Signature.Recv() != nil) then the first
|
// If the function is a method (Signature.Recv() != nil) then the first
|
||||||
// element of Params is the receiver parameter.
|
// element of Params is the receiver parameter.
|
||||||
@ -275,7 +302,7 @@ type Function struct {
|
|||||||
|
|
||||||
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
|
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)
|
Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error)
|
||||||
Prog *Program // enclosing program
|
Prog *Program // enclosing program
|
||||||
Params []*Parameter // function parameters; for methods, includes receiver
|
Params []*Parameter // function parameters; for methods, includes receiver
|
||||||
@ -284,7 +311,7 @@ type Function struct {
|
|||||||
Blocks []*BasicBlock // basic blocks of the function; nil => external
|
Blocks []*BasicBlock // basic blocks of the function; nil => external
|
||||||
Recover *BasicBlock // optional; control transfers here after recovered panic
|
Recover *BasicBlock // optional; control transfers here after recovered panic
|
||||||
AnonFuncs []*Function // anonymous functions directly beneath this one
|
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,
|
// The following fields are set transiently during building,
|
||||||
// then cleared.
|
// then cleared.
|
||||||
@ -1359,6 +1386,7 @@ func (v *Builtin) Name() string { return v.object.Name() }
|
|||||||
func (*Builtin) Referrers() *[]Instruction { return nil }
|
func (*Builtin) Referrers() *[]Instruction { return nil }
|
||||||
func (v *Builtin) Pos() token.Pos { return token.NoPos }
|
func (v *Builtin) Pos() token.Pos { return token.NoPos }
|
||||||
func (v *Builtin) Object() types.Object { return v.object }
|
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) Type() types.Type { return v.typ }
|
||||||
func (v *Capture) Name() string { return v.name }
|
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) Type() types.Type { return v.typ }
|
||||||
func (v *Global) Name() string { return v.name }
|
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) Pos() token.Pos { return v.pos }
|
||||||
func (v *Global) Referrers() *[]Instruction { return nil }
|
func (v *Global) Referrers() *[]Instruction { return nil }
|
||||||
func (v *Global) Token() token.Token { return token.VAR }
|
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) Object() types.Object { return v.object }
|
||||||
func (v *Function) String() string { return v.RelString(nil) }
|
func (v *Function) String() string { return v.RelString(nil) }
|
||||||
func (v *Function) Package() *Package { return v.Pkg }
|
func (v *Function) Package() *Package { return v.Pkg }
|
||||||
|
func (v *Function) Parent() *Function { return v.parent }
|
||||||
func (v *Function) Referrers() *[]Instruction {
|
func (v *Function) Referrers() *[]Instruction {
|
||||||
if v.Enclosing != nil {
|
if v.parent != nil {
|
||||||
return &v.referrers
|
return &v.referrers
|
||||||
}
|
}
|
||||||
return nil
|
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) Parent() *Function { return v.block.parent }
|
||||||
func (v *anInstruction) Block() *BasicBlock { return v.block }
|
func (v *anInstruction) Block() *BasicBlock { return v.block }
|
||||||
func (v *anInstruction) setBlock(block *BasicBlock) { v.block = 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) Name() string { return t.object.Name() }
|
||||||
func (t *Type) Pos() token.Pos { return t.object.Pos() }
|
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 {
|
func (v *UnOp) Operands(rands []*Value) []*Value {
|
||||||
return append(rands, &v.X)
|
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 }
|
||||||
|
@ -104,7 +104,7 @@ func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
|
|||||||
name: "matcher",
|
name: "matcher",
|
||||||
Signature: testingMainParams.At(0).Type().(*types.Signature),
|
Signature: testingMainParams.At(0).Type().(*types.Signature),
|
||||||
Synthetic: "test matcher predicate",
|
Synthetic: "test matcher predicate",
|
||||||
Enclosing: main,
|
parent: main,
|
||||||
Pkg: testmain,
|
Pkg: testmain,
|
||||||
Prog: prog,
|
Prog: prog,
|
||||||
}
|
}
|
||||||
|
@ -235,10 +235,10 @@ func funcToken(fn *ssa.Function) token.Pos {
|
|||||||
// prettyFunc pretty-prints fn for the user interface.
|
// prettyFunc pretty-prints fn for the user interface.
|
||||||
// TODO(adonovan): return HTML so we have more markup freedom.
|
// TODO(adonovan): return HTML so we have more markup freedom.
|
||||||
func prettyFunc(this *types.Package, fn *ssa.Function) string {
|
func prettyFunc(this *types.Package, fn *ssa.Function) string {
|
||||||
if fn.Enclosing != nil {
|
if fn.Parent() != nil {
|
||||||
return fmt.Sprintf("%s in %s",
|
return fmt.Sprintf("%s in %s",
|
||||||
types.TypeString(this, fn.Signature),
|
types.TypeString(this, fn.Signature),
|
||||||
prettyFunc(this, fn.Enclosing))
|
prettyFunc(this, fn.Parent()))
|
||||||
}
|
}
|
||||||
if fn.Synthetic != "" && fn.Name() == "init" {
|
if fn.Synthetic != "" && fn.Name() == "init" {
|
||||||
// (This is the actual initializer, not a declared 'func init').
|
// (This is the actual initializer, not a declared 'func init').
|
||||||
|
Loading…
Reference in New Issue
Block a user