| 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 |