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