Index: frog/leg/ssa/codegen_helpers.dart |
=================================================================== |
--- frog/leg/ssa/codegen_helpers.dart (revision 5925) |
+++ frog/leg/ssa/codegen_helpers.dart (working copy) |
@@ -1,353 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/** |
- * Instead of emitting each SSA instruction with a temporary variable |
- * mark instructions that can be emitted at their use-site. |
- * For example, in: |
- * t0 = 4; |
- * t1 = 3; |
- * t2 = add(t0, t1); |
- * t0 and t1 would be marked and the resulting code would then be: |
- * t2 = add(4, 3); |
- */ |
-class SsaInstructionMerger extends HBaseVisitor { |
- List<HInstruction> expectedInputs; |
- Set<HInstruction> generateAtUseSite; |
- |
- SsaInstructionMerger(this.generateAtUseSite); |
- |
- void visitGraph(HGraph graph) { |
- visitDominatorTree(graph); |
- } |
- |
- bool usedOnlyByPhis(instruction) { |
- for (HInstruction user in instruction.usedBy) { |
- if (user is !HPhi) return false; |
- } |
- return true; |
- } |
- |
- void visitInstruction(HInstruction instruction) { |
- // A code motion invariant instruction is dealt before visiting it. |
- assert(!instruction.isCodeMotionInvariant()); |
- for (HInstruction input in instruction.inputs) { |
- if (!generateAtUseSite.contains(input) |
- && !input.isCodeMotionInvariant() |
- && input.usedBy.length == 1) { |
- expectedInputs.add(input); |
- } |
- } |
- } |
- |
- // The codegen might use the input multiple times, so it must not be |
- // set generate at use site. |
- void visitIs(HIs instruction) {} |
- |
- // A check method must not have its input generate at use site, |
- // because it's using it multiple times. |
- void visitCheck(HCheck instruction) {} |
- |
- // A type guard should not generate its input at use site, otherwise |
- // they would not be alive. |
- void visitTypeGuard(HTypeGuard instruction) {} |
- |
- void tryGenerateAtUseSite(HInstruction instruction) { |
- // A type guard should never be generate at use site, otherwise we |
- // cannot bailout. |
- if (instruction is HTypeGuard) return; |
- |
- // A check should never be generate at use site, otherwise we |
- // cannot throw. |
- if (instruction is HCheck) return; |
- |
- generateAtUseSite.add(instruction); |
- } |
- |
- bool isBlockSinglePredecessor(HBasicBlock block) { |
- return block.successors.length === 1 |
- && block.successors[0].predecessors.length === 1; |
- } |
- |
- void visitBasicBlock(HBasicBlock block) { |
- // Compensate from not merging blocks: if the block is the |
- // single predecessor of its single successor, let the successor |
- // visit it. |
- if (isBlockSinglePredecessor(block)) return; |
- |
- tryMergingExpressions(block); |
- } |
- |
- void tryMergingExpressions(HBasicBlock block) { |
- // Visit each instruction of the basic block in last-to-first order. |
- // Keep a list of expected inputs of the current "expression" being |
- // merged. If instructions occur in the expected order, they are |
- // included in the expression. |
- |
- // The expectedInputs list holds non-trivial instructions that may |
- // be generated at their use site, if they occur in the correct order. |
- if (expectedInputs === null) expectedInputs = new List<HInstruction>(); |
- |
- // Pop instructions from expectedInputs until instruction is found. |
- // Return true if it is found, or false if not. |
- bool findInInputs(HInstruction instruction) { |
- while (!expectedInputs.isEmpty()) { |
- HInstruction nextInput = expectedInputs.removeLast(); |
- assert(!generateAtUseSite.contains(nextInput)); |
- assert(nextInput.usedBy.length == 1); |
- if (nextInput == instruction) { |
- return true; |
- } |
- } |
- return false; |
- } |
- |
- block.last.accept(this); |
- for (HInstruction instruction = block.last.previous; |
- instruction !== null; |
- instruction = instruction.previous) { |
- if (generateAtUseSite.contains(instruction)) { |
- continue; |
- } |
- if (instruction.isCodeMotionInvariant()) { |
- generateAtUseSite.add(instruction); |
- continue; |
- } |
- bool foundInInputs = false; |
- // See if the current instruction is the next non-trivial |
- // expected input. If not, drop the expectedInputs and |
- // start over. |
- if (findInInputs(instruction)) { |
- foundInInputs = true; |
- tryGenerateAtUseSite(instruction); |
- } else { |
- assert(expectedInputs.isEmpty()); |
- } |
- if (foundInInputs || usedOnlyByPhis(instruction)) { |
- // Try merging all non-trivial inputs. |
- instruction.accept(this); |
- } |
- } |
- |
- if (block.predecessors.length === 1 |
- && isBlockSinglePredecessor(block.predecessors[0])) { |
- tryMergingExpressions(block.predecessors[0]); |
- } else { |
- expectedInputs = null; |
- } |
- } |
-} |
- |
-/** |
- * Detect control flow arising from short-circuit logical operators, and |
- * prepare the program to be generated using these operators instead of |
- * nested ifs and boolean variables. |
- */ |
-class SsaConditionMerger extends HGraphVisitor { |
- Set<HInstruction> generateAtUseSite; |
- Map<HPhi, String> logicalOperations; |
- |
- SsaConditionMerger(this.generateAtUseSite, this.logicalOperations); |
- |
- void visitGraph(HGraph graph) { |
- visitDominatorTree(graph); |
- } |
- |
- /** |
- * Returns true if the given instruction is an expression that uses up all |
- * instructions up to the given [limit]. |
- * |
- * That is, all instructions starting after the [limit] block (at the branch |
- * leading to the [instruction]) down to the given [instruction] can be |
- * generated at use-site. |
- */ |
- bool isExpression(HInstruction instruction, HBasicBlock limit) { |
- if (instruction is HPhi && !logicalOperations.containsKey(instruction)) { |
- return false; |
- } |
- while (instruction.previous != null) { |
- instruction = instruction.previous; |
- if (!generateAtUseSite.contains(instruction)) { |
- return false; |
- } |
- } |
- HBasicBlock block = instruction.block; |
- if (!block.phis.isEmpty()) return false; |
- if (instruction is HPhi && logicalOperations.containsKey(instruction)) { |
- return isExpression(instruction.inputs[0], limit); |
- } |
- return block.predecessors.length == 1 && block.predecessors[0] == limit; |
- } |
- |
- void replaceWithLogicalOperator(HPhi phi, String type) { |
- if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi); |
- logicalOperations[phi] = type; |
- } |
- |
- bool canGenerateAtUseSite(HPhi phi) { |
- if (phi.usedBy.length != 1) return false; |
- assert(phi.next == null); |
- HInstruction use = phi.usedBy[0]; |
- |
- HInstruction current = phi.block.first; |
- while (current != use) { |
- if (!generateAtUseSite.contains(current)) return false; |
- if (current.next != null) { |
- current = current.next; |
- } else if (current is HPhi) { |
- current = current.block.first; |
- } else { |
- assert(current is HControlFlow); |
- if (current is !HGoto) return false; |
- HBasicBlock nextBlock = current.block.successors[0]; |
- if (!nextBlock.phis.isEmpty()) { |
- current = nextBlock.phis.first; |
- } else { |
- current = nextBlock.first; |
- } |
- } |
- } |
- return true; |
- } |
- |
- void detectLogicControlFlow(HPhi phi) { |
- // Check for the most common pattern for a short-circuit logic operation: |
- // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1) |
- // |\ |
- // | B1 b1 = ...; goto B2 |
- // |/ |
- // B2 b2 = phi(b0,b1); if(b2) ... |
- // TODO(lrn): Also recognize ?:-flow? |
- |
- if (phi.inputs.length != 2) return; |
- HInstruction first = phi.inputs[0]; |
- HBasicBlock firstBlock = first.block; |
- HInstruction second = phi.inputs[1]; |
- HBasicBlock secondBlock = second.block; |
- // Check second input of phi being an expression followed by a goto. |
- if (second.usedBy.length != 1) return; |
- HInstruction secondNext = |
- (second is HPhi) ? secondBlock.first : second.next; |
- if (secondNext != secondBlock.last) return; |
- if (secondBlock.last is !HGoto) return; |
- if (secondBlock.successors[0] != phi.block) return; |
- if (!isExpression(second, firstBlock)) return; |
- // Check first input of phi being followed by a (possibly negated) |
- // conditional branch based on the same value. |
- if (firstBlock != phi.block.dominator) return; |
- if (firstBlock.last is !HConditionalBranch) return; |
- HConditionalBranch firstBranch = firstBlock.last; |
- // Must be used both for value and for control to avoid the second branch. |
- if (first.usedBy.length != 2) return; |
- if (firstBlock.successors[1] != phi.block) return; |
- HInstruction firstNext = (first is HPhi) ? firstBlock.first : first.next; |
- if (firstNext == firstBranch && |
- firstBranch.condition == first) { |
- replaceWithLogicalOperator(phi, "&&"); |
- } else if (firstNext is HNot && |
- firstNext.inputs[0] == first && |
- generateAtUseSite.contains(firstNext) && |
- firstNext.next == firstBlock.last && |
- firstBranch.condition == firstNext) { |
- replaceWithLogicalOperator(phi, "||"); |
- } else { |
- return; |
- } |
- // Detected as logic control flow. Mark the corresponding |
- // inputs as generated at use site. These will now be generated |
- // as part of an expression. |
- generateAtUseSite.add(first); |
- generateAtUseSite.add(firstBlock.last); |
- generateAtUseSite.add(second); |
- generateAtUseSite.add(secondBlock.last); |
- } |
- |
- void visitBasicBlock(HBasicBlock block) { |
- if (!block.phis.isEmpty() && |
- block.phis.first == block.phis.last) { |
- detectLogicControlFlow(block.phis.first); |
- } |
- } |
-} |
- |
-// Precedence information for JavaScript operators. |
-class JSPrecedence { |
- // Used as precedence for something that's not even an expression. |
- static final int STATEMENT_PRECEDENCE = 0; |
- // Precedences of JS operators. |
- static final int EXPRESSION_PRECEDENCE = 1; |
- static final int ASSIGNMENT_PRECEDENCE = 2; |
- static final int CONDITIONAL_PRECEDENCE = 3; |
- static final int LOGICAL_OR_PRECEDENCE = 4; |
- static final int LOGICAL_AND_PRECEDENCE = 5; |
- static final int BITWISE_OR_PRECEDENCE = 6; |
- static final int BITWISE_XOR_PRECEDENCE = 7; |
- static final int BITWISE_AND_PRECEDENCE = 8; |
- static final int EQUALITY_PRECEDENCE = 9; |
- static final int RELATIONAL_PRECEDENCE = 10; |
- static final int SHIFT_PRECEDENCE = 11; |
- static final int ADDITIVE_PRECEDENCE = 12; |
- static final int MULTIPLICATIVE_PRECEDENCE = 13; |
- static final int PREFIX_PRECEDENCE = 14; |
- static final int POSTFIX_PRECEDENCE = 15; |
- static final int CALL_PRECEDENCE = 16; |
- // We never use "new MemberExpression" without arguments, so we can |
- // combine CallExpression and MemberExpression without ambiguity. |
- static final int MEMBER_PRECEDENCE = CALL_PRECEDENCE; |
- static final int PRIMARY_PRECEDENCE = 17; |
- |
- // The operators that an occur in HBinaryOp. |
- static final Map<String, JSBinaryOperatorPrecedence> binary = const { |
- "||" : const JSBinaryOperatorPrecedence(LOGICAL_OR_PRECEDENCE, |
- LOGICAL_AND_PRECEDENCE), |
- "&&" : const JSBinaryOperatorPrecedence(LOGICAL_AND_PRECEDENCE, |
- BITWISE_OR_PRECEDENCE), |
- "|" : const JSBinaryOperatorPrecedence(BITWISE_OR_PRECEDENCE, |
- BITWISE_XOR_PRECEDENCE), |
- "^" : const JSBinaryOperatorPrecedence(BITWISE_XOR_PRECEDENCE, |
- BITWISE_AND_PRECEDENCE), |
- "&" : const JSBinaryOperatorPrecedence(BITWISE_AND_PRECEDENCE, |
- EQUALITY_PRECEDENCE), |
- "==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, |
- RELATIONAL_PRECEDENCE), |
- "!=" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, |
- RELATIONAL_PRECEDENCE), |
- "===" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, |
- RELATIONAL_PRECEDENCE), |
- "!==" : const JSBinaryOperatorPrecedence(EQUALITY_PRECEDENCE, |
- RELATIONAL_PRECEDENCE), |
- "<" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, |
- SHIFT_PRECEDENCE), |
- ">" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, |
- SHIFT_PRECEDENCE), |
- "<=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, |
- SHIFT_PRECEDENCE), |
- ">=" : const JSBinaryOperatorPrecedence(RELATIONAL_PRECEDENCE, |
- SHIFT_PRECEDENCE), |
- "<<" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, |
- ADDITIVE_PRECEDENCE), |
- ">>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, |
- ADDITIVE_PRECEDENCE), |
- ">>>" : const JSBinaryOperatorPrecedence(SHIFT_PRECEDENCE, |
- ADDITIVE_PRECEDENCE), |
- "+" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE, |
- MULTIPLICATIVE_PRECEDENCE), |
- "-" : const JSBinaryOperatorPrecedence(ADDITIVE_PRECEDENCE, |
- MULTIPLICATIVE_PRECEDENCE), |
- "*" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, |
- PREFIX_PRECEDENCE), |
- "/" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, |
- PREFIX_PRECEDENCE), |
- "%" : const JSBinaryOperatorPrecedence(MULTIPLICATIVE_PRECEDENCE, |
- PREFIX_PRECEDENCE), |
- }; |
-} |
- |
-class JSBinaryOperatorPrecedence { |
- final int left; |
- final int right; |
- const JSBinaryOperatorPrecedence(this.left, this.right); |
- // All binary operators (excluding assignment) are left associative. |
- int get precedence() => left; |
-} |