Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart

Issue 1088493002: Assignment expressions in tree IR. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 part of tree_ir.optimization; 5 part of tree_ir.optimization;
6 6
7 /** 7 /**
8 * Performs the following transformations on the tree: 8 * Performs the following transformations on the tree:
9 * - Assignment propagation 9 * - Assignment inlining
10 * - Assignment expression propagation
10 * - If-to-conditional conversion 11 * - If-to-conditional conversion
11 * - Flatten nested ifs 12 * - Flatten nested ifs
12 * - Break inlining 13 * - Break inlining
13 * - Redirect breaks 14 * - Redirect breaks
14 * 15 *
15 * The above transformations all eliminate statements from the tree, and may 16 * The above transformations all eliminate statements from the tree, and may
16 * introduce redexes of each other. 17 * introduce redexes of each other.
17 * 18 *
18 * 19 *
19 * ASSIGNMENT PROPAGATION: 20 * ASSIGNMENT INLINING:
20 * Single-use definitions are propagated to their use site when possible. 21 * Single-use definitions are inlined at their use site when possible.
21 * For example: 22 * For example:
22 * 23 *
23 * { v0 = foo(); return v0; } 24 * { v0 = foo(); return v0; }
24 * ==> 25 * ==>
25 * return foo() 26 * return foo()
26 * 27 *
27 * After translating out of CPS, all intermediate values are bound by [Assign]. 28 * After translating out of CPS, all intermediate values are bound by [Assign].
28 * This transformation propagates such definitions to their uses when it is 29 * This transformation propagates such definitions to their uses when it is
29 * safe and profitable. Bindings are processed "on demand" when their uses are 30 * safe and profitable. Bindings are processed "on demand" when their uses are
30 * seen, but are only processed once to keep this transformation linear in 31 * seen, but are only processed once to keep this transformation linear in
31 * the size of the tree. 32 * the size of the tree.
32 * 33 *
33 * The transformation builds an environment containing [Assign] bindings that 34 * The transformation builds an environment containing [Assign] bindings that
34 * are in scope. These bindings have yet-untranslated definitions. When a use 35 * are in scope. These bindings have yet-untranslated definitions. When a use
35 * is encountered the transformation determines if it is safe and profitable 36 * is encountered the transformation determines if it is safe and profitable
36 * to propagate the definition to its use. If so, it is removed from the 37 * to propagate the definition to its use. If so, it is removed from the
37 * environment and the definition is recursively processed (in the 38 * environment and the definition is recursively processed (in the
38 * new environment at the use site) before being propagated. 39 * new environment at the use site) before being propagated.
39 * 40 *
40 * See [visitVariableUse] for the implementation of the heuristic for 41 * See [visitVariableUse] for the implementation of the heuristic for
41 * propagating a definition. 42 * propagating a definition.
42 * 43 *
43 * 44 *
45 * ASSIGNMENT EXPRESSION PROPAGATION:
46 * Definitions with multiple uses are propagated to their first use site
47 * when possible. For example:
48 *
49 * { v0 = foo(); bar(v0); return v0; }
50 * ==>
51 * { bar(v0 = foo()); return v0; }
52 *
53 * Note that the [RestoreInitializers] phase will later undo this rewrite
54 * in cases where it prevents an assignment from being pulled into an
55 * initializer.
56 *
57 *
44 * IF-TO-CONDITIONAL CONVERSION: 58 * IF-TO-CONDITIONAL CONVERSION:
45 * If-statement are converted to conditional expressions when possible. 59 * If-statement are converted to conditional expressions when possible.
46 * For example: 60 * For example:
47 * 61 *
48 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L } 62 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L }
49 * ==> 63 * ==>
50 * { v1 = v0 ? foo() : bar(); break L } 64 * { v1 = v0 ? foo() : bar(); break L }
51 * 65 *
52 * This can lead to inlining of L, which in turn can lead to further propagation 66 * This can lead to inlining of L, which in turn can lead to further propagation
53 * of the variable v1. 67 * of the variable v1.
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 * separated two ifs. 106 * separated two ifs.
93 */ 107 */
94 class StatementRewriter extends Transformer implements Pass { 108 class StatementRewriter extends Transformer implements Pass {
95 String get passName => 'Statement rewriter'; 109 String get passName => 'Statement rewriter';
96 110
97 @override 111 @override
98 void rewrite(RootNode node) { 112 void rewrite(RootNode node) {
99 node.replaceEachBody(visitStatement); 113 node.replaceEachBody(visitStatement);
100 } 114 }
101 115
102 // The binding environment. The rightmost element of the list is the nearest 116 /// True if targeting Dart.
103 // available enclosing binding. 117 final bool isDartMode;
104 List<Assign> environment = <Assign>[]; 118
119 /// The most recently evaluated impure expressions, with the most recent
120 /// expression being last.
121 ///
122 /// Most importantly, this contains [Assign] expressions that we attempt to
123 /// inline at their use site. It also contains other impure expressions that
124 /// we can propagate to a variable use if they are known to return the value
125 /// of that variable.
126 ///
127 /// Except for [Conditional]s, expressions in the environment have
128 /// not been processed, and all their subexpressions must therefore be
129 /// variables uses.
130 List<Expression> environment = <Expression>[];
105 131
106 /// Binding environment for variables that are assigned to effectively 132 /// Binding environment for variables that are assigned to effectively
107 /// constant expressions (see [isEffectivelyConstant]). 133 /// constant expressions (see [isEffectivelyConstant]).
108 final Map<Variable, Expression> constantEnvironment; 134 final Map<Variable, Expression> constantEnvironment;
109 135
110 /// Substitution map for labels. Any break to a label L should be substituted 136 /// Substitution map for labels. Any break to a label L should be substituted
111 /// for a break to L' if L maps to L'. 137 /// for a break to L' if L maps to L'.
112 Map<Label, Jump> labelRedirects = <Label, Jump>{}; 138 Map<Label, Jump> labelRedirects = <Label, Jump>{};
113 139
140 /// Number of uses seen so far. Used to detect the first use of a variable
141 /// (since we do backwards traversal, the first use is the last one seen).
142 Map<Variable, int> seenUses = <Variable, int>{};
143
114 /// Rewriter for methods. 144 /// Rewriter for methods.
115 StatementRewriter() : constantEnvironment = <Variable, Expression>{}; 145 StatementRewriter({this.isDartMode})
146 : constantEnvironment = <Variable, Expression>{} {
147 assert(isDartMode != null);
148 }
116 149
117 /// Rewriter for nested functions. 150 /// Rewriter for nested functions.
118 StatementRewriter.nested(StatementRewriter parent) 151 StatementRewriter.nested(StatementRewriter parent)
119 : constantEnvironment = parent.constantEnvironment; 152 : constantEnvironment = parent.constantEnvironment,
153 seenUses = parent.seenUses,
154 isDartMode = parent.isDartMode;
120 155
121 /// A set of labels that can be safely inlined at their use. 156 /// A set of labels that can be safely inlined at their use.
122 /// 157 ///
123 /// The successor statements for labeled statements that have only one break 158 /// The successor statements for labeled statements that have only one break
124 /// from them are normally rewritten inline at the site of the break. This 159 /// from them are normally rewritten inline at the site of the break. This
125 /// is not safe if the code would be moved inside the scope of an exception 160 /// is not safe if the code would be moved inside the scope of an exception
126 /// handler (i.e., if the code would be moved into a try from outside it). 161 /// handler (i.e., if the code would be moved into a try from outside it).
127 Set<Label> safeForInlining = new Set<Label>(); 162 Set<Label> safeForInlining = new Set<Label>();
128 163
129 /// Returns the redirect target of [jump] or [jump] itself if it should not 164 /// Returns the redirect target of [jump] or [jump] itself if it should not
130 /// be redirected. 165 /// be redirected.
131 Jump redirect(Jump jump) { 166 Jump redirect(Jump jump) {
132 Jump newJump = labelRedirects[jump.target]; 167 Jump newJump = labelRedirects[jump.target];
133 return newJump != null ? newJump : jump; 168 return newJump != null ? newJump : jump;
134 } 169 }
135 170
136 void inEmptyEnvironment(void action()) { 171 void inEmptyEnvironment(void action()) {
137 List<Assign> oldEnvironment = environment; 172 List oldEnvironment = environment;
138 environment = <Assign>[]; 173 environment = <Expression>[];
139 action(); 174 action();
140 assert(environment.isEmpty); 175 assert(environment.isEmpty);
141 environment = oldEnvironment; 176 environment = oldEnvironment;
142 } 177 }
143 178
144 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this); 179 /// Left-hand side of the given assignment, or `null` if not an assignment.
180 Variable getLeftHand(Expression e) {
181 return e is Assign ? e.variable : null;
182 }
183
184 /// If the given expression always returns the value of one of its
185 /// subexpressions, returns that subexpression, otherwise `null`.
186 Expression getValueSubexpression(Expression e) {
187 if (isDartMode &&
188 e is InvokeMethod &&
189 (e.selector.isSetter || e.selector.isIndexSet)) {
190 return e.arguments.last;
191 }
192 if (e is SetField) return e.value;
193 return null;
194 }
195
196 /// If the given expression always returns the value of one of its
197 /// subexpressions, and that subexpression is a variable use, returns that
198 /// variable. Otherwise `null`.
199 Variable getRightHand(Expression e) {
200 Expression value = getValueSubexpression(e);
201 return value is VariableUse ? value.variable : null;
202 }
145 203
146 @override 204 @override
147 Expression visitVariableUse(VariableUse node) { 205 Expression visitVariableUse(VariableUse node) {
206 // Count of number of uses seen so far.
207 seenUses[node.variable] = 1 + seenUses.putIfAbsent(node.variable, () => 0);
208
209 // We traverse the tree right-to-left, so when we have seen all uses,
210 // it means we are looking at the first use.
211 assert(seenUses[node.variable] <= node.variable.readCount);
212 bool isFirstUse = seenUses[node.variable] == node.variable.readCount;
213
148 // Propagate constant to use site. 214 // Propagate constant to use site.
149 Expression constant = constantEnvironment[node.variable]; 215 Expression constant = constantEnvironment[node.variable];
150 if (constant != null) { 216 if (constant != null) {
151 --node.variable.readCount; 217 --node.variable.readCount;
218 --seenUses[node.variable]; // Do not count the use we just destroyed.
152 return constant; 219 return constant;
153 } 220 }
154 221
155 // Propagate a variable's definition to its use site if: 222 // Try to propagate another expression into this variable use.
156 // 1. It has a single use, to avoid code growth and potential duplication 223 if (!environment.isEmpty) {
157 // of side effects, AND 224 Expression binding = environment.last;
158 // 2. It was the most recent expression evaluated so that we do not 225
159 // reorder expressions with side effects. 226 // Is this variable assigned by the most recently evaluated impure
160 if (!environment.isEmpty && 227 // expression?
161 environment.last.variable == node.variable && 228 //
162 node.variable.readCount == 1) { 229 // If so, propagate the assignment, e.g:
163 --node.variable.readCount; 230 //
164 return visitExpression(environment.removeLast().value); 231 // { x = foo(); bar(x, x) } ==> bar(x = foo(), x)
232 //
233 // We must ensure that no other uses separate this use from the
234 // assignment. We therefore only propagate assignments into the first use.
235 //
236 // Note that if this is only use, `visitAssign` will then remove the
237 // redundant assignment.
238 if (getLeftHand(binding) == node.variable && isFirstUse) {
239 environment.removeLast();
240 --node.variable.readCount;
241 --seenUses[node.variable]; // Do not count the use we just destroyed.
242 return visitExpression(binding);
243 }
244
245 // Is the most recently evaluated impure expression known to have the
246 // value of this variable?
247 //
248 // If so, we can replace this use with the impure expression, e.g:
249 //
250 // { E.foo = x; bar(x) } ==> bar(E.foo = x)
251 //
252 if (getRightHand(binding) == node.variable) {
253 environment.removeLast();
254 --node.variable.readCount;
255 --seenUses[node.variable];
256 return visitExpression(binding);
257 }
165 } 258 }
166 259
167 // If the definition could not be propagated, leave the variable use. 260 // If the definition could not be propagated, leave the variable use.
168 return node; 261 return node;
169 } 262 }
170 263
171 /// Returns true if [exp] has no side effects and has a constant value within 264 /// Returns true if [exp] has no side effects and has a constant value within
172 /// any given activation of the enclosing method. 265 /// any given activation of the enclosing method.
173 bool isEffectivelyConstant(Expression exp) { 266 bool isEffectivelyConstant(Expression exp) {
174 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional 267 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional
175 // expressions recursively. Determine if that is a valuable optimization 268 // expressions recursively. Determine if that is a valuable optimization
176 // and/or if it is better handled at the CPS level. 269 // and/or if it is better handled at the CPS level.
177 return exp is Constant || 270 return exp is Constant ||
178 exp is This || 271 exp is This ||
179 exp is ReifyTypeVar || 272 exp is ReifyTypeVar ||
180 exp is VariableUse && constantEnvironment.containsKey(exp.variable); 273 exp is VariableUse && constantEnvironment.containsKey(exp.variable);
181 } 274 }
182 275
183 Statement visitAssign(Assign node) { 276 /// True if [node] is an assignment that can be propagated as a constant.
184 if (isEffectivelyConstant(node.value) && 277 bool isEffectivelyConstantAssignment(Expression node) {
185 node.variable.writeCount == 1) { 278 return node is Assign &&
279 node.variable.writeCount == 1 &&
280 isEffectivelyConstant(node.value);
281 }
282
283 Statement visitExpressionStatement(ExpressionStatement stmt) {
284 if (isEffectivelyConstantAssignment(stmt.expression)) {
285 Assign assign = stmt.expression;
186 // Handle constant assignments specially. 286 // Handle constant assignments specially.
187 // They are always safe to propagate (though we should avoid duplication). 287 // They are always safe to propagate (though we should avoid duplication).
188 // Moreover, they should not prevent other expressions from propagating. 288 // Moreover, they should not prevent other expressions from propagating.
189 if (node.variable.readCount <= 1) { 289 if (assign.variable.readCount <= 1) {
190 // A single-use constant should always be propagted to its use site. 290 // A single-use constant should always be propagted to its use site.
191 constantEnvironment[node.variable] = visitExpression(node.value); 291 constantEnvironment[assign.variable] = visitExpression(assign.value);
192 --node.variable.writeCount; 292 --assign.variable.writeCount;
193 return visitStatement(node.next); 293 return visitStatement(stmt.next);
194 } else { 294 } else {
195 // With more than one use, we cannot propagate the constant. 295 // With more than one use, we cannot propagate the constant.
196 // Visit the following statement without polluting [environment] so 296 // Visit the following statement without polluting [environment] so
197 // that any preceding non-constant assignments might still propagate. 297 // that any preceding non-constant assignments might still propagate.
198 node.next = visitStatement(node.next); 298 stmt.next = visitStatement(stmt.next);
199 node.value = visitExpression(node.value); 299 assign.value = visitExpression(assign.value);
200 return node; 300 return stmt;
201 } 301 }
302 }
303 // Try to propagate the expression, and block previous impure expressions
304 // until this has propagated.
305 environment.add(stmt.expression);
306 stmt.next = visitStatement(stmt.next);
307 if (!environment.isEmpty && environment.last == stmt.expression) {
308 // Retain the expression statement.
309 environment.removeLast();
310 stmt.expression = visitExpression(stmt.expression);
311 return stmt;
202 } else { 312 } else {
203 // Try to propagate assignment, and block previous assignment until this 313 // Expression was propagated into the successor.
204 // has propagated. 314 return stmt.next;
205 environment.add(node);
206 Statement next = visitStatement(node.next);
207 if (!environment.isEmpty && environment.last == node) {
208 // The definition could not be propagated. Residualize the let binding.
209 node.next = next;
210 environment.removeLast();
211 node.value = visitExpression(node.value);
212 return node;
213 }
214 assert(!environment.contains(node));
215 --node.variable.writeCount; // This assignment was removed.
216 return next;
217 } 315 }
218 } 316 }
219 317
318 Expression visitAssign(Assign node) {
319 node.value = visitExpression(node.value);
320 // Remove assignments to variables without any uses. This can happen
321 // because the assignment was propagated into its use, e.g:
322 //
323 // { x = foo(); bar(x) } ==> bar(x = foo()) ==> bar(foo())
324 //
325 if (node.variable.readCount == 0) {
326 --node.variable.writeCount;
327 return node.value;
328 }
329 return node;
330 }
331
332 Statement visitVariableDeclaration(VariableDeclaration node) {
333 if (isEffectivelyConstant(node.value)) {
334 node.next = visitStatement(node.next);
335 } else {
336 inEmptyEnvironment(() {
337 node.next = visitStatement(node.next);
338 });
339 }
340 node.value = visitExpression(node.value);
341 return node;
342 }
343
220 Expression visitInvokeStatic(InvokeStatic node) { 344 Expression visitInvokeStatic(InvokeStatic node) {
221 // Process arguments right-to-left, the opposite of evaluation order. 345 // Process arguments right-to-left, the opposite of evaluation order.
222 for (int i = node.arguments.length - 1; i >= 0; --i) { 346 for (int i = node.arguments.length - 1; i >= 0; --i) {
223 node.arguments[i] = visitExpression(node.arguments[i]); 347 node.arguments[i] = visitExpression(node.arguments[i]);
224 } 348 }
225 return node; 349 return node;
226 } 350 }
227 351
228 Expression visitInvokeMethod(InvokeMethod node) { 352 Expression visitInvokeMethod(InvokeMethod node) {
229 for (int i = node.arguments.length - 1; i >= 0; --i) { 353 for (int i = node.arguments.length - 1; i >= 0; --i) {
(...skipping 19 matching lines...) Expand all
249 } 373 }
250 374
251 Expression visitConcatenateStrings(ConcatenateStrings node) { 375 Expression visitConcatenateStrings(ConcatenateStrings node) {
252 for (int i = node.arguments.length - 1; i >= 0; --i) { 376 for (int i = node.arguments.length - 1; i >= 0; --i) {
253 node.arguments[i] = visitExpression(node.arguments[i]); 377 node.arguments[i] = visitExpression(node.arguments[i]);
254 } 378 }
255 return node; 379 return node;
256 } 380 }
257 381
258 Expression visitConditional(Conditional node) { 382 Expression visitConditional(Conditional node) {
259 node.condition = visitExpression(node.condition); 383 // Conditional expressions do not exist in the input, but they are
260 384 // introduced by if-to-conditional conversion.
261 inEmptyEnvironment(() { 385 // Their subexpressions have already been processed; do not reprocess them.
262 node.thenExpression = visitExpression(node.thenExpression); 386 //
263 node.elseExpression = visitExpression(node.elseExpression); 387 // Note that this can only happen for conditional expressions. It is an
264 }); 388 // error for any other type of expression to be visited twice or to be
265 389 // created and then visited. We use this special treatment of conditionals
390 // to allow for assignment inlining after if-to-conditional conversion.
391 //
392 // There are several reasons we should not reprocess the subexpressions:
393 //
394 // - It will mess up the [seenUses] counter, since a single use will be
395 // counted twice.
396 //
397 // - Other visit methods assume that all subexpressions are variable uses
398 // because they come fresh out of the tree IR builder.
399 //
400 // - Reprocessing can be expensive.
401 //
266 return node; 402 return node;
267 } 403 }
268 404
269 Expression visitLogicalOperator(LogicalOperator node) { 405 Expression visitLogicalOperator(LogicalOperator node) {
270 node.left = visitExpression(node.left); 406 node.left = visitExpression(node.left);
271 407
272 // Impure expressions may not propagate across the branch. 408 // Impure expressions may not propagate across the branch.
273 inEmptyEnvironment(() { 409 inEmptyEnvironment(() {
274 node.right = visitExpression(node.right); 410 node.right = visitExpression(node.right);
275 }); 411 });
(...skipping 15 matching lines...) Expand all
291 new StatementRewriter.nested(this).rewrite(node.definition); 427 new StatementRewriter.nested(this).rewrite(node.definition);
292 node.next = visitStatement(node.next); 428 node.next = visitStatement(node.next);
293 return node; 429 return node;
294 } 430 }
295 431
296 Statement visitReturn(Return node) { 432 Statement visitReturn(Return node) {
297 node.value = visitExpression(node.value); 433 node.value = visitExpression(node.value);
298 return node; 434 return node;
299 } 435 }
300 436
301
302 Statement visitBreak(Break node) { 437 Statement visitBreak(Break node) {
303 // Redirect through chain of breaks. 438 // Redirect through chain of breaks.
304 // Note that useCount was accounted for at visitLabeledStatement. 439 // Note that useCount was accounted for at visitLabeledStatement.
305 // Note redirect may return either a Break or Continue statement. 440 // Note redirect may return either a Break or Continue statement.
306 Jump jump = redirect(node); 441 Jump jump = redirect(node);
307 if (jump is Break && 442 if (jump is Break &&
308 jump.target.useCount == 1 && 443 jump.target.useCount == 1 &&
309 safeForInlining.contains(jump.target)) { 444 safeForInlining.contains(jump.target)) {
310 --jump.target.useCount; 445 --jump.target.useCount;
311 return visitStatement(jump.target.binding.next); 446 return visitStatement(jump.target.binding.next);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 node.condition = visitExpression(node.condition); 490 node.condition = visitExpression(node.condition);
356 491
357 // Do not propagate assignments into branches. Doing so will lead to code 492 // Do not propagate assignments into branches. Doing so will lead to code
358 // duplication. 493 // duplication.
359 // TODO(kmillikin): Rethink this. Propagating some assignments 494 // TODO(kmillikin): Rethink this. Propagating some assignments
360 // (e.g. variables) is benign. If they can occur here, they should 495 // (e.g. variables) is benign. If they can occur here, they should
361 // be handled well. 496 // be handled well.
362 inEmptyEnvironment(() { 497 inEmptyEnvironment(() {
363 node.thenStatement = visitStatement(node.thenStatement); 498 node.thenStatement = visitStatement(node.thenStatement);
364 node.elseStatement = visitStatement(node.elseStatement); 499 node.elseStatement = visitStatement(node.elseStatement);
500
501 tryCollapseIf(node);
365 }); 502 });
366 503
367 tryCollapseIf(node); 504 Statement reduced = combineStatementsInBranches(
368
369 Statement reduced = combineStatementsWithSubexpressions(
370 node.thenStatement, 505 node.thenStatement,
371 node.elseStatement, 506 node.elseStatement,
372 (t,f) => new Conditional(node.condition, t, f)..processed = true); 507 node.condition);
373 if (reduced != null) { 508 if (reduced != null) {
374 // TODO(asgerf): Avoid revisiting nodes or visiting nodes that we created. 509 return reduced;
375 // This breaks the assumption that all subexpressions are
376 // variable uses, and it can be expensive.
377 // Revisit in case the break can now be inlined.
378 return visitStatement(reduced);
379 } 510 }
380 511
381 return node; 512 return node;
382 } 513 }
383 514
384 Statement visitWhileTrue(WhileTrue node) { 515 Statement visitWhileTrue(WhileTrue node) {
385 // Do not propagate assignments into loops. Doing so is not safe for 516 // Do not propagate assignments into loops. Doing so is not safe for
386 // variables modified in the loop (the initial value will be propagated). 517 // variables modified in the loop (the initial value will be propagated).
387 inEmptyEnvironment(() { 518 inEmptyEnvironment(() {
388 node.body = visitStatement(node.body); 519 node.body = visitStatement(node.body);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
433 entry.key = visitExpression(entry.key); 564 entry.key = visitExpression(entry.key);
434 } 565 }
435 return node; 566 return node;
436 } 567 }
437 568
438 Expression visitTypeOperator(TypeOperator node) { 569 Expression visitTypeOperator(TypeOperator node) {
439 node.receiver = visitExpression(node.receiver); 570 node.receiver = visitExpression(node.receiver);
440 return node; 571 return node;
441 } 572 }
442 573
443 Statement visitExpressionStatement(ExpressionStatement node) { 574 Expression visitSetField(SetField node) {
444 node.expression = visitExpression(node.expression);
445 // Do not allow propagation of assignments past an expression evaluated
446 // for its side effects because it risks reordering side effects.
447 // TODO(kmillikin): Rethink this. Some propagation is benign,
448 // e.g. variables, or other pure values that are not destroyed by
449 // the expression statement. If they can occur here they should be
450 // handled well.
451 inEmptyEnvironment(() {
452 node.next = visitStatement(node.next);
453 });
454 return node;
455 }
456
457 Statement visitSetField(SetField node) {
458 node.next = visitStatement(node.next);
459 node.value = visitExpression(node.value); 575 node.value = visitExpression(node.value);
460 node.object = visitExpression(node.object); 576 node.object = visitExpression(node.object);
461 return node; 577 return node;
462 } 578 }
463 579
464 Expression visitGetField(GetField node) { 580 Expression visitGetField(GetField node) {
465 node.object = visitExpression(node.object); 581 node.object = visitExpression(node.object);
466 return node; 582 return node;
467 } 583 }
468 584
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 /// If [combine] returns E1 then the unified statement is equivalent to [s], 620 /// If [combine] returns E1 then the unified statement is equivalent to [s],
505 /// and if [combine] returns E2 the unified statement is equivalence to [t]. 621 /// and if [combine] returns E2 the unified statement is equivalence to [t].
506 /// 622 ///
507 /// It is guaranteed that no side effects occur between the beginning of the 623 /// It is guaranteed that no side effects occur between the beginning of the
508 /// statement and the position of the combined expression. 624 /// statement and the position of the combined expression.
509 /// 625 ///
510 /// Returns null if the statements are too different. 626 /// Returns null if the statements are too different.
511 /// 627 ///
512 /// If non-null is returned, the caller MUST discard [s] and [t] and use 628 /// If non-null is returned, the caller MUST discard [s] and [t] and use
513 /// the returned statement instead. 629 /// the returned statement instead.
514 static Statement combineStatementsWithSubexpressions( 630 Statement combineStatementsInBranches(
515 Statement s, 631 Statement s,
516 Statement t, 632 Statement t,
517 Expression combine(Expression s, Expression t)) { 633 Expression condition) {
518 if (s is Return && t is Return) { 634 if (s is Return && t is Return) {
519 return new Return(combine(s.value, t.value)); 635 return new Return(new Conditional(condition, s.value, t.value));
520 }
521 if (s is Assign && t is Assign && s.variable == t.variable) {
522 Statement next = combineStatements(s.next, t.next);
523 if (next != null) {
524 // Destroy both original assignments to the variable.
525 --s.variable.writeCount;
526 --t.variable.writeCount;
527 // The Assign constructor will increment the reference count again.
528 return new Assign(s.variable,
529 combine(s.value, t.value),
530 next);
531 }
532 } 636 }
533 if (s is ExpressionStatement && t is ExpressionStatement) { 637 if (s is ExpressionStatement && t is ExpressionStatement) {
638 // Combine the two expressions and the two successor statements.
639 //
640 // C ? {E1 ; S1} : {E2 ; S2}
641 // ==>
642 // (C ? E1 : E2) : combine(S1, S2)
643 //
644 // If E1 and E2 are assignments, we want to propagate these into the
645 // combined statement.
646 //
647 // It might not be possible to combine the statements, so we combine the
648 // expressions, put the result in the environment, and then uncombine the
649 // expressions if the statements could not be combined.
650
651 // Combine the expressions.
652 CombinedExpressions values =
653 combineAsConditional(s.expression, t.expression, condition);
654
655 // Put this into the environment and try to combine the statements.
656 // We are not in risk of reprocessing the original subexpressions because
657 // the combined expression will always hide them inside a Conditional.
658 environment.add(values.combined);
534 Statement next = combineStatements(s.next, t.next); 659 Statement next = combineStatements(s.next, t.next);
535 if (next != null) { 660
536 return new ExpressionStatement(combine(s.expression, t.expression), 661 if (next == null) {
537 next); 662 // Statements could not be combined.
663 // Restore the environment and uncombine expressions again.
664 environment.removeLast();
665 values.uncombine();
666 return null;
667 } else if (!environment.isEmpty && environment.last == values.combined) {
668 // Statements were combined but the combined expression could not be
669 // propagated. Leave it as an expression statement here.
670 environment.removeLast();
671 s.expression = values.combined;
672 s.next = next;
673 return s;
674 } else {
675 // Statements were combined and the combined expressions were
676 // propagated into the combined statement.
677 return next;
538 } 678 }
539 } 679 }
540 return null; 680 return null;
541 } 681 }
542 682
683 /// Creates the expression `[condition] ? [s] : [t]` or an equivalent
684 /// expression if something better can be done.
685 ///
686 /// In particular, assignments will be merged as follows:
687 ///
688 /// C ? (v = E1) : (v = E2)
689 /// ==>
690 /// v = C ? E1 : E2
691 ///
692 /// The latter form is more compact and can also be inlined.
693 CombinedExpressions combineAsConditional(
694 Expression s,
695 Expression t,
696 Expression condition) {
697 if (s is Assign && t is Assign && s.variable == t.variable) {
698 Expression values = new Conditional(condition, s.value, t.value);
699 return new CombinedAssigns(s, t, new CombinedExpressions(values));
700 }
701 return new CombinedExpressions(new Conditional(condition, s, t));
702 }
703
543 /// Returns a statement equivalent to both [s] and [t], or null if [s] and 704 /// Returns a statement equivalent to both [s] and [t], or null if [s] and
544 /// [t] are incompatible. 705 /// [t] are incompatible.
545 /// If non-null is returned, the caller MUST discard [s] and [t] and use 706 /// If non-null is returned, the caller MUST discard [s] and [t] and use
546 /// the returned statement instead. 707 /// the returned statement instead.
547 /// If two breaks are combined, the label's break counter will be decremented. 708 /// If two breaks are combined, the label's break counter will be decremented.
548 static Statement combineStatements(Statement s, Statement t) { 709 Statement combineStatements(Statement s, Statement t) {
549 if (s is Break && t is Break && s.target == t.target) { 710 if (s is Break && t is Break && s.target == t.target) {
550 --t.target.useCount; // Two breaks become one. 711 --t.target.useCount; // Two breaks become one.
712 if (s.target.useCount == 1 && safeForInlining.contains(s.target)) {
713 // Only one break remains; inline it.
714 --s.target.useCount;
715 return visitStatement(s.target.binding.next);
716 }
551 return s; 717 return s;
552 } 718 }
553 if (s is Continue && t is Continue && s.target == t.target) { 719 if (s is Continue && t is Continue && s.target == t.target) {
554 --t.target.useCount; // Two continues become one. 720 --t.target.useCount; // Two continues become one.
555 return s; 721 return s;
556 } 722 }
557 if (s is Return && t is Return) { 723 if (s is Return && t is Return) {
558 Expression e = combineExpressions(s.value, t.value); 724 CombinedExpressions values = combineExpressions(s.value, t.value);
559 if (e != null) { 725 if (values != null) {
560 return new Return(e); 726 return new Return(values.combined);
561 } 727 }
562 } 728 }
563 if (s is Assign && t is Assign && 729 if (s is ExpressionStatement && t is ExpressionStatement) {
564 s.variable == t.variable && 730 CombinedExpressions values =
565 isSameVariable(s.value, t.value)) { 731 combineExpressions(s.expression, t.expression);
732 if (values == null) return null;
733 environment.add(values.combined);
566 Statement next = combineStatements(s.next, t.next); 734 Statement next = combineStatements(s.next, t.next);
567 if (next != null) { 735 if (next == null) {
736 // The successors could not be combined.
737 // Restore the environment and uncombine the values again.
738 assert(environment.last == values.combined);
739 environment.removeLast();
740 values.uncombine();
741 return null;
742 } else if (!environment.isEmpty && environment.last == values.combined) {
743 // The successors were combined but the combined expressions were not
744 // propagated. Leave the combined expression as a statement.
745 environment.removeLast();
746 s.expression = values.combined;
568 s.next = next; 747 s.next = next;
569 --t.variable.writeCount;
570 --(t.value as VariableUse).variable.readCount;
571 return s; 748 return s;
749 } else {
750 // The successors were combined, and the combined expressions were
751 // propagated into the successors.
752 return next;
572 } 753 }
573 } 754 }
574 return null; 755 return null;
575 } 756 }
576 757
577 /// Returns an expression equivalent to both [e1] and [e2]. 758 /// Returns an expression equivalent to both [e1] and [e2].
578 /// If non-null is returned, the caller must discard [e1] and [e2] and use 759 /// If non-null is returned, the caller must discard [e1] and [e2] and use
579 /// the resulting expression in the tree. 760 /// the resulting expression in the tree.
580 static Expression combineExpressions(Expression e1, Expression e2) { 761 CombinedExpressions combineExpressions(Expression e1, Expression e2) {
581 if (e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable) { 762 if (e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable) {
582 --e1.variable.readCount; // Two references become one. 763 return new CombinedUses(e1, e2);
583 return e1; 764 }
765 if (e1 is Assign && e2 is Assign && e1.variable == e2.variable) {
766 CombinedExpressions values = combineExpressions(e1.value, e2.value);
767 if (values != null) {
768 return new CombinedAssigns(e1, e2, values);
769 }
584 } 770 }
585 if (e1 is Constant && e2 is Constant && e1.value == e2.value) { 771 if (e1 is Constant && e2 is Constant && e1.value == e2.value) {
586 return e1; 772 return new CombinedExpressions(e1);
587 } 773 }
588 return null; 774 return null;
589 } 775 }
590 776
591 /// Try to collapse nested ifs using && and || expressions. 777 /// Try to collapse nested ifs using && and || expressions.
592 /// For example: 778 /// For example:
593 /// 779 ///
594 /// if (E1) { if (E2) S else break L } else break L 780 /// if (E1) { if (E2) S else break L } else break L
595 /// ==> 781 /// ==>
596 /// if (E1 && E2) S else break L 782 /// if (E1 && E2) S else break L
597 /// 783 ///
598 /// [branch1] and [branch2] control the position of the S statement. 784 /// [branch1] and [branch2] control the position of the S statement.
599 /// 785 ///
600 /// Returns true if another collapse redex might have been introduced. 786 /// Must be called with an empty environment.
601 void tryCollapseIf(If node) { 787 void tryCollapseIf(If node) {
788 assert(environment.isEmpty);
602 // Repeatedly try to collapse nested ifs. 789 // Repeatedly try to collapse nested ifs.
603 // The transformation is shrinking (destroys an if) so it remains linear. 790 // The transformation is shrinking (destroys an if) so it remains linear.
604 // Here is an example where more than one iteration is required: 791 // Here is an example where more than one iteration is required:
605 // 792 //
606 // if (E1) 793 // if (E1)
607 // if (E2) break L2 else break L1 794 // if (E2) break L2 else break L1
608 // else 795 // else
609 // break L1 796 // break L1
610 // 797 //
611 // L1.target ::= 798 // L1.target ::=
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
646 Statement innerThen = getBranch(innerIf, branch2); 833 Statement innerThen = getBranch(innerIf, branch2);
647 Statement innerElse = getBranch(innerIf, !branch2); 834 Statement innerElse = getBranch(innerIf, !branch2);
648 Statement combinedElse = combineStatements(innerElse, outerElse); 835 Statement combinedElse = combineStatements(innerElse, outerElse);
649 if (combinedElse != null) { 836 if (combinedElse != null) {
650 // We always put S in the then branch of the result, and adjust the 837 // We always put S in the then branch of the result, and adjust the
651 // condition expression if S was actually found in the else branch(es). 838 // condition expression if S was actually found in the else branch(es).
652 outerIf.condition = new LogicalOperator.and( 839 outerIf.condition = new LogicalOperator.and(
653 makeCondition(outerIf.condition, branch1), 840 makeCondition(outerIf.condition, branch1),
654 makeCondition(innerIf.condition, branch2)); 841 makeCondition(innerIf.condition, branch2));
655 outerIf.thenStatement = innerThen; 842 outerIf.thenStatement = innerThen;
656 843 outerIf.elseStatement = combinedElse;
657 // Try to inline the remaining break. Do not propagate assignments.
658 inEmptyEnvironment(() {
659 // TODO(asgerf): Avoid quadratic cost from repeated processing. This
660 // should be easier after we introduce basic blocks.
661 outerIf.elseStatement = visitStatement(combinedElse);
662 });
663
664 return outerIf.elseStatement is If; 844 return outerIf.elseStatement is If;
665 } 845 }
666 } 846 }
667 return false; 847 return false;
668 } 848 }
669 849
670 static bool isSameVariable(Expression e1, Expression e2) {
671 return e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable;
672 }
673
674 Expression makeCondition(Expression e, bool polarity) { 850 Expression makeCondition(Expression e, bool polarity) {
675 return polarity ? e : new Not(e); 851 return polarity ? e : new Not(e);
676 } 852 }
677 853
678 Statement getBranch(If node, bool polarity) { 854 Statement getBranch(If node, bool polarity) {
679 return polarity ? node.thenStatement : node.elseStatement; 855 return polarity ? node.thenStatement : node.elseStatement;
680 } 856 }
681 } 857 }
858
859 /// Result of combining two expressions, with the potential for reverting the
860 /// combination.
861 ///
862 /// Reverting a combination is done by calling [uncombine]. In this case,
863 /// both the original expressions should remain in the tree, and the [combined]
864 /// expression should be orphaned.
865 ///
866 /// Explicitly reverting a combination is necessary to maintain variable
867 /// reference counts.
868 abstract class CombinedExpressions {
869 Expression get combined;
870 void uncombine();
871
872 factory CombinedExpressions(Expression e) = GenericCombinedExpressions;
873 }
874
875 /// Combines assignments of form `[variable] := E1` and `[variable] := E2` into
876 /// a single assignment of form `[variable] := combine(E1, E2)`.
877 class CombinedAssigns implements CombinedExpressions {
878 Assign assign1, assign2;
879 CombinedExpressions value;
880 Expression combined;
881
882 CombinedAssigns(this.assign1, this.assign2, this.value) {
883 assert(assign1.variable == assign2.variable);
884 assign1.variable.writeCount -= 2; // Destroy the two original assignemnts.
885 combined = new Assign(assign1.variable, value.combined);
886 }
887
888 void uncombine() {
889 value.uncombine();
890 ++assign1.variable.writeCount; // Restore original reference count.
891 }
892 }
893
894 /// Combines two variable uses into one.
895 class CombinedUses implements CombinedExpressions {
896 VariableUse use1, use2;
897 Expression combined;
898
899 CombinedUses(this.use1, this.use2) {
900 assert(use1.variable == use2.variable);
901 use1.variable.readCount -= 2; // Destroy both the original uses.
902 combined = new VariableUse(use1.variable);
903 }
904
905 void uncombine() {
906 ++use1.variable.readCount; // Restore original reference count.
907 }
908 }
909
910 /// Result of combining two expressions that do not affect reference counting.
911 class GenericCombinedExpressions implements CombinedExpressions {
912 Expression combined;
913
914 GenericCombinedExpressions(this.combined);
915
916 void uncombine() {}
917 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698