| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Instead of emitting each SSA instruction with a temporary variable | 6 * Instead of emitting each SSA instruction with a temporary variable |
| 7 * mark instructions that can be emitted at their use-site. | 7 * mark instructions that can be emitted at their use-site. |
| 8 * For example, in: | 8 * For example, in: |
| 9 * t0 = 4; | 9 * t0 = 4; |
| 10 * t1 = 3; | 10 * t1 = 3; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 visitDominatorTree(graph); | 22 visitDominatorTree(graph); |
| 23 } | 23 } |
| 24 | 24 |
| 25 void visitInstruction(HInstruction instruction) { | 25 void visitInstruction(HInstruction instruction) { |
| 26 // A code motion invariant instruction is dealt before visiting it. | 26 // A code motion invariant instruction is dealt before visiting it. |
| 27 assert(!instruction.isCodeMotionInvariant()); | 27 assert(!instruction.isCodeMotionInvariant()); |
| 28 for (HInstruction input in instruction.inputs) { | 28 for (HInstruction input in instruction.inputs) { |
| 29 if (!generateAtUseSite.contains(input) | 29 if (!generateAtUseSite.contains(input) |
| 30 && !input.isCodeMotionInvariant() | 30 && !input.isCodeMotionInvariant() |
| 31 && input.usedBy.length == 1 | 31 && input.usedBy.length == 1 |
| 32 && input is! HPhi) { | 32 && input is !HPhi) { |
| 33 expectedInputs.add(input); | 33 expectedInputs.add(input); |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 } | 36 } |
| 37 | 37 |
| 38 // The codegen might use the input multiple times, so it must not be | 38 // The codegen might use the input multiple times, so it must not be |
| 39 // set generate at use site. | 39 // set generate at use site. |
| 40 void visitIs(HIs instruction) {} | 40 void visitIs(HIs instruction) {} |
| 41 | 41 |
| 42 // A check method must not have its input generate at use site, | 42 // A check method must not have its input generate at use site, |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 } | 126 } |
| 127 } | 127 } |
| 128 | 128 |
| 129 /** | 129 /** |
| 130 * Detect control flow arising from short-circuit logical operators, and | 130 * Detect control flow arising from short-circuit logical operators, and |
| 131 * prepare the program to be generated using these operators instead of | 131 * prepare the program to be generated using these operators instead of |
| 132 * nested ifs and boolean variables. | 132 * nested ifs and boolean variables. |
| 133 */ | 133 */ |
| 134 class SsaConditionMerger extends HGraphVisitor { | 134 class SsaConditionMerger extends HGraphVisitor { |
| 135 Set<HInstruction> generateAtUseSite; | 135 Set<HInstruction> generateAtUseSite; |
| 136 Map<HPhi, String> logicalOperations; | 136 Set<HInstruction> logicalOperations; |
| 137 | 137 |
| 138 SsaConditionMerger(this.generateAtUseSite, this.logicalOperations); | 138 SsaConditionMerger(this.generateAtUseSite, this.logicalOperations); |
| 139 | 139 |
| 140 void visitGraph(HGraph graph) { | 140 void visitGraph(HGraph graph) { |
| 141 visitDominatorTree(graph); | 141 visitPostDominatorTree(graph); |
| 142 } | 142 } |
| 143 | 143 |
| 144 /** | 144 /** |
| 145 * Returns true if the given instruction is an expression that uses up all | 145 * Check if a block has at least one statement other than |
| 146 * instructions up to the given [limit]. | 146 * [instruction]. |
| 147 * | |
| 148 * That is, all instructions starting after the [limit] block (at the branch | |
| 149 * leading to the [instruction]) down to the given [instruction] can be | |
| 150 * generated at use-site. | |
| 151 */ | 147 */ |
| 152 bool isExpression(HInstruction instruction, HBasicBlock limit) { | 148 bool hasStatement(HBasicBlock block, HInstruction instruction) { |
| 153 HBasicBlock block = instruction.block; | 149 // If [instruction] is not in [block], then if the block is not |
| 154 if (instruction is HPhi) { | 150 // empty, we know there will be a statement to emit. |
| 155 if (!logicalOperations.containsKey(instruction)) { | 151 if (instruction.block !== block) return block.last !== block.first; |
| 156 return false; | 152 |
| 157 } | 153 // If [instruction] is not the last instruction of the block |
| 158 } else { | 154 // before the control flow instruction, then we will have to emit |
| 159 while (instruction.previous != null) { | 155 // a statement for that last instruction. |
| 160 instruction = instruction.previous; | 156 if (instruction !== block.last.previous) return true; |
| 161 if (!generateAtUseSite.contains(instruction)) { | 157 |
| 162 return false; | 158 // If one of the instructions in the block until [instruction] is |
| 163 } | 159 // not generated at use site, then we will have to emit a |
| 164 } | 160 // statement for it. |
| 165 // Now [instruction] is the first instruction of the block | 161 // TODO(ngeoffray): we could generate a comma separated |
| 166 // (aka [block.first]). If there are also a phi, check the current | 162 // list of expressions. |
| 167 // [instruction] normally and make [instruction] be the phi. | 163 for (HInstruction temp = block.first; |
| 168 if (!block.phis.isEmpty()) { | 164 temp !== instruction; |
| 169 if (!generateAtUseSite.contains(instruction)) { | 165 temp = temp.next) { |
| 170 return false; | 166 if (!generateAtUseSite.contains(temp)) return true; |
| 171 } | |
| 172 instruction = block.phis.last; | |
| 173 if (block.phis.first !== instruction) { | |
| 174 // If there is more than one phi, don't try to undestand it. | |
| 175 return false; | |
| 176 } | |
| 177 if (!logicalOperations.containsKey(instruction)) { | |
| 178 return false; | |
| 179 } | |
| 180 } | |
| 181 } | 167 } |
| 182 if (instruction is HPhi) { | |
| 183 assert(logicalOperations.containsKey(instruction)); | |
| 184 return isExpression(instruction.inputs[0], limit); | |
| 185 } | |
| 186 if (block.predecessors.length !== 1) { | |
| 187 return false; | |
| 188 } | |
| 189 HBasicBlock previousBlock = block.predecessors[0]; | |
| 190 if (previousBlock === limit) return true; | |
| 191 if (previousBlock.successors.length !== 1 || | |
| 192 previousBlock.last is! HGoto) { | |
| 193 return false; | |
| 194 } | |
| 195 return isExpression(previousBlock.last, limit); | |
| 196 } | |
| 197 | 168 |
| 198 void replaceWithLogicalOperator(HPhi phi, String type) { | 169 return false; |
| 199 if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi); | |
| 200 logicalOperations[phi] = type; | |
| 201 // If the phi corresponds to logical control flow, mark the | |
| 202 // control-flow instructions as generate-at-use-site. | |
| 203 generateAtUseSite.add(phi.block.predecessors[0].last); | |
| 204 generateAtUseSite.add(phi.block.predecessors[1].last); | |
| 205 // If the first input is only used as branch condition and result, it too | |
| 206 // can be generate-at-use-site. | |
| 207 if (phi.inputs[0].usedBy.length == 2) { | |
| 208 generateAtUseSite.add(phi.inputs[0]); | |
| 209 } | |
| 210 if (phi.inputs[1].usedBy.length == 1) { | |
| 211 generateAtUseSite.add(phi.inputs[1]); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 bool canGenerateAtUseSite(HPhi phi) { | |
| 216 if (phi.usedBy.length != 1) { | |
| 217 return false; | |
| 218 } | |
| 219 assert(phi.next == null); | |
| 220 HInstruction use = phi.usedBy[0]; | |
| 221 | |
| 222 HInstruction current = phi.block.first; | |
| 223 while (current != use) { | |
| 224 // Check that every instruction between the start of the block and the | |
| 225 // use of the phi (i.e., every instruction between the phi and the use) | |
| 226 // is itself generated at use site. That means that the phi can be | |
| 227 // moved to its use site without crossing any other code, because those | |
| 228 // instructions (if any) are moved too. | |
| 229 if (current is! HControlFlow && !generateAtUseSite.contains(current)) { | |
| 230 return false; | |
| 231 } | |
| 232 if (current.next != null) { | |
| 233 current = current.next; | |
| 234 } else if (current is HPhi) { | |
| 235 current = current.block.first; | |
| 236 } else { | |
| 237 assert(current is HControlFlow); | |
| 238 if (current is !HGoto) { | |
| 239 return false; | |
| 240 } | |
| 241 HBasicBlock nextBlock = current.block.successors[0]; | |
| 242 if (!nextBlock.phis.isEmpty()) { | |
| 243 current = nextBlock.phis.first; | |
| 244 } else { | |
| 245 current = nextBlock.first; | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 return true; | |
| 250 } | |
| 251 | |
| 252 HInstruction previousInstruction(HInstruction instruction) { | |
| 253 if (instruction.previous != null) return instruction.previous; | |
| 254 HBasicBlock block = instruction.block; | |
| 255 if (instruction is! HPhi) { | |
| 256 if (block.phis.last != null) return block.phis.last; | |
| 257 } | |
| 258 if (block.predecessors.length == 1) { | |
| 259 HBasicBlock previousBlock = block.predecessors[0]; | |
| 260 if (previousBlock.last is HGoto) { | |
| 261 assert(previousBlock.successors.length == 1); | |
| 262 assert(previousBlock.successors[0] === block); | |
| 263 return previousInstruction(previousBlock.last); | |
| 264 } | |
| 265 } | |
| 266 return null; | |
| 267 } | |
| 268 | |
| 269 void detectLogicControlFlow(HPhi phi) { | |
| 270 // Check for the most common pattern for a short-circuit logic operation: | |
| 271 // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1) | |
| 272 // |\ | |
| 273 // | B1 b1 = ...; goto B2 | |
| 274 // |/ | |
| 275 // B2 b2 = phi(b0,b1); if(b2) ... | |
| 276 // TODO(lrn): Also recognize ?:-flow? | |
| 277 if (phi.inputs.length != 2) return; | |
| 278 HInstruction first = phi.inputs[0]; | |
| 279 HBasicBlock firstBlock = phi.block.predecessors[0]; | |
| 280 HInstruction second = phi.inputs[1]; | |
| 281 HBasicBlock secondBlock = phi.block.predecessors[1]; | |
| 282 // Check second input of phi being an expression followed by a goto. | |
| 283 if (second.usedBy.length != 1) return; | |
| 284 HInstruction secondNext = | |
| 285 (second is HPhi) ? secondBlock.first : second.next; | |
| 286 if (secondNext != secondBlock.last) return; | |
| 287 if (secondBlock.last is !HGoto) return; | |
| 288 if (secondBlock.successors[0] != phi.block) return; | |
| 289 if (!isExpression(second, firstBlock)) return; | |
| 290 // Check first input of phi being followed by a (possibly negated) | |
| 291 // conditional branch based on the same value. | |
| 292 if (firstBlock != phi.block.dominator) return; | |
| 293 if (firstBlock.last is! HIf) return; | |
| 294 if (firstBlock.successors[1] != phi.block) return; | |
| 295 HIf firstBranch = firstBlock.last; | |
| 296 HInstruction condition = firstBranch.inputs[0]; | |
| 297 if (condition === first) { | |
| 298 replaceWithLogicalOperator(phi, "&&"); | |
| 299 } else if (condition is HNot && | |
| 300 condition.inputs[0] == first) { | |
| 301 replaceWithLogicalOperator(phi, "||"); | |
| 302 // If the negation is only used by this logical operation, or only by | |
| 303 // logical operators in general, it won't need to be generated. | |
| 304 if (!generateAtUseSite.contains(condition)) { | |
| 305 for (HInstruction user in condition.usedBy) { | |
| 306 if (user is! HIf || !generateAtUseSite.contains(user)) { | |
| 307 return; | |
| 308 } | |
| 309 } | |
| 310 generateAtUseSite.add(condition); | |
| 311 } | |
| 312 } | |
| 313 return; | |
| 314 } | 170 } |
| 315 | 171 |
| 316 void visitBasicBlock(HBasicBlock block) { | 172 void visitBasicBlock(HBasicBlock block) { |
| 317 if (!block.phis.isEmpty() && | 173 if (block.last is !HIf) return; |
| 318 block.phis.first === block.phis.last) { | 174 HIf startIf = block.last; |
| 319 detectLogicControlFlow(block.phis.first); | 175 HBasicBlock end = startIf.joinBlock; |
| 176 |
| 177 // We check that the structure is the following: |
| 178 // If |
| 179 // / \ |
| 180 // / \ |
| 181 // 1 expr goto |
| 182 // goto / |
| 183 // \ / |
| 184 // \ / |
| 185 // phi(expr, true|false) |
| 186 if (end == null) return; |
| 187 if (end.phis.isEmpty()) return; |
| 188 if (end.phis.first !== end.phis.last) return; |
| 189 HBasicBlock thenBlock = startIf.thenBlock; |
| 190 HBasicBlock elseBlock = startIf.elseBlock; |
| 191 if (end.predecessors[0] !== thenBlock) return; |
| 192 if (end.predecessors[1] !== elseBlock) return; |
| 193 HPhi phi = end.phis.first; |
| 194 if (!phi.inputs[1].isConstantBoolean()) return; |
| 195 |
| 196 HInstruction thenInput = phi.inputs[0]; |
| 197 if (hasStatement(thenBlock, thenInput)) return; |
| 198 if (elseBlock.first !== elseBlock.last) return; |
| 199 |
| 200 // From now on, we have recognized a logical operation built from |
| 201 // the builder. We don't expect the builder and the optimizers to |
| 202 // generate the then and else branches with multiple successors, |
| 203 // and the join branch to have more than two predecessors. |
| 204 |
| 205 // Mark the if instruction as a logical operation. |
| 206 logicalOperations.add(startIf); |
| 207 |
| 208 // If the logical operation is only used by the first instruction |
| 209 // of its block, it can be generated at use site. |
| 210 if (phi.usedBy.length == 1 && phi.usedBy[0] === phi.block.first) { |
| 211 generateAtUseSite.add(phi); |
| 212 } |
| 213 |
| 214 // If [thenInput] is defined in [thenBlock], then it is only used |
| 215 // by [phi] and can be generated at use site. |
| 216 if (thenInput.block === thenBlock) { |
| 217 assert(thenInput.usedBy.length == 1); |
| 218 generateAtUseSite.add(thenInput); |
| 320 } | 219 } |
| 321 } | 220 } |
| 322 } | 221 } |
| 323 | 222 |
| 324 // Precedence information for JavaScript operators. | 223 // Precedence information for JavaScript operators. |
| 325 class JSPrecedence { | 224 class JSPrecedence { |
| 326 // Used as precedence for something that's not even an expression. | 225 // Used as precedence for something that's not even an expression. |
| 327 static final int STATEMENT_PRECEDENCE = 0; | 226 static final int STATEMENT_PRECEDENCE = 0; |
| 328 // Precedences of JS operators. | 227 // Precedences of JS operators. |
| 329 static final int EXPRESSION_PRECEDENCE = 1; | 228 static final int EXPRESSION_PRECEDENCE = 1; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 }; | 293 }; |
| 395 } | 294 } |
| 396 | 295 |
| 397 class JSBinaryOperatorPrecedence { | 296 class JSBinaryOperatorPrecedence { |
| 398 final int left; | 297 final int left; |
| 399 final int right; | 298 final int right; |
| 400 const JSBinaryOperatorPrecedence(this.left, this.right); | 299 const JSBinaryOperatorPrecedence(this.left, this.right); |
| 401 // All binary operators (excluding assignment) are left associative. | 300 // All binary operators (excluding assignment) are left associative. |
| 402 int get precedence() => left; | 301 int get precedence() => left; |
| 403 } | 302 } |
| OLD | NEW |