| Index: pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| diff --git a/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| index 65ca23aeb925804c417b44290dadc9c688b3fa19..2b614ae62450c4a6e1993d6ad5b0f8a08d672c67 100644
|
| --- a/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart
|
| @@ -61,27 +61,41 @@ class LogicalRewriter extends RecursiveTransformer
|
| node.body = visitStatement(node.body);
|
| }
|
|
|
| - /// Statement to be executed next by natural fallthrough. Although fallthrough
|
| - /// is not introduced in this phase, we need to reason about fallthrough when
|
| - /// evaluating the benefit of swapping the branches of an [If].
|
| - Statement fallthrough;
|
| + final FallthroughStack fallthrough = new FallthroughStack();
|
|
|
| @override
|
| void visitInnerFunction(FunctionDefinition node) {
|
| new LogicalRewriter().rewrite(node);
|
| }
|
|
|
| - Statement visitLabeledStatement(LabeledStatement node) {
|
| - Statement savedFallthrough = fallthrough;
|
| - fallthrough = node.next;
|
| - node.body = visitStatement(node.body);
|
| - fallthrough = savedFallthrough;
|
| - node.next = visitStatement(node.next);
|
| - return node;
|
| + /// True if the given statement is equivalent to its fallthrough semantics.
|
| + ///
|
| + /// This means it will ultimately translate to an empty statement.
|
| + bool isFallthrough(Statement node) {
|
| + return node is Break && isFallthroughBreak(node) ||
|
| + node is Continue && isFallthroughContinue(node) ||
|
| + node is Return && isFallthroughReturn(node);
|
| + }
|
| +
|
| + bool isFallthroughBreak(Break node) {
|
| + Statement target = fallthrough.target;
|
| + return node.target.binding.next == target ||
|
| + target is Break && target.target == node.target;
|
| + }
|
| +
|
| + bool isFallthroughContinue(Continue node) {
|
| + Statement target = fallthrough.target;
|
| + return node.target.binding == target ||
|
| + target is Continue && target.target == node.target;
|
| + }
|
| +
|
| + bool isFallthroughReturn(Return node) {
|
| + return isNull(node.value) && fallthrough.target == null;
|
| }
|
|
|
| - bool isFallthroughBreak(Statement node) {
|
| - return node is Break && node.target.binding.next == fallthrough;
|
| + bool isTerminator(Statement node) {
|
| + return (node is Jump || node is Return) && !isFallthrough(node) ||
|
| + (node is ExpressionStatement && node.next is Unreachable);
|
| }
|
|
|
| Statement visitIf(If node) {
|
| @@ -94,27 +108,64 @@ class LogicalRewriter extends RecursiveTransformer
|
| // In the tree language, empty statements do not exist yet, so we must check
|
| // if one branch contains a break that can be eliminated by fallthrough.
|
|
|
| - // Swap branches if then is a fallthrough break.
|
| - if (isFallthroughBreak(node.thenStatement)) {
|
| + // Rewrite each branch and keep track of which ones might fall through.
|
| + int usesBefore = fallthrough.useCount;
|
| + node.thenStatement = visitStatement(node.thenStatement);
|
| + int usesAfterThen = fallthrough.useCount;
|
| + node.elseStatement = visitStatement(node.elseStatement);
|
| + bool thenHasFallthrough = (fallthrough.useCount > usesBefore);
|
| + bool elseHasFallthrough = (fallthrough.useCount > usesAfterThen);
|
| +
|
| + // Determine which branch is most beneficial as 'then' branch.
|
| + const int THEN = 1;
|
| + const int NEITHER = 0;
|
| + const int ELSE = -1;
|
| + int bestThenBranch = NEITHER;
|
| + if (isFallthrough(node.thenStatement) &&
|
| + !isFallthrough(node.elseStatement)) {
|
| + // Put the empty statement in the 'else' branch.
|
| + // if (E) {} else {S} ==> if (!E) {S}
|
| + bestThenBranch = ELSE;
|
| + } else if (isFallthrough(node.elseStatement) &&
|
| + !isFallthrough(node.thenStatement)) {
|
| + // Keep the empty statement in the 'else' branch.
|
| + // if (E) {S} else {}
|
| + bestThenBranch = THEN;
|
| + } else if (thenHasFallthrough && !elseHasFallthrough) {
|
| + // Put abrupt termination in the 'then' branch to omit 'else'.
|
| + // if (E) {S1} else {S2; return v} ==> if (!E) {S2; return v}; S1
|
| + bestThenBranch = ELSE;
|
| + } else if (!thenHasFallthrough && elseHasFallthrough) {
|
| + // Keep abrupt termination in the 'then' branch to omit 'else'.
|
| + // if (E) {S1; return v}; S2
|
| + bestThenBranch = THEN;
|
| + } else if (isTerminator(node.elseStatement) &&
|
| + !isTerminator(node.thenStatement)) {
|
| + // Put early termination in the 'then' branch to reduce nesting depth.
|
| + // if (E) {S}; return v ==> if (!E) return v; S
|
| + bestThenBranch = ELSE;
|
| + } else if (isTerminator(node.thenStatement) &&
|
| + !isTerminator(node.elseStatement)) {
|
| + // Keep early termination in the 'then' branch to reduce nesting depth.
|
| + // if (E) {return v;} S
|
| + bestThenBranch = THEN;
|
| + }
|
| +
|
| + // Swap branches if 'else' is better as 'then'
|
| + if (bestThenBranch == ELSE) {
|
| node.condition = new Not(node.condition);
|
| Statement tmp = node.thenStatement;
|
| node.thenStatement = node.elseStatement;
|
| node.elseStatement = tmp;
|
| }
|
|
|
| - // Can the else part be eliminated?
|
| - // (Either due to the above swap or if the break was already there).
|
| - bool emptyElse = isFallthroughBreak(node.elseStatement);
|
| -
|
| - node.condition = makeCondition(node.condition, true, liftNots: !emptyElse);
|
| - node.thenStatement = visitStatement(node.thenStatement);
|
| - node.elseStatement = visitStatement(node.elseStatement);
|
| -
|
| - // If neither branch is empty, eliminate a negation in the condition
|
| + // If neither branch is better, eliminate a negation in the condition
|
| // if (!E) S1 else S2
|
| // ==>
|
| // if (E) S2 else S1
|
| - if (!emptyElse && node.condition is Not) {
|
| + node.condition = makeCondition(node.condition, true,
|
| + liftNots: bestThenBranch == NEITHER);
|
| + if (bestThenBranch == NEITHER && node.condition is Not) {
|
| node.condition = (node.condition as Not).operand;
|
| Statement tmp = node.thenStatement;
|
| node.thenStatement = node.elseStatement;
|
| @@ -124,13 +175,52 @@ class LogicalRewriter extends RecursiveTransformer
|
| return node;
|
| }
|
|
|
| + Statement visitLabeledStatement(LabeledStatement node) {
|
| + fallthrough.push(node.next);
|
| + node.body = visitStatement(node.body);
|
| + fallthrough.pop();
|
| + node.next = visitStatement(node.next);
|
| + return node;
|
| + }
|
| +
|
| + Statement visitWhileTrue(WhileTrue node) {
|
| + fallthrough.push(node);
|
| + node.body = visitStatement(node.body);
|
| + fallthrough.pop();
|
| + return node;
|
| + }
|
| +
|
| Statement visitWhileCondition(WhileCondition node) {
|
| + fallthrough.push(node);
|
| node.condition = makeCondition(node.condition, true, liftNots: false);
|
| node.body = visitStatement(node.body);
|
| + fallthrough.pop();
|
| node.next = visitStatement(node.next);
|
| return node;
|
| }
|
|
|
| + Statement visitBreak(Break node) {
|
| + if (isFallthroughBreak(node)) {
|
| + fallthrough.use();
|
| + }
|
| + return node;
|
| + }
|
| +
|
| + Statement visitContinue(Continue node) {
|
| + if (isFallthroughContinue(node)) {
|
| + fallthrough.use();
|
| + }
|
| + return node;
|
| + }
|
| +
|
| + Statement visitReturn(Return node) {
|
| + node.value = visitExpression(node.value);
|
| + if (isFallthroughReturn(node)) {
|
| + fallthrough.use();
|
| + }
|
| + return node;
|
| + }
|
| +
|
| Expression visitNot(Not node) {
|
| return toBoolean(makeCondition(node.operand, false, liftNots: false));
|
| }
|
| @@ -391,6 +481,10 @@ class LogicalRewriter extends RecursiveTransformer
|
| return polarity ? e : new Not(e);
|
| }
|
|
|
| + bool isNull(Expression e) {
|
| + return e is Constant && e.value.isNull;
|
| + }
|
| +
|
| bool isTrue(Expression e) {
|
| return e is Constant && e.value.isTrue;
|
| }
|
|
|