Index: pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart |
diff --git a/pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart |
index 8287bacdb751ff7c54394e5258fdfe5809522c4b..14069e87ac0e6a9b3bebc134b43a49bbf0ab14c2 100644 |
--- a/pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart |
+++ b/pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart |
@@ -7,7 +7,7 @@ library tree_ir.optimization.loop_rewriter; |
import 'optimization.dart' show Pass; |
import '../tree_ir_nodes.dart'; |
-/// Rewrites [WhileTrue] statements. |
+/// Rewrites [WhileTrue] statements into [For] statements. |
/// |
/// Before this phase, loops usually contain a lot of "exit code", that is, |
/// code that happens at a point where a [Continue] can no longer be reached, |
@@ -50,16 +50,40 @@ import '../tree_ir_nodes.dart'; |
/// |
/// A similar transformation is used when S2 occurs in the 'then' position. |
/// |
-/// Note that the last pattern above needs no iteration since nested ifs |
-/// have been collapsed previously in the [StatementRewriter] phase. |
+/// Note that the pattern above needs no iteration since nested ifs have been |
+/// collapsed previously in the [StatementRewriter] phase. |
+/// |
/// |
-/// [WhileCondition] statements exist only after this phase. |
+/// PULL INTO UPDATE EXPRESSION: |
+/// |
+/// Assignment expressions before the unique continue to a [whileCondition] are |
+/// pulled into the updates for the loop. |
+/// |
+/// L: |
+/// for (; condition; updates) { |
+/// S [ x = E; continue L ] |
+/// } |
+/// ==> |
+/// L: |
+/// for (; condition; updates, x = E) { |
+/// S [ continue L ] |
+/// } |
+/// |
+/// The decision to only pull in assignments is a heuristic to balance |
+/// readability and stack trace usability versus the modest code size |
+/// reduction one might get by aggressively moving expressions into the |
+/// updates. |
class LoopRewriter extends RecursiveTransformer |
implements Pass { |
String get passName => 'Loop rewriter'; |
Set<Label> usedContinueLabels = new Set<Label>(); |
+ /// Maps loop labels to a list, if that loop can accept update expressions. |
+ /// The list will then be populated while traversing the body of that loop. |
+ /// If a loop is not in the map, update expressions cannot be hoisted there. |
+ Map<Label, List<Expression>> updateExpressions = <Label, List<Expression>>{}; |
+ |
void rewrite(FunctionDefinition root) { |
root.body = visitStatement(root.body); |
} |
@@ -96,26 +120,29 @@ class LoopRewriter extends RecursiveTransformer |
} |
} |
- // Rewrite while(true) to while(condition). |
+ // Rewrite while(true) to for(; condition; updates). |
Statement loop = node; |
if (node.body is If) { |
If body = node.body; |
+ updateExpressions[node.label] = <Expression>[]; |
body.thenStatement = visitStatement(body.thenStatement); |
bool thenHasContinue = usedContinueLabels.remove(node.label); |
body.elseStatement = visitStatement(body.elseStatement); |
bool elseHasContinue = usedContinueLabels.remove(node.label); |
if (thenHasContinue && !elseHasContinue) { |
node.label.binding = null; // Prepare to rebind the label. |
- loop = new WhileCondition( |
+ loop = new For( |
node.label, |
body.condition, |
+ updateExpressions[node.label], |
body.thenStatement, |
body.elseStatement); |
} else if (!thenHasContinue && elseHasContinue) { |
node.label.binding = null; |
- loop = new WhileCondition( |
+ loop = new For( |
node.label, |
new Not(body.condition), |
+ updateExpressions[node.label], |
body.elseStatement, |
body.thenStatement); |
} |
@@ -133,4 +160,42 @@ class LoopRewriter extends RecursiveTransformer |
tail.body = loop; |
return head; |
} |
+ |
+ Statement visitExpressionStatement(ExpressionStatement node) { |
+ if (updateExpressions.isEmpty) { |
+ // Avoid allocating a list if there is no loop. |
+ return super.visitExpressionStatement(node); |
+ } |
+ List<ExpressionStatement> statements = <ExpressionStatement>[]; |
+ while (node.next is ExpressionStatement) { |
+ statements.add(node); |
+ node = node.next; |
+ } |
+ statements.add(node); |
+ Statement next = visitStatement(node.next); |
+ if (next is Continue && next.target.useCount == 1) { |
+ List<Expression> updates = updateExpressions[next.target]; |
+ if (updates != null) { |
+ // Pull expressions before the continue into the for loop update. |
+ // As a heuristic, we only pull in assignment expressions. |
+ // Determine the index of the first assignment to pull in. |
+ int index = statements.length; |
+ while (index > 0 && statements[index - 1].expression is Assign) { |
+ --index; |
+ } |
+ for (ExpressionStatement stmt in statements.skip(index)) { |
+ updates.add(stmt.expression); |
+ } |
+ if (index > 0) { |
+ statements[index - 1].next = next; |
+ return statements.first; |
+ } else { |
+ return next; |
+ } |
+ } |
+ } |
+ // The expression statements could not be pulled into a loop update. |
+ node.next = next; |
+ return statements.first; |
+ } |
} |