mirror of
https://github.com/golang/go
synced 2024-11-18 20:14:43 -07:00
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 len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
|
||||
jump := new(Jump)
|
||||
jump.SetBlock(a)
|
||||
jump.setBlock(a)
|
||||
a.Instrs[len(a.Instrs)-1] = jump
|
||||
a.Succs = a.Succs[:1]
|
||||
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.
|
||||
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
|
||||
for _, instr := range b.Instrs {
|
||||
instr.SetBlock(a)
|
||||
instr.setBlock(a)
|
||||
}
|
||||
|
||||
// A inherits B's successors
|
||||
|
@ -2040,8 +2040,6 @@ start:
|
||||
|
||||
fn.currentBlock = done
|
||||
|
||||
// TODO statement debug info.
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
b.switchStmt(fn, s, label)
|
||||
|
||||
|
@ -37,7 +37,7 @@ func (b *BasicBlock) String() string {
|
||||
// If the instruction defines a Value, it is returned.
|
||||
//
|
||||
func (b *BasicBlock) emit(i Instruction) Value {
|
||||
i.SetBlock(b)
|
||||
i.setBlock(b)
|
||||
b.Instrs = append(b.Instrs, i)
|
||||
v, _ := i.(Value)
|
||||
return v
|
||||
|
@ -291,14 +291,11 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
if n == 0 {
|
||||
s.errorf("basic block contains no instructions")
|
||||
}
|
||||
var rands [10]*Value // reuse storage
|
||||
for j, instr := range b.Instrs {
|
||||
if instr == nil {
|
||||
s.errorf("nil instruction at index %d", j)
|
||||
@ -316,6 +313,48 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
23
ssa/ssa.go
23
ssa/ssa.go
@ -127,8 +127,9 @@ type Value interface {
|
||||
// caller may perform mutations to the object's state.
|
||||
//
|
||||
// Referrers is currently only defined for the function-local
|
||||
// values Capture, Parameter and all value-defining instructions.
|
||||
// It returns nil for Function, Builtin, Const and Global.
|
||||
// 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.
|
||||
Referrers() *[]Instruction
|
||||
@ -184,10 +185,8 @@ type Instruction interface {
|
||||
// belongs.
|
||||
Block() *BasicBlock
|
||||
|
||||
// SetBlock sets the basic block to which this instruction
|
||||
// belongs.
|
||||
// TODO(adonovan): unexport this.
|
||||
SetBlock(*BasicBlock)
|
||||
// setBlock sets the basic block to which this instruction belongs.
|
||||
setBlock(*BasicBlock)
|
||||
|
||||
// Operands returns the operands of this instruction: the
|
||||
// set of Values it references.
|
||||
@ -279,6 +278,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)
|
||||
|
||||
// The following fields are set transiently during building,
|
||||
// then cleared.
|
||||
@ -1212,7 +1212,7 @@ type register struct {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
block *BasicBlock // the basic block of this instruction
|
||||
}
|
||||
@ -1371,9 +1371,14 @@ func (v *Global) Object() types.Object { return v.object }
|
||||
func (v *Function) Name() string { return v.name }
|
||||
func (v *Function) Type() types.Type { return v.Signature }
|
||||
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) 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) 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) 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) Pos() token.Pos { return t.object.Pos() }
|
||||
|
Loading…
Reference in New Issue
Block a user