Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |