go.tools/ssa: populate Function.Referrers(), for anon functions.
Added sanity check to ensure Operands/Referrers are complete and dual. Also: unexport Instruction.setBlock (=> no longer user-implementable). R=gri CC=golang-dev https://golang.org/cl/22150043
This commit is contained in:
parent
7123ca00a8
commit
f1e5b03c6e
|
|
@ -85,7 +85,7 @@ func jumpThreading(f *Function, b *BasicBlock) bool {
|
||||||
// If a now has two edges to c, replace its degenerate If by Jump.
|
// If a now has two edges to c, replace its degenerate If by Jump.
|
||||||
if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
|
if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
|
||||||
jump := new(Jump)
|
jump := new(Jump)
|
||||||
jump.SetBlock(a)
|
jump.setBlock(a)
|
||||||
a.Instrs[len(a.Instrs)-1] = jump
|
a.Instrs[len(a.Instrs)-1] = jump
|
||||||
a.Succs = a.Succs[:1]
|
a.Succs = a.Succs[:1]
|
||||||
c.removePred(b)
|
c.removePred(b)
|
||||||
|
|
@ -120,7 +120,7 @@ func fuseBlocks(f *Function, a *BasicBlock) bool {
|
||||||
// Eliminate jump at end of A, then copy all of B across.
|
// Eliminate jump at end of A, then copy all of B across.
|
||||||
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
|
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
|
||||||
for _, instr := range b.Instrs {
|
for _, instr := range b.Instrs {
|
||||||
instr.SetBlock(a)
|
instr.setBlock(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A inherits B's successors
|
// A inherits B's successors
|
||||||
|
|
|
||||||
|
|
@ -2040,8 +2040,6 @@ start:
|
||||||
|
|
||||||
fn.currentBlock = done
|
fn.currentBlock = done
|
||||||
|
|
||||||
// TODO statement debug info.
|
|
||||||
|
|
||||||
case *ast.SwitchStmt:
|
case *ast.SwitchStmt:
|
||||||
b.switchStmt(fn, s, label)
|
b.switchStmt(fn, s, label)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ func (b *BasicBlock) String() string {
|
||||||
// If the instruction defines a Value, it is returned.
|
// If the instruction defines a Value, it is returned.
|
||||||
//
|
//
|
||||||
func (b *BasicBlock) emit(i Instruction) Value {
|
func (b *BasicBlock) emit(i Instruction) Value {
|
||||||
i.SetBlock(b)
|
i.setBlock(b)
|
||||||
b.Instrs = append(b.Instrs, i)
|
b.Instrs = append(b.Instrs, i)
|
||||||
v, _ := i.(Value)
|
v, _ := i.(Value)
|
||||||
return v
|
return v
|
||||||
|
|
|
||||||
|
|
@ -291,14 +291,11 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each instruction is sane.
|
// Check each instruction is sane.
|
||||||
// TODO(adonovan): check Instruction invariants:
|
|
||||||
// - check Operands is dual to Value.Referrers.
|
|
||||||
// - check all Operands that are also Instructions belong to s.fn too
|
|
||||||
// (and for bonus marks, that their block dominates block b).
|
|
||||||
n := len(b.Instrs)
|
n := len(b.Instrs)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
s.errorf("basic block contains no instructions")
|
s.errorf("basic block contains no instructions")
|
||||||
}
|
}
|
||||||
|
var rands [10]*Value // reuse storage
|
||||||
for j, instr := range b.Instrs {
|
for j, instr := range b.Instrs {
|
||||||
if instr == nil {
|
if instr == nil {
|
||||||
s.errorf("nil instruction at index %d", j)
|
s.errorf("nil instruction at index %d", j)
|
||||||
|
|
@ -316,6 +313,48 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
||||||
} else {
|
} else {
|
||||||
s.checkFinalInstr(j, instr)
|
s.checkFinalInstr(j, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check Instruction.Operands.
|
||||||
|
operands:
|
||||||
|
for i, op := range instr.Operands(rands[:0]) {
|
||||||
|
if op == nil {
|
||||||
|
s.errorf("nil operand pointer %d of %s", i, instr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := *op
|
||||||
|
if val == nil {
|
||||||
|
continue // a nil operand is ok
|
||||||
|
}
|
||||||
|
// Check that Operands that are also Instructions belong to same function.
|
||||||
|
// TODO(adonovan): also check their block dominates block b.
|
||||||
|
if val, ok := val.(Instruction); ok {
|
||||||
|
if val.Parent() != s.fn {
|
||||||
|
s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that each function-local operand of
|
||||||
|
// instr refers back to instr. (NB: quadratic)
|
||||||
|
switch val := val.(type) {
|
||||||
|
case *Const, *Global, *Builtin:
|
||||||
|
continue // not local
|
||||||
|
case *Function:
|
||||||
|
if val.Enclosing == nil {
|
||||||
|
continue // only anon functions are local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if refs := val.Referrers(); refs != nil {
|
||||||
|
for _, ref := range *refs {
|
||||||
|
if ref == instr {
|
||||||
|
continue operands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
|
||||||
|
} else {
|
||||||
|
s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
33
ssa/ssa.go
33
ssa/ssa.go
|
|
@ -127,8 +127,9 @@ type Value interface {
|
||||||
// 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 for the function-local
|
||||||
// values Capture, Parameter and all value-defining instructions.
|
// values Capture, Parameter, Functions (iff anonymous) and
|
||||||
// It returns nil for Function, Builtin, Const and Global.
|
// all value-defining instructions.
|
||||||
|
// 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.
|
||||||
Referrers() *[]Instruction
|
Referrers() *[]Instruction
|
||||||
|
|
@ -184,10 +185,8 @@ type Instruction interface {
|
||||||
// belongs.
|
// belongs.
|
||||||
Block() *BasicBlock
|
Block() *BasicBlock
|
||||||
|
|
||||||
// SetBlock sets the basic block to which this instruction
|
// setBlock sets the basic block to which this instruction belongs.
|
||||||
// belongs.
|
setBlock(*BasicBlock)
|
||||||
// TODO(adonovan): unexport this.
|
|
||||||
SetBlock(*BasicBlock)
|
|
||||||
|
|
||||||
// Operands returns the operands of this instruction: the
|
// Operands returns the operands of this instruction: the
|
||||||
// set of Values it references.
|
// set of Values it references.
|
||||||
|
|
@ -279,6 +278,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)
|
||||||
|
|
||||||
// The following fields are set transiently during building,
|
// The following fields are set transiently during building,
|
||||||
// then cleared.
|
// then cleared.
|
||||||
|
|
@ -1212,7 +1212,7 @@ type register struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// anInstruction is a mix-in embedded by all Instructions.
|
// anInstruction is a mix-in embedded by all Instructions.
|
||||||
// It provides the implementations of the Block and SetBlock methods.
|
// It provides the implementations of the Block and setBlock methods.
|
||||||
type anInstruction struct {
|
type anInstruction struct {
|
||||||
block *BasicBlock // the basic block of this instruction
|
block *BasicBlock // the basic block of this instruction
|
||||||
}
|
}
|
||||||
|
|
@ -1368,12 +1368,17 @@ func (*Global) Referrers() *[]Instruction { return nil }
|
||||||
func (v *Global) Token() token.Token { return token.VAR }
|
func (v *Global) Token() token.Token { return token.VAR }
|
||||||
func (v *Global) Object() types.Object { return v.object }
|
func (v *Global) Object() types.Object { return v.object }
|
||||||
|
|
||||||
func (v *Function) Name() string { return v.name }
|
func (v *Function) Name() string { return v.name }
|
||||||
func (v *Function) Type() types.Type { return v.Signature }
|
func (v *Function) Type() types.Type { return v.Signature }
|
||||||
func (v *Function) Pos() token.Pos { return v.pos }
|
func (v *Function) Pos() token.Pos { return v.pos }
|
||||||
func (*Function) Referrers() *[]Instruction { return nil }
|
func (v *Function) Token() token.Token { return token.FUNC }
|
||||||
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) Referrers() *[]Instruction {
|
||||||
|
if v.Enclosing != nil {
|
||||||
|
return &v.referrers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Parameter) Type() types.Type { return v.typ }
|
func (v *Parameter) Type() types.Type { return v.typ }
|
||||||
func (v *Parameter) Name() string { return v.name }
|
func (v *Parameter) Name() string { return v.name }
|
||||||
|
|
@ -1396,7 +1401,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 (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() }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue