Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library tree_ir.optimization.loop_rewriter; | 5 library tree_ir.optimization.loop_rewriter; |
| 6 | 6 |
| 7 import 'optimization.dart' show Pass; | 7 import 'optimization.dart' show Pass; |
| 8 import '../tree_ir_nodes.dart'; | 8 import '../tree_ir_nodes.dart'; |
| 9 | 9 |
| 10 /// Rewrites [WhileTrue] statements. | 10 /// Rewrites [WhileTrue] statements into [WhileCondition] statements. |
| 11 /// | 11 /// |
| 12 /// Before this phase, loops usually contain a lot of "exit code", that is, | 12 /// Before this phase, loops usually contain a lot of "exit code", that is, |
| 13 /// code that happens at a point where a [Continue] can no longer be reached, | 13 /// code that happens at a point where a [Continue] can no longer be reached, |
| 14 /// and is therefore not really part of the loop. | 14 /// and is therefore not really part of the loop. |
| 15 /// Exit code is moved down after the loop using the following rewrites rules: | 15 /// Exit code is moved down after the loop using the following rewrites rules: |
| 16 /// | 16 /// |
| 17 /// EXTRACT LABELED STATEMENT: | 17 /// EXTRACT LABELED STATEMENT: |
| 18 /// | 18 /// |
| 19 /// L: | 19 /// L: |
| 20 /// while (true) { | 20 /// while (true) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 43 /// } | 43 /// } |
| 44 /// ==> | 44 /// ==> |
| 45 /// L: | 45 /// L: |
| 46 /// while (E) { | 46 /// while (E) { |
| 47 /// S1 | 47 /// S1 |
| 48 /// }; | 48 /// }; |
| 49 /// S2 | 49 /// S2 |
| 50 /// | 50 /// |
| 51 /// A similar transformation is used when S2 occurs in the 'then' position. | 51 /// A similar transformation is used when S2 occurs in the 'then' position. |
| 52 /// | 52 /// |
| 53 /// Note that the last pattern above needs no iteration since nested ifs | 53 /// Note that the pattern above needs no iteration since nested ifs have been |
| 54 /// have been collapsed previously in the [StatementRewriter] phase. | 54 /// collapsed previously in the [StatementRewriter] phase. |
| 55 /// | |
| 55 /// | 56 /// |
| 56 /// [WhileCondition] statements exist only after this phase. | 57 /// PULL INTO UPDATE EXPRESSION: |
| 58 /// | |
| 59 /// Assignment expressions before the unique continue to a [whileCondition] are | |
| 60 /// pulled into the updates for the loop. | |
| 61 /// | |
| 62 /// L: | |
| 63 /// for (; condition; updates) { | |
| 64 /// S [ x = E; continue L ] | |
| 65 /// } | |
| 66 /// ==> | |
| 67 /// L: | |
| 68 /// for (; condition; updates, x = E) { | |
| 69 /// S [ continue L ] | |
| 70 /// } | |
| 71 /// | |
| 72 /// The decision to only pull in assignments is a heuristic to balance | |
| 73 /// readability and stack trace usability versus the modest code size | |
| 74 /// reduction one might get by aggressively moving expressions into the | |
| 75 /// updates. | |
| 57 class LoopRewriter extends RecursiveTransformer | 76 class LoopRewriter extends RecursiveTransformer |
| 58 implements Pass { | 77 implements Pass { |
| 59 String get passName => 'Loop rewriter'; | 78 String get passName => 'Loop rewriter'; |
| 60 | 79 |
| 61 Set<Label> usedContinueLabels = new Set<Label>(); | 80 Set<Label> usedContinueLabels = new Set<Label>(); |
| 62 | 81 |
| 82 /// Maps loop labels to a list, if that loop can accept update expressions. | |
| 83 /// The list will then be populated while traversing the body of that loop. | |
| 84 /// If a loop is not in the map, update expressions cannot be hoisted there. | |
| 85 Map<Label, List<Expression>> updateExpressions = <Label, List<Expression>>{}; | |
| 86 | |
| 63 void rewrite(FunctionDefinition root) { | 87 void rewrite(FunctionDefinition root) { |
| 64 root.body = visitStatement(root.body); | 88 root.body = visitStatement(root.body); |
| 65 } | 89 } |
| 66 | 90 |
| 67 @override | 91 @override |
| 68 void visitInnerFunction(FunctionDefinition node) { | 92 void visitInnerFunction(FunctionDefinition node) { |
| 69 node.body = new LoopRewriter().visitStatement(node.body); | 93 node.body = new LoopRewriter().visitStatement(node.body); |
| 70 } | 94 } |
| 71 | 95 |
| 72 Statement visitContinue(Continue node) { | 96 Statement visitContinue(Continue node) { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 93 } else { | 117 } else { |
| 94 tail.body = inner; | 118 tail.body = inner; |
| 95 tail = inner; | 119 tail = inner; |
| 96 } | 120 } |
| 97 } | 121 } |
| 98 | 122 |
| 99 // Rewrite while(true) to while(condition). | 123 // Rewrite while(true) to while(condition). |
| 100 Statement loop = node; | 124 Statement loop = node; |
| 101 if (node.body is If) { | 125 if (node.body is If) { |
| 102 If body = node.body; | 126 If body = node.body; |
| 127 updateExpressions[node.label] = <Expression>[]; | |
| 103 body.thenStatement = visitStatement(body.thenStatement); | 128 body.thenStatement = visitStatement(body.thenStatement); |
| 104 bool thenHasContinue = usedContinueLabels.remove(node.label); | 129 bool thenHasContinue = usedContinueLabels.remove(node.label); |
| 105 body.elseStatement = visitStatement(body.elseStatement); | 130 body.elseStatement = visitStatement(body.elseStatement); |
| 106 bool elseHasContinue = usedContinueLabels.remove(node.label); | 131 bool elseHasContinue = usedContinueLabels.remove(node.label); |
| 107 if (thenHasContinue && !elseHasContinue) { | 132 if (thenHasContinue && !elseHasContinue) { |
| 108 node.label.binding = null; // Prepare to rebind the label. | 133 node.label.binding = null; // Prepare to rebind the label. |
| 109 loop = new WhileCondition( | 134 loop = new WhileCondition( |
| 110 node.label, | 135 node.label, |
| 111 body.condition, | 136 body.condition, |
| 137 updateExpressions[node.label], | |
| 112 body.thenStatement, | 138 body.thenStatement, |
| 113 body.elseStatement); | 139 body.elseStatement); |
| 114 } else if (!thenHasContinue && elseHasContinue) { | 140 } else if (!thenHasContinue && elseHasContinue) { |
| 115 node.label.binding = null; | 141 node.label.binding = null; |
| 116 loop = new WhileCondition( | 142 loop = new WhileCondition( |
| 117 node.label, | 143 node.label, |
| 118 new Not(body.condition), | 144 new Not(body.condition), |
| 145 updateExpressions[node.label], | |
| 119 body.elseStatement, | 146 body.elseStatement, |
| 120 body.thenStatement); | 147 body.thenStatement); |
| 121 } | 148 } |
| 122 } else if (node.body is LabeledStatement) { | 149 } else if (node.body is LabeledStatement) { |
| 123 // If the body is a labeled statement, its .next has already been visited. | 150 // If the body is a labeled statement, its .next has already been visited. |
| 124 LabeledStatement body = node.body; | 151 LabeledStatement body = node.body; |
| 125 body.body = visitStatement(body.body); | 152 body.body = visitStatement(body.body); |
| 126 usedContinueLabels.remove(node.label); | 153 usedContinueLabels.remove(node.label); |
| 127 } else { | 154 } else { |
| 128 node.body = visitStatement(node.body); | 155 node.body = visitStatement(node.body); |
| 129 usedContinueLabels.remove(node.label); | 156 usedContinueLabels.remove(node.label); |
| 130 } | 157 } |
| 131 | 158 |
| 132 if (head == null) return loop; | 159 if (head == null) return loop; |
| 133 tail.body = loop; | 160 tail.body = loop; |
| 134 return head; | 161 return head; |
| 135 } | 162 } |
| 163 | |
| 164 Statement visitExpressionStatement(ExpressionStatement node) { | |
| 165 if (updateExpressions.isEmpty) { | |
| 166 // Avoid allocating a list if there is no loop. | |
| 167 return super.visitExpressionStatement(node); | |
| 168 } | |
| 169 List<ExpressionStatement> statements = <ExpressionStatement>[]; | |
| 170 while (node.next is ExpressionStatement) { | |
| 171 statements.add(node); | |
| 172 node = node.next; | |
| 173 } | |
| 174 statements.add(node); | |
| 175 Statement next = visitStatement(node.next); | |
| 176 if (next is Continue && next.target.useCount == 1) { | |
| 177 List<Expression> updates = updateExpressions[next.target]; | |
| 178 if (updates != null) { | |
| 179 // Pull expressions before the continue into the for loop update. | |
| 180 // As a heuristic, we only pull in assignment expressions. | |
| 181 // Determine the index of the first assignment to pull in. | |
| 182 int assignIndex = 0; | |
|
Kevin Millikin (Google)
2015/08/13 11:32:47
Seems kind of tricky that this might be one past t
asgerf
2015/08/13 12:06:42
Yeah that looks better. Done.
| |
| 183 for (int i = 0; i < statements.length; i++) { | |
| 184 if (statements[i].expression is! Assign) { | |
| 185 assignIndex = i + 1; | |
| 186 } | |
| 187 } | |
| 188 for (ExpressionStatement stmt in statements.skip(assignIndex)) { | |
| 189 updates.add(stmt.expression); | |
| 190 } | |
| 191 if (assignIndex > 0) { | |
| 192 statements[assignIndex - 1].next = next; | |
| 193 return statements.first; | |
| 194 } else { | |
| 195 return next; | |
| 196 } | |
| 197 } | |
| 198 } | |
| 199 // The expression statements could not be pulled into a loop update. | |
| 200 node.next = next; | |
| 201 return statements.first; | |
| 202 } | |
| 136 } | 203 } |
| OLD | NEW |