diff --git a/ssa/blockopt.go b/ssa/blockopt.go index bea292016f2..0253b350b63 100644 --- a/ssa/blockopt.go +++ b/ssa/blockopt.go @@ -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 diff --git a/ssa/builder.go b/ssa/builder.go index 491f65ac5cf..3d1c6325103 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -2040,8 +2040,6 @@ start: fn.currentBlock = done - // TODO statement debug info. - case *ast.SwitchStmt: b.switchStmt(fn, s, label) diff --git a/ssa/func.go b/ssa/func.go index cd94e8d97d9..cf1aa6561d5 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -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 diff --git a/ssa/sanity.go b/ssa/sanity.go index 81f33516d7d..f6afb062649 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -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) + } + } } } diff --git a/ssa/ssa.go b/ssa/ssa.go index c463664ee6d..a4f968eaa1a 100644 --- a/ssa/ssa.go +++ b/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 } @@ -1368,12 +1368,17 @@ func (*Global) Referrers() *[]Instruction { return nil } func (v *Global) Token() token.Token { return token.VAR } 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) Name() string { return v.name } +func (v *Function) Type() types.Type { return v.Signature } +func (v *Function) Pos() token.Pos { return v.pos } +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() }