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 class HValidator extends HInstructionVisitor { | |
6 bool isValid = true; | |
7 HGraph graph; | |
8 | |
9 void visitGraph(HGraph graph) { | |
10 this.graph = graph; | |
11 visitDominatorTree(graph); | |
12 } | |
13 | |
14 void markInvalid(String reason) { | |
15 print(reason); | |
16 isValid = false; | |
17 } | |
18 | |
19 // Note that during construction of the Ssa graph the basic blocks are | |
20 // not required to be valid yet. | |
21 void visitBasicBlock(HBasicBlock block) { | |
22 currentBlock = block; | |
23 if (!isValid) return; // Don't need to continue if we are already invalid. | |
24 | |
25 // Test that the last instruction is a branching instruction and that the | |
26 // basic block contains the branch-target. | |
27 if (block.first === null || block.last === null) { | |
28 markInvalid("empty block"); | |
29 } | |
30 if (block.last is !HControlFlow) { | |
31 markInvalid("block ends with non-tail node."); | |
32 } | |
33 if (block.last is HIf && block.successors.length != 2) { | |
34 markInvalid("If node without two successors"); | |
35 } | |
36 if (block.last is HConditionalBranch && block.successors.length != 2) { | |
37 markInvalid("Conditional node without two successors"); | |
38 } | |
39 if (block.last is HGoto && block.successors.length != 1) { | |
40 markInvalid("Goto node without one successor"); | |
41 } | |
42 if (block.last is HReturn && | |
43 (block.successors.length != 1 || !block.successors[0].isExitBlock())) { | |
44 markInvalid("Return node with > 1 succesor or not going to exit-block"); | |
45 } | |
46 if (block.last is HExit && !block.successors.isEmpty()) { | |
47 markInvalid("Exit block with successor"); | |
48 } | |
49 if (block.last is HThrow && !block.successors.isEmpty()) { | |
50 markInvalid("Throw block with successor"); | |
51 } | |
52 | |
53 if (block.successors.isEmpty() && | |
54 block.last is !HThrow && | |
55 !block.isExitBlock()) { | |
56 markInvalid("Non-exit or throw block without successor"); | |
57 } | |
58 | |
59 // Make sure that successors ids are always higher than the current one. | |
60 // TODO(floitsch): this is, of course, not true for back-branches. | |
61 if (block.id === null) markInvalid("block without id"); | |
62 for (HBasicBlock successor in block.successors) { | |
63 if (!isValid) break; | |
64 if (successor.id === null) markInvalid("successor without id"); | |
65 if (successor.id <= block.id && !successor.isLoopHeader()) { | |
66 markInvalid("successor with lower id, but not a loop-header"); | |
67 } | |
68 } | |
69 | |
70 // Make sure that the entries in the dominated-list are sorted. | |
71 int lastId = 0; | |
72 for (HBasicBlock dominated in block.dominatedBlocks) { | |
73 if (!isValid) break; | |
74 if (dominated.dominator !== block) { | |
75 markInvalid("dominated block not pointing back"); | |
76 } | |
77 if (dominated.id === null || dominated.id <= lastId) { | |
78 markInvalid("dominated.id === null or dominated has <= id"); | |
79 } | |
80 lastId = dominated.id; | |
81 } | |
82 | |
83 if (!isValid) return; | |
84 block.forEachPhi(visitInstruction); | |
85 super.visitBasicBlock(block); | |
86 } | |
87 | |
88 /** Returns how often [instruction] is contained in [instructions]. */ | |
89 static int countInstruction(List<HInstruction> instructions, | |
90 HInstruction instruction) { | |
91 int result = 0; | |
92 for (int i = 0; i < instructions.length; i++) { | |
93 if (instructions[i] === instruction) result++; | |
94 } | |
95 return result; | |
96 } | |
97 | |
98 /** | |
99 * Returns true if the predicate returns true for every instruction in the | |
100 * list. The argument to [f] is an instruction with the count of how often | |
101 * it appeared in the list [instructions]. | |
102 */ | |
103 static bool everyInstruction(List<HInstruction> instructions, Function f) { | |
104 var copy = new List<HInstruction>.from(instructions); | |
105 // TODO(floitsch): there is currently no way to sort HInstructions before | |
106 // we have assigned an ID. The loop is therefore O(n^2) for now. | |
107 for (int i = 0; i < copy.length; i++) { | |
108 var current = copy[i]; | |
109 if (current === null) continue; | |
110 int count = 1; | |
111 for (int j = i + 1; j < copy.length; j++) { | |
112 if (copy[j] === current) { | |
113 copy[j] = null; | |
114 count++; | |
115 } | |
116 } | |
117 if (!f(current, count)) return false; | |
118 } | |
119 return true; | |
120 } | |
121 | |
122 void visitInstruction(HInstruction instruction) { | |
123 // Verifies that we are in the use list of our inputs. | |
124 bool hasCorrectInputs(instruction) { | |
125 bool inBasicBlock = instruction.isInBasicBlock(); | |
126 return everyInstruction(instruction.inputs, (input, count) { | |
127 if (inBasicBlock) { | |
128 return countInstruction(input.usedBy, instruction) == count; | |
129 } else { | |
130 return countInstruction(input.usedBy, instruction) == 0; | |
131 } | |
132 }); | |
133 } | |
134 | |
135 // Verifies that all our uses have us in their inputs. | |
136 bool hasCorrectUses(instruction) { | |
137 if (!instruction.isInBasicBlock()) return true; | |
138 return everyInstruction(instruction.usedBy, (use, count) { | |
139 return countInstruction(use.inputs, instruction) == count; | |
140 }); | |
141 } | |
142 | |
143 if (instruction.block !== currentBlock) { | |
144 markInvalid("Instruction in wrong block"); | |
145 } | |
146 if (!hasCorrectInputs(instruction)) { | |
147 markInvalid("Incorrect inputs"); | |
148 } | |
149 if (!hasCorrectUses(instruction)) { | |
150 markInvalid("Incorrect uses"); | |
151 } | |
152 } | |
153 } | |
OLD | NEW |