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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/dart_backend/statement_rewriter.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library statement_rewriter;
6
7 import 'tree_ir_nodes.dart';
8
9 /**
10 * Performs the following transformations on the tree:
11 * - Assignment propagation
12 * - If-to-conditional conversion
13 * - Flatten nested ifs
14 * - Break inlining
15 * - Redirect breaks
16 *
17 * The above transformations all eliminate statements from the tree, and may
18 * introduce redexes of each other.
19 *
20 *
21 * ASSIGNMENT PROPAGATION:
22 * Single-use definitions are propagated to their use site when possible.
23 * For example:
24 *
25 * { v0 = foo(); return v0; }
26 * ==>
27 * return foo()
28 *
29 * After translating out of CPS, all intermediate values are bound by [Assign].
30 * This transformation propagates such definitions to their uses when it is
31 * safe and profitable. Bindings are processed "on demand" when their uses are
32 * seen, but are only processed once to keep this transformation linear in
33 * the size of the tree.
34 *
35 * The transformation builds an environment containing [Assign] bindings that
36 * are in scope. These bindings have yet-untranslated definitions. When a use
37 * is encountered the transformation determines if it is safe and profitable
38 * to propagate the definition to its use. If so, it is removed from the
39 * environment and the definition is recursively processed (in the
40 * new environment at the use site) before being propagated.
41 *
42 * See [visitVariable] for the implementation of the heuristic for propagating
43 * a definition.
44 *
45 *
46 * IF-TO-CONDITIONAL CONVERSION:
47 * If-statement are converted to conditional expressions when possible.
48 * For example:
49 *
50 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L }
51 * ==>
52 * { v1 = v0 ? foo() : bar(); break L }
53 *
54 * This can lead to inlining of L, which in turn can lead to further propagation
55 * of the variable v1.
56 *
57 * See [visitIf].
58 *
59 *
60 * FLATTEN NESTED IFS:
61 * An if inside an if is converted to an if with a logical operator.
62 * For example:
63 *
64 * if (E1) { if (E2) {S} else break L } else break L
65 * ==>
66 * if (E1 && E2) {S} else break L
67 *
68 * This may lead to inlining of L.
69 *
70 *
71 * BREAK INLINING:
72 * Single-use labels are inlined at [Break] statements.
73 * For example:
74 *
75 * L0: { v0 = foo(); break L0 }; return v0;
76 * ==>
77 * v0 = foo(); return v0;
78 *
79 * This can lead to propagation of v0.
80 *
81 * See [visitBreak] and [visitLabeledStatement].
82 *
83 *
84 * REDIRECT BREAKS:
85 * Labeled statements whose next is a break become flattened and all breaks
86 * to their label are redirected.
87 * For example, where 'jump' is either break or continue:
88 *
89 * L0: {... break L0 ...}; jump L1
90 * ==>
91 * {... jump L1 ...}
92 *
93 * This may trigger a flattening of nested ifs in case the eliminated label
94 * separated two ifs.
95 */
96 class StatementRewriter extends Visitor<Statement, Expression> {
97 // The binding environment. The rightmost element of the list is the nearest
98 // available enclosing binding.
99 List<Assign> environment;
100
101 /// Substitution map for labels. Any break to a label L should be substituted
102 /// for a break to L' if L maps to L'.
103 Map<Label, Jump> labelRedirects = <Label, Jump>{};
104
105 /// Returns the redirect target of [label] or [label] itself if it should not
106 /// be redirected.
107 Jump redirect(Jump jump) {
108 Jump newJump = labelRedirects[jump.target];
109 return newJump != null ? newJump : jump;
110 }
111
112 void rewrite(FunctionDefinition definition) {
113 if (definition.isAbstract) return;
114
115 environment = <Assign>[];
116 definition.body = visitStatement(definition.body);
117
118 // TODO(kmillikin): Allow definitions that are not propagated. Here,
119 // this means rebuilding the binding with a recursively unnamed definition,
120 // or else introducing a variable definition and an assignment.
121 assert(environment.isEmpty);
122 }
123
124 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this);
125
126 Expression visitVariable(Variable node) {
127 // Propagate a variable's definition to its use site if:
128 // 1. It has a single use, to avoid code growth and potential duplication
129 // of side effects, AND
130 // 2. It was the most recent expression evaluated so that we do not
131 // reorder expressions with side effects.
132 if (!environment.isEmpty &&
133 environment.last.variable == node &&
134 environment.last.hasExactlyOneUse) {
135 return visitExpression(environment.removeLast().definition);
136 }
137 // If the definition could not be propagated, leave the variable use.
138 return node;
139 }
140
141
142 Statement visitAssign(Assign node) {
143 environment.add(node);
144 Statement next = visitStatement(node.next);
145
146 if (!environment.isEmpty && environment.last == node) {
147 // The definition could not be propagated. Residualize the let binding.
148 node.next = next;
149 environment.removeLast();
150 node.definition = visitExpression(node.definition);
151 return node;
152 }
153 assert(!environment.contains(node));
154 return next;
155 }
156
157 Expression visitInvokeStatic(InvokeStatic node) {
158 // Process arguments right-to-left, the opposite of evaluation order.
159 for (int i = node.arguments.length - 1; i >= 0; --i) {
160 node.arguments[i] = visitExpression(node.arguments[i]);
161 }
162 return node;
163 }
164
165 Expression visitInvokeMethod(InvokeMethod node) {
166 for (int i = node.arguments.length - 1; i >= 0; --i) {
167 node.arguments[i] = visitExpression(node.arguments[i]);
168 }
169 node.receiver = visitExpression(node.receiver);
170 return node;
171 }
172
173 Expression visitInvokeSuperMethod(InvokeSuperMethod node) {
174 for (int i = node.arguments.length - 1; i >= 0; --i) {
175 node.arguments[i] = visitExpression(node.arguments[i]);
176 }
177 return node;
178 }
179
180 Expression visitInvokeConstructor(InvokeConstructor node) {
181 for (int i = node.arguments.length - 1; i >= 0; --i) {
182 node.arguments[i] = visitExpression(node.arguments[i]);
183 }
184 return node;
185 }
186
187 Expression visitConcatenateStrings(ConcatenateStrings node) {
188 for (int i = node.arguments.length - 1; i >= 0; --i) {
189 node.arguments[i] = visitExpression(node.arguments[i]);
190 }
191 return node;
192 }
193
194 Expression visitConditional(Conditional node) {
195 node.condition = visitExpression(node.condition);
196
197 List<Assign> savedEnvironment = environment;
198 environment = <Assign>[];
199 node.thenExpression = visitExpression(node.thenExpression);
200 assert(environment.isEmpty);
201 node.elseExpression = visitExpression(node.elseExpression);
202 assert(environment.isEmpty);
203 environment = savedEnvironment;
204
205 return node;
206 }
207
208 Expression visitLogicalOperator(LogicalOperator node) {
209 node.left = visitExpression(node.left);
210
211 environment.add(null); // impure expressions may not propagate across branch
212 node.right = visitExpression(node.right);
213 environment.removeLast();
214
215 return node;
216 }
217
218 Expression visitNot(Not node) {
219 node.operand = visitExpression(node.operand);
220 return node;
221 }
222
223 Expression visitFunctionExpression(FunctionExpression node) {
224 new StatementRewriter().rewrite(node.definition);
225 return node;
226 }
227
228 Statement visitFunctionDeclaration(FunctionDeclaration node) {
229 new StatementRewriter().rewrite(node.definition);
230 node.next = visitStatement(node.next);
231 return node;
232 }
233
234 Statement visitReturn(Return node) {
235 node.value = visitExpression(node.value);
236 return node;
237 }
238
239
240 Statement visitBreak(Break node) {
241 // Redirect through chain of breaks.
242 // Note that useCount was accounted for at visitLabeledStatement.
243 // Note redirect may return either a Break or Continue statement.
244 Jump jump = redirect(node);
245 if (jump is Break && jump.target.useCount == 1) {
246 --jump.target.useCount;
247 return visitStatement(jump.target.binding.next);
248 }
249 return jump;
250 }
251
252 Statement visitContinue(Continue node) {
253 return node;
254 }
255
256 Statement visitLabeledStatement(LabeledStatement node) {
257 if (node.next is Jump) {
258 // Eliminate label if next is a break or continue statement
259 // Breaks to this label are redirected to the outer label.
260 // Note that breakCount for the two labels is updated proactively here
261 // so breaks can reliably tell if they should inline their target.
262 Jump next = node.next;
263 Jump newJump = redirect(next);
264 labelRedirects[node.label] = newJump;
265 newJump.target.useCount += node.label.useCount - 1;
266 node.label.useCount = 0;
267 Statement result = visitStatement(node.body);
268 labelRedirects.remove(node.label); // Save some space.
269 return result;
270 }
271
272 node.body = visitStatement(node.body);
273
274 if (node.label.useCount == 0) {
275 // Eliminate the label if next was inlined at a break
276 return node.body;
277 }
278
279 // Do not propagate assignments into the successor statements, since they
280 // may be overwritten by assignments in the body.
281 List<Assign> savedEnvironment = environment;
282 environment = <Assign>[];
283 node.next = visitStatement(node.next);
284 environment = savedEnvironment;
285
286 return node;
287 }
288
289 Statement visitIf(If node) {
290 node.condition = visitExpression(node.condition);
291
292 // Do not propagate assignments into branches. Doing so will lead to code
293 // duplication.
294 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g.,
295 // constants or variables) is benign. If they can occur here, they should
296 // be handled well.
297 List<Assign> savedEnvironment = environment;
298 environment = <Assign>[];
299 node.thenStatement = visitStatement(node.thenStatement);
300 assert(environment.isEmpty);
301 node.elseStatement = visitStatement(node.elseStatement);
302 assert(environment.isEmpty);
303 environment = savedEnvironment;
304
305 tryCollapseIf(node);
306
307 Statement reduced = combineStatementsWithSubexpressions(
308 node.thenStatement,
309 node.elseStatement,
310 (t,f) => new Conditional(node.condition, t, f)..processed = true);
311 if (reduced != null) {
312 if (reduced.next is Break) {
313 // In case the break can now be inlined.
314 reduced = visitStatement(reduced);
315 }
316 return reduced;
317 }
318
319 return node;
320 }
321
322 Statement visitWhileTrue(WhileTrue node) {
323 // Do not propagate assignments into loops. Doing so is not safe for
324 // variables modified in the loop (the initial value will be propagated).
325 List<Assign> savedEnvironment = environment;
326 environment = <Assign>[];
327 node.body = visitStatement(node.body);
328 assert(environment.isEmpty);
329 environment = savedEnvironment;
330 return node;
331 }
332
333 Statement visitWhileCondition(WhileCondition node) {
334 // Not introduced yet
335 throw "Unexpected WhileCondition in StatementRewriter";
336 }
337
338 Expression visitConstant(Constant node) {
339 return node;
340 }
341
342 Expression visitThis(This node) {
343 return node;
344 }
345
346 Expression visitReifyTypeVar(ReifyTypeVar node) {
347 return node;
348 }
349
350 Expression visitLiteralList(LiteralList node) {
351 // Process values right-to-left, the opposite of evaluation order.
352 for (int i = node.values.length - 1; i >= 0; --i) {
353 node.values[i] = visitExpression(node.values[i]);
354 }
355 return node;
356 }
357
358 Expression visitLiteralMap(LiteralMap node) {
359 // Process arguments right-to-left, the opposite of evaluation order.
360 for (LiteralMapEntry entry in node.entries.reversed) {
361 entry.value = visitExpression(entry.value);
362 entry.key = visitExpression(entry.key);
363 }
364 return node;
365 }
366
367 Expression visitTypeOperator(TypeOperator node) {
368 node.receiver = visitExpression(node.receiver);
369 return node;
370 }
371
372 Statement visitExpressionStatement(ExpressionStatement node) {
373 node.expression = visitExpression(node.expression);
374 // Do not allow propagation of assignments past an expression evaluated
375 // for its side effects because it risks reordering side effects.
376 // TODO(kmillikin): Rethink this. Some propagation is benign, e.g.,
377 // constants, variables, or other pure values that are not destroyed by
378 // the expression statement. If they can occur here they should be
379 // handled well.
380 List<Assign> savedEnvironment = environment;
381 environment = <Assign>[];
382 node.next = visitStatement(node.next);
383 assert(environment.isEmpty);
384 environment = savedEnvironment;
385 return node;
386 }
387
388 /// If [s] and [t] are similar statements we extract their subexpressions
389 /// and returns a new statement of the same type using expressions combined
390 /// with the [combine] callback. For example:
391 ///
392 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2)
393 ///
394 /// If [combine] returns E1 then the unified statement is equivalent to [s],
395 /// and if [combine] returns E2 the unified statement is equivalence to [t].
396 ///
397 /// It is guaranteed that no side effects occur between the beginning of the
398 /// statement and the position of the combined expression.
399 ///
400 /// Returns null if the statements are too different.
401 ///
402 /// If non-null is returned, the caller MUST discard [s] and [t] and use
403 /// the returned statement instead.
404 static Statement combineStatementsWithSubexpressions(
405 Statement s,
406 Statement t,
407 Expression combine(Expression s, Expression t)) {
408 if (s is Return && t is Return) {
409 return new Return(combine(s.value, t.value));
410 }
411 if (s is Assign && t is Assign && s.variable == t.variable) {
412 Statement next = combineStatements(s.next, t.next);
413 if (next != null) {
414 --t.variable.writeCount; // Two assignments become one.
415 return new Assign(s.variable,
416 combine(s.definition, t.definition),
417 next);
418 }
419 }
420 if (s is ExpressionStatement && t is ExpressionStatement) {
421 Statement next = combineStatements(s.next, t.next);
422 if (next != null) {
423 return new ExpressionStatement(combine(s.expression, t.expression),
424 next);
425 }
426 }
427 return null;
428 }
429
430 /// Returns a statement equivalent to both [s] and [t], or null if [s] and
431 /// [t] are incompatible.
432 /// If non-null is returned, the caller MUST discard [s] and [t] and use
433 /// the returned statement instead.
434 /// If two breaks are combined, the label's break counter will be decremented.
435 static Statement combineStatements(Statement s, Statement t) {
436 if (s is Break && t is Break && s.target == t.target) {
437 --t.target.useCount; // Two breaks become one.
438 return s;
439 }
440 if (s is Continue && t is Continue && s.target == t.target) {
441 --t.target.useCount; // Two continues become one.
442 return s;
443 }
444 if (s is Return && t is Return) {
445 Expression e = combineExpressions(s.value, t.value);
446 if (e != null) {
447 return new Return(e);
448 }
449 }
450 return null;
451 }
452
453 /// Returns an expression equivalent to both [e1] and [e2].
454 /// If non-null is returned, the caller must discard [e1] and [e2] and use
455 /// the resulting expression in the tree.
456 static Expression combineExpressions(Expression e1, Expression e2) {
457 if (e1 is Variable && e1 == e2) {
458 --e1.readCount; // Two references become one.
459 return e1;
460 }
461 if (e1 is Constant && e2 is Constant && e1.value == e2.value) {
462 return e1;
463 }
464 return null;
465 }
466
467 /// Try to collapse nested ifs using && and || expressions.
468 /// For example:
469 ///
470 /// if (E1) { if (E2) S else break L } else break L
471 /// ==>
472 /// if (E1 && E2) S else break L
473 ///
474 /// [branch1] and [branch2] control the position of the S statement.
475 ///
476 /// Returns true if another collapse redex might have been introduced.
477 void tryCollapseIf(If node) {
478 // Repeatedly try to collapse nested ifs.
479 // The transformation is shrinking (destroys an if) so it remains linear.
480 // Here is an example where more than one iteration is required:
481 //
482 // if (E1)
483 // if (E2) break L2 else break L1
484 // else
485 // break L1
486 //
487 // L1.target ::=
488 // if (E3) S else break L2
489 //
490 // After first collapse:
491 //
492 // if (E1 && E2)
493 // break L2
494 // else
495 // {if (E3) S else break L2} (inlined from break L1)
496 //
497 // We can then do another collapse using the inlined nested if.
498 bool changed = true;
499 while (changed) {
500 changed = false;
501 if (tryCollapseIfAux(node, true, true)) {
502 changed = true;
503 }
504 if (tryCollapseIfAux(node, true, false)) {
505 changed = true;
506 }
507 if (tryCollapseIfAux(node, false, true)) {
508 changed = true;
509 }
510 if (tryCollapseIfAux(node, false, false)) {
511 changed = true;
512 }
513 }
514 }
515
516 bool tryCollapseIfAux(If outerIf, bool branch1, bool branch2) {
517 // NOTE: We name variables here as if S is in the then-then position.
518 Statement outerThen = getBranch(outerIf, branch1);
519 Statement outerElse = getBranch(outerIf, !branch1);
520 if (outerThen is If && outerElse is Break) {
521 If innerIf = outerThen;
522 Statement innerThen = getBranch(innerIf, branch2);
523 Statement innerElse = getBranch(innerIf, !branch2);
524 if (innerElse is Break && innerElse.target == outerElse.target) {
525 // We always put S in the then branch of the result, and adjust the
526 // condition expression if S was actually found in the else branch(es).
527 outerIf.condition = new LogicalOperator.and(
528 makeCondition(outerIf.condition, branch1),
529 makeCondition(innerIf.condition, branch2));
530 outerIf.thenStatement = innerThen;
531 --innerElse.target.useCount;
532
533 // Try to inline the remaining break. Do not propagate assignments.
534 List<Assign> savedEnvironment = environment;
535 environment = <Assign>[];
536 outerIf.elseStatement = visitStatement(outerElse);
537 assert(environment.isEmpty);
538 environment = savedEnvironment;
539
540 return outerIf.elseStatement is If && innerThen is Break;
541 }
542 }
543 return false;
544 }
545
546 Expression makeCondition(Expression e, bool polarity) {
547 return polarity ? e : new Not(e);
548 }
549
550 Statement getBranch(If node, bool polarity) {
551 return polarity ? node.thenStatement : node.elseStatement;
552 }
553 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698