Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(520)

Delta Between Two Patch Sets: src/pkg/exp/ssa/sanity.go

Issue 7199052: code review 7199052: exp/ssa: (#2 of 5): core utilities (Closed)
Left Patch Set: Created 12 years, 2 months ago
Right Patch Set: diff -r ca5e5de48173 https://code.google.com/p/go/ Created 12 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/exp/ssa/print.go ('k') | src/pkg/exp/ssa/ssa.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 package ssa
2
3 // An optional pass for sanity checking invariants of the SSA representation.
4 // Currently it checks CFG invariants but little at the instruction level.
5
6 import (
7 "bytes"
8 "fmt"
9 "io"
10 "os"
11 )
12
13 type sanity struct {
14 reporter io.Writer
15 fn *Function
16 block *BasicBlock
17 insane bool
18 }
19
20 // SanityCheck performs integrity checking of the SSA representation
21 // of the function fn and returns true if it was valid. Diagnostics
22 // are written to reporter if non-nil, os.Stderr otherwise. Some
23 // diagnostics are only warnings and do not imply a negative result.
24 //
25 // Sanity checking is intended to facilitate the debugging of code
26 // transformation passes.
27 //
28 func SanityCheck(fn *Function, reporter io.Writer) bool {
29 if reporter == nil {
30 reporter = os.Stderr
31 }
32 return (&sanity{reporter: reporter}).checkFunction(fn)
33 }
34
35 // MustSanityCheck is like SanityCheck but panics instead of returning
36 // a negative result.
37 //
38 func MustSanityCheck(fn *Function, reporter io.Writer) {
39 if !SanityCheck(fn, reporter) {
40 panic("SanityCheck failed")
41 }
42 }
43
44 // blockNames returns the names of the specified blocks as a
45 // human-readable string.
46 //
47 func blockNames(blocks []*BasicBlock) string {
48 var buf bytes.Buffer
49 for i, b := range blocks {
50 if i > 0 {
51 io.WriteString(&buf, ", ")
52 }
53 io.WriteString(&buf, b.Name)
54 }
55 return buf.String()
56 }
57
58 func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
59 fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn.FullName())
60 if s.block != nil {
61 fmt.Fprintf(s.reporter, ", block %s", s.block.Name)
62 }
63 io.WriteString(s.reporter, ": ")
64 fmt.Fprintf(s.reporter, format, args...)
65 io.WriteString(s.reporter, "\n")
66 }
67
68 func (s *sanity) errorf(format string, args ...interface{}) {
69 s.insane = true
70 s.diagnostic("Error", format, args...)
71 }
72
73 func (s *sanity) warnf(format string, args ...interface{}) {
74 s.diagnostic("Warning", format, args...)
75 }
76
77 // findDuplicate returns an arbitrary basic block that appeared more
78 // than once in blocks, or nil if all were unique.
79 func findDuplicate(blocks []*BasicBlock) *BasicBlock {
80 if len(blocks) < 2 {
81 return nil
82 }
83 if blocks[0] == blocks[1] {
84 return blocks[0]
85 }
86 // Slow path:
87 m := make(map[*BasicBlock]bool)
88 for _, b := range blocks {
89 if m[b] {
90 return b
91 }
92 m[b] = true
93 }
94 return nil
95 }
96
97 func (s *sanity) checkInstr(idx int, instr Instruction) {
98 switch instr := instr.(type) {
99 case *If, *Jump, *Ret:
100 s.errorf("control flow instruction not at end of block")
101 case *Phi:
102 if idx == 0 {
103 // It suffices to apply this check to just the first phi node.
104 if dup := findDuplicate(s.block.Preds); dup != nil {
105 s.errorf("phi node in block with duplicate prede cessor %s", dup.Name)
106 }
107 } else {
108 prev := s.block.Instrs[idx-1]
109 if _, ok := prev.(*Phi); !ok {
110 s.errorf("Phi instruction follows a non-Phi: %T" , prev)
111 }
112 }
113 if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
114 s.errorf("phi node has %d edges but %d predecessors", ne , np)
115 }
116
117 case *Alloc:
118 case *Call:
119 case *BinOp:
120 case *UnOp:
121 case *MakeClosure:
122 case *MakeChan:
123 case *MakeMap:
124 case *MakeSlice:
125 case *Slice:
126 case *Field:
127 case *FieldAddr:
128 case *IndexAddr:
129 case *Index:
130 case *Select:
131 case *Range:
132 case *TypeAssert:
133 case *Extract:
134 case *Go:
135 case *Defer:
136 case *Send:
137 case *Store:
138 case *MapUpdate:
139 case *Next:
140 case *Lookup:
141 case *Conv:
142 case *ChangeInterface:
143 case *MakeInterface:
144 // TODO(adonovan): implement checks.
145 default:
146 panic(fmt.Sprintf("Unknown instruction type: %T", instr))
147 }
148 }
149
150 func (s *sanity) checkFinalInstr(idx int, instr Instruction) {
151 switch instr.(type) {
152 case *If:
153 if nsuccs := len(s.block.Succs); nsuccs != 2 {
154 s.errorf("If-terminated block has %d successors; expecte d 2", nsuccs)
155 return
156 }
157 if s.block.Succs[0] == s.block.Succs[1] {
158 s.errorf("If-instruction has same True, False target blo cks: %s", s.block.Succs[0].Name)
159 return
160 }
161
162 case *Jump:
163 if nsuccs := len(s.block.Succs); nsuccs != 1 {
164 s.errorf("Jump-terminated block has %d successors; expec ted 1", nsuccs)
165 return
166 }
167
168 case *Ret:
169 if nsuccs := len(s.block.Succs); nsuccs != 0 {
170 s.errorf("Ret-terminated block has %d successors; expect ed none", nsuccs)
171 return
172 }
173 // TODO(adonovan): check number and types of results
174
175 default:
176 s.errorf("non-control flow instruction at end of block")
177 }
178 }
179
180 func (s *sanity) checkBlock(b *BasicBlock, isEntry bool) {
181 s.block = b
182
183 // Check all blocks are reachable.
184 // (The entry block is always implicitly reachable.)
185 if !isEntry && len(b.Preds) == 0 {
186 s.warnf("unreachable block")
187 if b.Instrs == nil {
188 // Since this block is about to be pruned,
189 // tolerating transient problems in it
190 // simplifies other optimisations.
191 return
192 }
193 }
194
195 // Check predecessor and successor relations are dual.
196 for _, a := range b.Preds {
197 found := false
198 for _, bb := range a.Succs {
199 if bb == b {
200 found = true
201 break
202 }
203 }
204 if !found {
205 s.errorf("expected successor edge in predecessor %s; fou nd only: %s", a.Name, blockNames(a.Succs))
206 }
207 }
208 for _, c := range b.Succs {
209 found := false
210 for _, bb := range c.Preds {
211 if bb == b {
212 found = true
213 break
214 }
215 }
216 if !found {
217 s.errorf("expected predecessor edge in successor %s; fou nd only: %s", c.Name, blockNames(c.Preds))
218 }
219 }
220
221 // Check each instruction is sane.
222 n := len(b.Instrs)
223 if n == 0 {
224 s.errorf("basic block contains no instructions")
225 }
226 for j, instr := range b.Instrs {
227 if b2 := instr.Block(); b2 == nil {
228 s.errorf("nil Block() for instruction at index %d", j)
229 continue
230 } else if b2 != b {
231 s.errorf("wrong Block() (%s) for instruction at index %d ", b2.Name, j)
232 continue
233 }
234 if j < n-1 {
235 s.checkInstr(j, instr)
236 } else {
237 s.checkFinalInstr(j, instr)
238 }
239 }
240 }
241
242 func (s *sanity) checkFunction(fn *Function) bool {
243 // TODO(adonovan): check Function invariants:
244 // - check owning Package (if any) contains this function.
245 // - check params match signature
246 // - check locals are all !Heap
247 // - check transient fields are nil
248 // - check block labels are unique (warning)
249 s.fn = fn
250 if fn.Prog == nil {
251 s.errorf("nil Prog")
252 }
253 for i, b := range fn.Blocks {
254 if b == nil {
255 s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
256 continue
257 }
258 s.checkBlock(b, i == 0)
259 }
260 s.block = nil
261 s.fn = nil
262 return !s.insane
263 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b