1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:54:42 -07:00

go.tools/go/ssa: introduce a sanity check for dead referrers

This extends the sanity checker to identify and report referrers
which do not appear in the function's instruction lists, and fixes two
bugs in the lifting algorithm which were caught by the sanity check.

LGTM=adonovan
R=adonovan
CC=axwalk, golang-codereviews
https://golang.org/cl/110210045
This commit is contained in:
Peter Collingbourne 2014-07-10 06:27:25 -04:00 committed by Alan Donovan
parent daba707591
commit 661e836afb
2 changed files with 52 additions and 2 deletions

View File

@ -108,6 +108,21 @@ func buildDomFrontier(fn *Function) domFrontier {
return df
}
func removeInstr(refs []Instruction, instr Instruction) []Instruction {
i := 0
for _, ref := range refs {
if ref == instr {
continue
}
refs[i] = ref
i++
}
for j := i; j != len(refs); j++ {
refs[j] = nil // aid GC
}
return refs[:i]
}
// lift attempts to replace local and new Allocs accessed only with
// load/store by SSA registers, inserting φ-nodes where necessary.
// The result is a program in classical pruned SSA form.
@ -208,7 +223,13 @@ func lift(fn *Function) {
j := 0
for _, np := range nps {
if !phiIsLive(np.phi) {
continue // discard it
// discard it, first removing it from referrers
for _, newval := range np.phi.Edges {
if refs := newval.Referrers(); refs != nil {
*refs = removeInstr(*refs, np.phi)
}
}
continue
}
nps[j] = np
j++
@ -495,6 +516,10 @@ func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
instr, instr.Val.Name())
}
// Remove the store from the referrer list of the stored value.
if refs := instr.Val.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
// Delete the Store.
u.Instrs[i] = nil
u.gaps++

View File

@ -20,6 +20,7 @@ type sanity struct {
reporter io.Writer
fn *Function
block *BasicBlock
instrs map[Instruction]struct{}
insane bool
}
@ -185,7 +186,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
}
}
// Check that value-defining instructions have valid types.
// Check that value-defining instructions have valid types
// and a valid referrer list.
if v, ok := instr.(Value); ok {
t := v.Type()
if t == nil {
@ -195,6 +197,7 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
}
s.checkReferrerList(v)
}
// Untyped constants are legal as instruction Operands(),
@ -381,6 +384,19 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
}
}
func (s *sanity) checkReferrerList(v Value) {
refs := v.Referrers()
if refs == nil {
s.errorf("%s has missing referrer list", v.Name())
return
}
for i, ref := range *refs {
if _, ok := s.instrs[ref]; !ok {
s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
}
}
}
func (s *sanity) checkFunction(fn *Function) bool {
// TODO(adonovan): check Function invariants:
// - check params match signature
@ -418,15 +434,24 @@ func (s *sanity) checkFunction(fn *Function) bool {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
// Build the set of valid referrers.
s.instrs = make(map[Instruction]struct{})
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
s.instrs[instr] = struct{}{}
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
s.checkReferrerList(fv)
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {