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 into [For] statements. | 10 /// Rewrites [WhileTrue] 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 pattern above needs no iteration since nested ifs have been | 53 /// Note that the last pattern above needs no iteration since nested ifs |
54 /// collapsed previously in the [StatementRewriter] phase. | 54 /// have been collapsed previously in the [StatementRewriter] phase. |
55 /// | |
56 /// | 55 /// |
57 /// PULL INTO UPDATE EXPRESSION: | 56 /// [WhileCondition] statements exist only after this phase. |
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. | |
76 class LoopRewriter extends RecursiveTransformer | 57 class LoopRewriter extends RecursiveTransformer |
77 implements Pass { | 58 implements Pass { |
78 String get passName => 'Loop rewriter'; | 59 String get passName => 'Loop rewriter'; |
79 | 60 |
80 Set<Label> usedContinueLabels = new Set<Label>(); | 61 Set<Label> usedContinueLabels = new Set<Label>(); |
81 | 62 |
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 | |
87 void rewrite(FunctionDefinition root) { | 63 void rewrite(FunctionDefinition root) { |
88 root.body = visitStatement(root.body); | 64 root.body = visitStatement(root.body); |
89 } | 65 } |
90 | 66 |
91 @override | 67 @override |
92 void visitInnerFunction(FunctionDefinition node) { | 68 void visitInnerFunction(FunctionDefinition node) { |
93 node.body = new LoopRewriter().visitStatement(node.body); | 69 node.body = new LoopRewriter().visitStatement(node.body); |
94 } | 70 } |
95 | 71 |
96 Statement visitContinue(Continue node) { | 72 Statement visitContinue(Continue node) { |
(...skipping 16 matching lines...) Expand all Loading... |
113 node.body = inner.body; | 89 node.body = inner.body; |
114 inner.body = node; | 90 inner.body = node; |
115 if (head == null) { | 91 if (head == null) { |
116 head = tail = inner; | 92 head = tail = inner; |
117 } else { | 93 } else { |
118 tail.body = inner; | 94 tail.body = inner; |
119 tail = inner; | 95 tail = inner; |
120 } | 96 } |
121 } | 97 } |
122 | 98 |
123 // Rewrite while(true) to for(; condition; updates). | 99 // Rewrite while(true) to while(condition). |
124 Statement loop = node; | 100 Statement loop = node; |
125 if (node.body is If) { | 101 if (node.body is If) { |
126 If body = node.body; | 102 If body = node.body; |
127 updateExpressions[node.label] = <Expression>[]; | |
128 body.thenStatement = visitStatement(body.thenStatement); | 103 body.thenStatement = visitStatement(body.thenStatement); |
129 bool thenHasContinue = usedContinueLabels.remove(node.label); | 104 bool thenHasContinue = usedContinueLabels.remove(node.label); |
130 body.elseStatement = visitStatement(body.elseStatement); | 105 body.elseStatement = visitStatement(body.elseStatement); |
131 bool elseHasContinue = usedContinueLabels.remove(node.label); | 106 bool elseHasContinue = usedContinueLabels.remove(node.label); |
132 if (thenHasContinue && !elseHasContinue) { | 107 if (thenHasContinue && !elseHasContinue) { |
133 node.label.binding = null; // Prepare to rebind the label. | 108 node.label.binding = null; // Prepare to rebind the label. |
134 loop = new For( | 109 loop = new WhileCondition( |
135 node.label, | 110 node.label, |
136 body.condition, | 111 body.condition, |
137 updateExpressions[node.label], | |
138 body.thenStatement, | 112 body.thenStatement, |
139 body.elseStatement); | 113 body.elseStatement); |
140 } else if (!thenHasContinue && elseHasContinue) { | 114 } else if (!thenHasContinue && elseHasContinue) { |
141 node.label.binding = null; | 115 node.label.binding = null; |
142 loop = new For( | 116 loop = new WhileCondition( |
143 node.label, | 117 node.label, |
144 new Not(body.condition), | 118 new Not(body.condition), |
145 updateExpressions[node.label], | |
146 body.elseStatement, | 119 body.elseStatement, |
147 body.thenStatement); | 120 body.thenStatement); |
148 } | 121 } |
149 } else if (node.body is LabeledStatement) { | 122 } else if (node.body is LabeledStatement) { |
150 // If the body is a labeled statement, its .next has already been visited. | 123 // If the body is a labeled statement, its .next has already been visited. |
151 LabeledStatement body = node.body; | 124 LabeledStatement body = node.body; |
152 body.body = visitStatement(body.body); | 125 body.body = visitStatement(body.body); |
153 usedContinueLabels.remove(node.label); | 126 usedContinueLabels.remove(node.label); |
154 } else { | 127 } else { |
155 node.body = visitStatement(node.body); | 128 node.body = visitStatement(node.body); |
156 usedContinueLabels.remove(node.label); | 129 usedContinueLabels.remove(node.label); |
157 } | 130 } |
158 | 131 |
159 if (head == null) return loop; | 132 if (head == null) return loop; |
160 tail.body = loop; | 133 tail.body = loop; |
161 return head; | 134 return head; |
162 } | 135 } |
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 index = statements.length; | |
183 while (index > 0 && statements[index - 1].expression is Assign) { | |
184 --index; | |
185 } | |
186 for (ExpressionStatement stmt in statements.skip(index)) { | |
187 updates.add(stmt.expression); | |
188 } | |
189 if (index > 0) { | |
190 statements[index - 1].next = next; | |
191 return statements.first; | |
192 } else { | |
193 return next; | |
194 } | |
195 } | |
196 } | |
197 // The expression statements could not be pulled into a loop update. | |
198 node.next = next; | |
199 return statements.first; | |
200 } | |
201 } | 136 } |
OLD | NEW |