OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of ssa; | |
6 | |
7 class HValidator extends HInstructionVisitor { | |
8 bool isValid = true; | |
9 HGraph graph; | |
10 | |
11 void visitGraph(HGraph visitee) { | |
12 graph = visitee; | |
13 visitDominatorTree(visitee); | |
14 } | |
15 | |
16 void markInvalid(String reason) { | |
17 print(reason); | |
18 isValid = false; | |
19 } | |
20 | |
21 // Note that during construction of the Ssa graph the basic blocks are | |
22 // not required to be valid yet. | |
23 void visitBasicBlock(HBasicBlock block) { | |
24 currentBlock = block; | |
25 if (!isValid) return; // Don't need to continue if we are already invalid. | |
26 | |
27 // Test that the last instruction is a branching instruction and that the | |
28 // basic block contains the branch-target. | |
29 if (block.first == null || block.last == null) { | |
30 markInvalid("empty block"); | |
31 } | |
32 if (block.last is !HControlFlow) { | |
33 markInvalid("block ends with non-tail node."); | |
34 } | |
35 if (block.last is HIf && block.successors.length != 2) { | |
36 markInvalid("If node without two successors"); | |
37 } | |
38 if (block.last is HConditionalBranch && block.successors.length != 2) { | |
39 markInvalid("Conditional node without two successors"); | |
40 } | |
41 if (block.last is HLoopBranch) { | |
42 // Assert that the block we inserted to avoid critical edges satisfies | |
43 // our assumptions. That is, it must not contain any instructions | |
44 // (although it may contain phi-updates). | |
45 HBasicBlock avoidCriticalEdgeBlock = block.successors.last; | |
46 if (avoidCriticalEdgeBlock.first is! HGoto) { | |
47 markInvalid("Critical edge block contains instructions"); | |
48 } | |
49 } | |
50 if (block.last is HGoto && block.successors.length != 1) { | |
51 markInvalid("Goto node with not exactly one successor"); | |
52 } | |
53 if (block.last is HJump && block.successors.length != 1) { | |
54 markInvalid("Break or continue node without one successor"); | |
55 } | |
56 if ((block.last is HReturn || block.last is HThrow) && | |
57 (block.successors.length != 1 || !block.successors[0].isExitBlock())) { | |
58 markInvalid("Return or throw node with > 1 successor " | |
59 "or not going to exit-block"); | |
60 } | |
61 if (block.last is HExit && !block.successors.isEmpty) { | |
62 markInvalid("Exit block with successor"); | |
63 } | |
64 | |
65 if (block.successors.isEmpty && !block.isExitBlock()) { | |
66 markInvalid("Non-exit block without successor"); | |
67 } | |
68 | |
69 // Check that successors ids are always higher than the current one. | |
70 // TODO(floitsch): this is, of course, not true for back-branches. | |
71 if (block.id == null) markInvalid("block without id"); | |
72 for (HBasicBlock successor in block.successors) { | |
73 if (!isValid) break; | |
74 if (successor.id == null) markInvalid("successor without id"); | |
75 if (successor.id <= block.id && !successor.isLoopHeader()) { | |
76 markInvalid("successor with lower id, but not a loop-header"); | |
77 } | |
78 } | |
79 // Make sure we don't have a critical edge. | |
80 if (isValid && block.successors.length > 1 && | |
81 block.last is! HTry && block.last is! HExitTry && | |
82 block.last is! HSwitch) { | |
83 for (HBasicBlock successor in block.successors) { | |
84 if (!isValid) break; | |
85 if (successor.predecessors.length >= 2) { | |
86 markInvalid("SSA graph contains critical edge."); | |
87 } | |
88 } | |
89 } | |
90 | |
91 // Check that the entries in the dominated-list are sorted. | |
92 int lastId = 0; | |
93 for (HBasicBlock dominated in block.dominatedBlocks) { | |
94 if (!isValid) break; | |
95 if (!identical(dominated.dominator, block)) { | |
96 markInvalid("dominated block not pointing back"); | |
97 } | |
98 if (dominated.id == null || dominated.id <= lastId) { | |
99 markInvalid("dominated.id == null or dominated has <= id"); | |
100 } | |
101 lastId = dominated.id; | |
102 } | |
103 | |
104 if (!isValid) return; | |
105 block.forEachPhi(visitInstruction); | |
106 | |
107 // Check that the blocks of the parameters of a phi are dominating the | |
108 // corresponding predecessor block. Note that a block dominates | |
109 // itself. | |
110 block.forEachPhi((HPhi phi) { | |
111 assert(phi.inputs.length <= block.predecessors.length); | |
112 for (int i = 0; i < phi.inputs.length; i++) { | |
113 HInstruction input = phi.inputs[i]; | |
114 if (!input.block.dominates(block.predecessors[i])) { | |
115 markInvalid("Definition does not dominate use"); | |
116 } | |
117 } | |
118 }); | |
119 | |
120 // Check that the blocks of the inputs of an instruction dominate the | |
121 // instruction's block. | |
122 block.forEachInstruction((HInstruction instruction) { | |
123 for (HInstruction input in instruction.inputs) { | |
124 if (!input.block.dominates(block)) { | |
125 markInvalid("Definition does not dominate use"); | |
126 } | |
127 } | |
128 }); | |
129 | |
130 super.visitBasicBlock(block); | |
131 } | |
132 | |
133 /** Returns how often [instruction] is contained in [instructions]. */ | |
134 static int countInstruction(List<HInstruction> instructions, | |
135 HInstruction instruction) { | |
136 int result = 0; | |
137 for (int i = 0; i < instructions.length; i++) { | |
138 if (identical(instructions[i], instruction)) result++; | |
139 } | |
140 return result; | |
141 } | |
142 | |
143 /** | |
144 * Returns true if the predicate returns true for every instruction in the | |
145 * list. The argument to [f] is an instruction with the count of how often | |
146 * it appeared in the list [instructions]. | |
147 */ | |
148 static bool everyInstruction(List<HInstruction> instructions, Function f) { | |
149 var copy = new List<HInstruction>.from(instructions); | |
150 // TODO(floitsch): there is currently no way to sort HInstructions before | |
151 // we have assigned an ID. The loop is therefore O(n^2) for now. | |
152 for (int i = 0; i < copy.length; i++) { | |
153 var current = copy[i]; | |
154 if (current == null) continue; | |
155 int count = 1; | |
156 for (int j = i + 1; j < copy.length; j++) { | |
157 if (identical(copy[j], current)) { | |
158 copy[j] = null; | |
159 count++; | |
160 } | |
161 } | |
162 if (!f(current, count)) return false; | |
163 } | |
164 return true; | |
165 } | |
166 | |
167 void visitInstruction(HInstruction instruction) { | |
168 // Verifies that we are in the use list of our inputs. | |
169 bool hasCorrectInputs() { | |
170 bool inBasicBlock = instruction.isInBasicBlock(); | |
171 return everyInstruction(instruction.inputs, (input, count) { | |
172 if (inBasicBlock) { | |
173 return input.isInBasicBlock() | |
174 && countInstruction(input.usedBy, instruction) == count; | |
175 } else { | |
176 return countInstruction(input.usedBy, instruction) == 0; | |
177 } | |
178 }); | |
179 } | |
180 | |
181 // Verifies that all our uses have us in their inputs. | |
182 bool hasCorrectUses() { | |
183 if (!instruction.isInBasicBlock()) return true; | |
184 return everyInstruction(instruction.usedBy, (use, count) { | |
185 return use.isInBasicBlock() | |
186 && countInstruction(use.inputs, instruction) == count; | |
187 }); | |
188 } | |
189 | |
190 if (!identical(instruction.block, currentBlock)) { | |
191 markInvalid("Instruction in wrong block"); | |
192 } | |
193 if (!hasCorrectInputs()) { | |
194 markInvalid("Incorrect inputs"); | |
195 } | |
196 if (!hasCorrectUses()) { | |
197 markInvalid("Incorrect uses"); | |
198 } | |
199 } | |
200 } | |
OLD | NEW |