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

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

Issue 918653002: Propagate pseudo-constants expressions separately in tree rewriter. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 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 propagation
10 * - If-to-conditional conversion 10 * - If-to-conditional conversion
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 * {... jump L1 ...} 89 * {... jump L1 ...}
90 * 90 *
91 * This may trigger a flattening of nested ifs in case the eliminated label 91 * This may trigger a flattening of nested ifs in case the eliminated label
92 * separated two ifs. 92 * separated two ifs.
93 */ 93 */
94 class StatementRewriter extends Visitor<Statement, Expression> with PassMixin { 94 class StatementRewriter extends Visitor<Statement, Expression> with PassMixin {
95 // The binding environment. The rightmost element of the list is the nearest 95 // The binding environment. The rightmost element of the list is the nearest
96 // available enclosing binding. 96 // available enclosing binding.
97 List<Assign> environment; 97 List<Assign> environment;
98 98
99 /// Binding environment for variables that are assigned to effectively
100 /// constant expressions (see [isEffectivelyConstant]).
101 final Map<Variable, Expression> constantEnvironment;
102
99 /// Substitution map for labels. Any break to a label L should be substituted 103 /// Substitution map for labels. Any break to a label L should be substituted
100 /// for a break to L' if L maps to L'. 104 /// for a break to L' if L maps to L'.
101 Map<Label, Jump> labelRedirects = <Label, Jump>{}; 105 Map<Label, Jump> labelRedirects = <Label, Jump>{};
102 106
107 /// Rewriter for methods.
108 StatementRewriter() : constantEnvironment = <Variable, Expression>{};
109
110 /// Rewriter for nested functions.
111 StatementRewriter.nested(StatementRewriter parent)
112 : constantEnvironment = parent.constantEnvironment;
113
103 /// Returns the redirect target of [label] or [label] itself if it should not 114 /// Returns the redirect target of [label] or [label] itself if it should not
104 /// be redirected. 115 /// be redirected.
105 Jump redirect(Jump jump) { 116 Jump redirect(Jump jump) {
106 Jump newJump = labelRedirects[jump.target]; 117 Jump newJump = labelRedirects[jump.target];
107 return newJump != null ? newJump : jump; 118 return newJump != null ? newJump : jump;
108 } 119 }
109 120
110
111 rewriteExecutableDefinition(ExecutableDefinition definition) { 121 rewriteExecutableDefinition(ExecutableDefinition definition) {
112 definition.body = rewriteInEmptyEnvironment(definition.body); 122 inEmptyEnvironment(() {
123 definition.body = visitStatement(definition.body);
124 });
113 } 125 }
114 126
115 void rewriteConstructorDefinition(ConstructorDefinition definition) { 127 void rewriteConstructorDefinition(ConstructorDefinition definition) {
116 if (definition.isAbstract) return; 128 if (definition.isAbstract) return;
117 definition.initializers.forEach(visitExpression); 129 definition.initializers.forEach(visitExpression);
118 rewriteExecutableDefinition(definition); 130 rewriteExecutableDefinition(definition);
119 } 131 }
120 132
121 Statement rewriteInEmptyEnvironment(Statement body) { 133 void inEmptyEnvironment(void action()) {
122 List<Assign> oldEnvironment = environment; 134 List<Assign> oldEnvironment = environment;
123 environment = <Assign>[]; 135 environment = <Assign>[];
124 136 action();
125 Statement result = visitStatement(body);
126 // TODO(kmillikin): Allow definitions that are not propagated. Here,
127 // this means rebuilding the binding with a recursively unnamed definition,
128 // or else introducing a variable definition and an assignment.
129 assert(environment.isEmpty); 137 assert(environment.isEmpty);
130 environment = oldEnvironment; 138 environment = oldEnvironment;
131 return result;
132 } 139 }
133 140
134 Expression visitFieldInitializer(FieldInitializer node) { 141 Expression visitFieldInitializer(FieldInitializer node) {
135 node.body = rewriteInEmptyEnvironment(node.body); 142 inEmptyEnvironment(() {
143 node.body = visitStatement(node.body);
144 });
136 return node; 145 return node;
137 } 146 }
138 147
139 Expression visitSuperInitializer(SuperInitializer node) { 148 Expression visitSuperInitializer(SuperInitializer node) {
140 for (int i = node.arguments.length - 1; i >= 0; --i) { 149 inEmptyEnvironment(() {
141 node.arguments[i] = rewriteInEmptyEnvironment(node.arguments[i]); 150 for (int i = node.arguments.length - 1; i >= 0; --i) {
142 } 151 node.arguments[i] = visitStatement(node.arguments[i]);
152 assert(environment.isEmpty);
153 }
154 });
143 return node; 155 return node;
144 } 156 }
145 157
146 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this); 158 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this);
147 159
148 Expression visitVariable(Variable node) { 160 Expression visitVariable(Variable node) {
161 // Propagate constant to use site.
162 Expression constant = constantEnvironment[node];
163 if (constant != null) return constant;
164
149 // Propagate a variable's definition to its use site if: 165 // Propagate a variable's definition to its use site if:
150 // 1. It has a single use, to avoid code growth and potential duplication 166 // 1. It has a single use, to avoid code growth and potential duplication
151 // of side effects, AND 167 // of side effects, AND
152 // 2. It was the most recent expression evaluated so that we do not 168 // 2. It was the most recent expression evaluated so that we do not
153 // reorder expressions with side effects. 169 // reorder expressions with side effects.
154 if (!environment.isEmpty && 170 if (!environment.isEmpty &&
155 environment.last.variable == node && 171 environment.last.variable == node &&
156 environment.last.hasExactlyOneUse) { 172 environment.last.hasExactlyOneUse) {
157 return visitExpression(environment.removeLast().definition); 173 return visitExpression(environment.removeLast().definition);
158 } 174 }
159 // If the definition could not be propagated, leave the variable use. 175 // If the definition could not be propagated, leave the variable use.
160 return node; 176 return node;
161 } 177 }
162 178
179 /// Returns true if [exp] has no side effects and has a constant value within
180 /// any given activation of the enclosing method.
181 bool isEffectivelyConstant(Expression exp) {
182 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional
183 // expressions recursively. Determine if that is a valuable optimization
184 // and/or if it is better handled at the CPS level.
185 return exp is Constant ||
186 exp is This ||
187 exp is ReifyTypeVar ||
188 exp is Variable && constantEnvironment.containsKey(exp);
189 }
163 190
164 Statement visitAssign(Assign node) { 191 Statement visitAssign(Assign node) {
165 environment.add(node); 192 if (isEffectivelyConstant(node.definition)) {
166 Statement next = visitStatement(node.next); 193 // Handle constant assignments specially.
167 194 // They are always safe to propagate (though we should avoid duplication).
168 if (!environment.isEmpty && environment.last == node) { 195 // Moreover, they should not prevent other expressions from propagating.
169 // The definition could not be propagated. Residualize the let binding. 196 if (node.variable.readCount <= 1 && node.variable.writeCount == 1) {
170 node.next = next; 197 // A single-use constant should always be propagted to its use site.
171 environment.removeLast(); 198 constantEnvironment[node.variable] = visitExpression(node.definition);
172 node.definition = visitExpression(node.definition); 199 return visitStatement(node.next);
173 return node; 200 } else {
201 // With more than one use, we cannot propagate the constant.
202 // Visit the following statement without polluting [environment] so
203 // the previous assignment might still propagate.
Kevin Millikin (Google) 2015/02/12 11:52:51 Maybe "previous non-constant assignment" or "immed
asgerf 2015/02/12 12:31:14 Rephrased a bit.
204 node.next = visitStatement(node.next);
205 node.definition = visitExpression(node.definition);
206 return node;
207 }
208 } else {
209 // Try to propagate assignment, and block previous assignment until this
210 // has propagated.
211 environment.add(node);
212 Statement next = visitStatement(node.next);
213 if (!environment.isEmpty && environment.last == node) {
214 // The definition could not be propagated. Residualize the let binding.
215 node.next = next;
216 environment.removeLast();
217 node.definition = visitExpression(node.definition);
218 return node;
219 }
220 assert(!environment.contains(node));
221 return next;
174 } 222 }
175 assert(!environment.contains(node));
176 return next;
177 } 223 }
178 224
179 Expression visitInvokeStatic(InvokeStatic node) { 225 Expression visitInvokeStatic(InvokeStatic node) {
180 // Process arguments right-to-left, the opposite of evaluation order. 226 // Process arguments right-to-left, the opposite of evaluation order.
181 for (int i = node.arguments.length - 1; i >= 0; --i) { 227 for (int i = node.arguments.length - 1; i >= 0; --i) {
182 node.arguments[i] = visitExpression(node.arguments[i]); 228 node.arguments[i] = visitExpression(node.arguments[i]);
183 } 229 }
184 return node; 230 return node;
185 } 231 }
186 232
(...skipping 23 matching lines...) Expand all
210 Expression visitConcatenateStrings(ConcatenateStrings node) { 256 Expression visitConcatenateStrings(ConcatenateStrings node) {
211 for (int i = node.arguments.length - 1; i >= 0; --i) { 257 for (int i = node.arguments.length - 1; i >= 0; --i) {
212 node.arguments[i] = visitExpression(node.arguments[i]); 258 node.arguments[i] = visitExpression(node.arguments[i]);
213 } 259 }
214 return node; 260 return node;
215 } 261 }
216 262
217 Expression visitConditional(Conditional node) { 263 Expression visitConditional(Conditional node) {
218 node.condition = visitExpression(node.condition); 264 node.condition = visitExpression(node.condition);
219 265
220 List<Assign> savedEnvironment = environment; 266 inEmptyEnvironment(() {
221 environment = <Assign>[]; 267 node.thenExpression = visitExpression(node.thenExpression);
222 node.thenExpression = visitExpression(node.thenExpression); 268 node.elseExpression = visitExpression(node.elseExpression);
223 assert(environment.isEmpty); 269 });
224 node.elseExpression = visitExpression(node.elseExpression);
225 assert(environment.isEmpty);
226 environment = savedEnvironment;
227 270
228 return node; 271 return node;
229 } 272 }
230 273
231 Expression visitLogicalOperator(LogicalOperator node) { 274 Expression visitLogicalOperator(LogicalOperator node) {
232 node.left = visitExpression(node.left); 275 node.left = visitExpression(node.left);
233 276
234 environment.add(null); // impure expressions may not propagate across branch 277 // Impure expressions may not propagate across the branch.
235 node.right = visitExpression(node.right); 278 inEmptyEnvironment(() {
236 environment.removeLast(); 279 node.right = visitExpression(node.right);
280 });
237 281
238 return node; 282 return node;
239 } 283 }
240 284
241 Expression visitNot(Not node) { 285 Expression visitNot(Not node) {
242 node.operand = visitExpression(node.operand); 286 node.operand = visitExpression(node.operand);
243 return node; 287 return node;
244 } 288 }
245 289
246 Expression visitFunctionExpression(FunctionExpression node) { 290 Expression visitFunctionExpression(FunctionExpression node) {
247 new StatementRewriter().rewrite(node.definition); 291 new StatementRewriter.nested(this).rewrite(node.definition);
248 return node; 292 return node;
249 } 293 }
250 294
251 Statement visitFunctionDeclaration(FunctionDeclaration node) { 295 Statement visitFunctionDeclaration(FunctionDeclaration node) {
252 new StatementRewriter().rewrite(node.definition); 296 new StatementRewriter.nested(this).rewrite(node.definition);
253 node.next = visitStatement(node.next); 297 node.next = visitStatement(node.next);
254 return node; 298 return node;
255 } 299 }
256 300
257 Statement visitReturn(Return node) { 301 Statement visitReturn(Return node) {
258 node.value = visitExpression(node.value); 302 node.value = visitExpression(node.value);
259 return node; 303 return node;
260 } 304 }
261 305
262 306
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 338
295 node.body = visitStatement(node.body); 339 node.body = visitStatement(node.body);
296 340
297 if (node.label.useCount == 0) { 341 if (node.label.useCount == 0) {
298 // Eliminate the label if next was inlined at a break 342 // Eliminate the label if next was inlined at a break
299 return node.body; 343 return node.body;
300 } 344 }
301 345
302 // Do not propagate assignments into the successor statements, since they 346 // Do not propagate assignments into the successor statements, since they
303 // may be overwritten by assignments in the body. 347 // may be overwritten by assignments in the body.
304 List<Assign> savedEnvironment = environment; 348 inEmptyEnvironment(() {
305 environment = <Assign>[]; 349 node.next = visitStatement(node.next);
306 node.next = visitStatement(node.next); 350 });
307 environment = savedEnvironment;
308 351
309 return node; 352 return node;
310 } 353 }
311 354
312 Statement visitIf(If node) { 355 Statement visitIf(If node) {
313 node.condition = visitExpression(node.condition); 356 node.condition = visitExpression(node.condition);
314 357
315 // Do not propagate assignments into branches. Doing so will lead to code 358 // Do not propagate assignments into branches. Doing so will lead to code
316 // duplication. 359 // duplication.
317 // TODO(kmillikin): Rethink this. Propagating some assignments (e.g., 360 // TODO(kmillikin): Rethink this. Propagating some assignments
318 // constants or variables) is benign. If they can occur here, they should 361 // (e.g. variables) is benign. If they can occur here, they should
319 // be handled well. 362 // be handled well.
320 List<Assign> savedEnvironment = environment; 363 inEmptyEnvironment(() {
321 environment = <Assign>[]; 364 node.thenStatement = visitStatement(node.thenStatement);
322 node.thenStatement = visitStatement(node.thenStatement); 365 node.elseStatement = visitStatement(node.elseStatement);
323 assert(environment.isEmpty); 366 });
324 node.elseStatement = visitStatement(node.elseStatement);
325 assert(environment.isEmpty);
326 environment = savedEnvironment;
327 367
328 tryCollapseIf(node); 368 tryCollapseIf(node);
329 369
330 Statement reduced = combineStatementsWithSubexpressions( 370 Statement reduced = combineStatementsWithSubexpressions(
331 node.thenStatement, 371 node.thenStatement,
332 node.elseStatement, 372 node.elseStatement,
333 (t,f) => new Conditional(node.condition, t, f)..processed = true); 373 (t,f) => new Conditional(node.condition, t, f)..processed = true);
334 if (reduced != null) { 374 if (reduced != null) {
335 if (reduced.next is Break) { 375 if (reduced.next is Break) {
336 // In case the break can now be inlined. 376 // In case the break can now be inlined.
337 reduced = visitStatement(reduced); 377 reduced = visitStatement(reduced);
338 } 378 }
339 return reduced; 379 return reduced;
340 } 380 }
341 381
342 return node; 382 return node;
343 } 383 }
344 384
345 Statement visitWhileTrue(WhileTrue node) { 385 Statement visitWhileTrue(WhileTrue node) {
346 // Do not propagate assignments into loops. Doing so is not safe for 386 // Do not propagate assignments into loops. Doing so is not safe for
347 // variables modified in the loop (the initial value will be propagated). 387 // variables modified in the loop (the initial value will be propagated).
348 List<Assign> savedEnvironment = environment; 388 inEmptyEnvironment(() {
349 environment = <Assign>[]; 389 node.body = visitStatement(node.body);
350 node.body = visitStatement(node.body); 390 });
351 assert(environment.isEmpty);
352 environment = savedEnvironment;
353 return node; 391 return node;
354 } 392 }
355 393
356 Statement visitWhileCondition(WhileCondition node) { 394 Statement visitWhileCondition(WhileCondition node) {
357 // Not introduced yet 395 // Not introduced yet
358 throw "Unexpected WhileCondition in StatementRewriter"; 396 throw "Unexpected WhileCondition in StatementRewriter";
359 } 397 }
360 398
361 Expression visitConstant(Constant node) { 399 Expression visitConstant(Constant node) {
362 return node; 400 return node;
(...skipping 26 matching lines...) Expand all
389 427
390 Expression visitTypeOperator(TypeOperator node) { 428 Expression visitTypeOperator(TypeOperator node) {
391 node.receiver = visitExpression(node.receiver); 429 node.receiver = visitExpression(node.receiver);
392 return node; 430 return node;
393 } 431 }
394 432
395 Statement visitExpressionStatement(ExpressionStatement node) { 433 Statement visitExpressionStatement(ExpressionStatement node) {
396 node.expression = visitExpression(node.expression); 434 node.expression = visitExpression(node.expression);
397 // Do not allow propagation of assignments past an expression evaluated 435 // Do not allow propagation of assignments past an expression evaluated
398 // for its side effects because it risks reordering side effects. 436 // for its side effects because it risks reordering side effects.
399 // TODO(kmillikin): Rethink this. Some propagation is benign, e.g., 437 // TODO(kmillikin): Rethink this. Some propagation is benign,
400 // constants, variables, or other pure values that are not destroyed by 438 // e.g. variables, or other pure values that are not destroyed by
401 // the expression statement. If they can occur here they should be 439 // the expression statement. If they can occur here they should be
402 // handled well. 440 // handled well.
403 List<Assign> savedEnvironment = environment; 441 inEmptyEnvironment(() {
404 environment = <Assign>[]; 442 node.next = visitStatement(node.next);
405 node.next = visitStatement(node.next); 443 });
406 assert(environment.isEmpty);
407 environment = savedEnvironment;
408 return node; 444 return node;
409 } 445 }
410 446
411 Statement visitSetField(SetField node) { 447 Statement visitSetField(SetField node) {
412 node.next = visitStatement(node.next); 448 node.next = visitStatement(node.next);
413 node.value = visitExpression(node.value); 449 node.value = visitExpression(node.value);
414 node.object = visitExpression(node.object); 450 node.object = visitExpression(node.object);
415 return node; 451 return node;
416 } 452 }
417 453
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 if (innerElse is Break && innerElse.target == outerElse.target) { 606 if (innerElse is Break && innerElse.target == outerElse.target) {
571 // We always put S in the then branch of the result, and adjust the 607 // We always put S in the then branch of the result, and adjust the
572 // condition expression if S was actually found in the else branch(es). 608 // condition expression if S was actually found in the else branch(es).
573 outerIf.condition = new LogicalOperator.and( 609 outerIf.condition = new LogicalOperator.and(
574 makeCondition(outerIf.condition, branch1), 610 makeCondition(outerIf.condition, branch1),
575 makeCondition(innerIf.condition, branch2)); 611 makeCondition(innerIf.condition, branch2));
576 outerIf.thenStatement = innerThen; 612 outerIf.thenStatement = innerThen;
577 --innerElse.target.useCount; 613 --innerElse.target.useCount;
578 614
579 // Try to inline the remaining break. Do not propagate assignments. 615 // Try to inline the remaining break. Do not propagate assignments.
580 List<Assign> savedEnvironment = environment; 616 inEmptyEnvironment(() {
581 environment = <Assign>[]; 617 outerIf.elseStatement = visitStatement(outerElse);
582 outerIf.elseStatement = visitStatement(outerElse); 618 });
583 assert(environment.isEmpty);
584 environment = savedEnvironment;
585 619
586 return outerIf.elseStatement is If && innerThen is Break; 620 return outerIf.elseStatement is If && innerThen is Break;
587 } 621 }
588 } 622 }
589 return false; 623 return false;
590 } 624 }
591 625
592 Expression makeCondition(Expression e, bool polarity) { 626 Expression makeCondition(Expression e, bool polarity) {
593 return polarity ? e : new Not(e); 627 return polarity ? e : new Not(e);
594 } 628 }
595 629
596 Statement getBranch(If node, bool polarity) { 630 Statement getBranch(If node, bool polarity) {
597 return polarity ? node.thenStatement : node.elseStatement; 631 return polarity ? node.thenStatement : node.elseStatement;
598 } 632 }
599 } 633 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart ('k') | pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698