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 library tree_ir.optimization.statement_rewriter; | 5 library tree_ir.optimization.statement_rewriter; |
| 6 | 6 |
| 7 import 'optimization.dart' show Pass; | 7 import 'optimization.dart' show Pass; |
| 8 import '../tree_ir_nodes.dart'; | 8 import '../tree_ir_nodes.dart'; |
| 9 | 9 |
| 10 /** | 10 /** |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 257 environment.removeLast(); | 257 environment.removeLast(); |
| 258 --node.variable.readCount; | 258 --node.variable.readCount; |
| 259 return visitExpression(binding); | 259 return visitExpression(binding); |
| 260 } | 260 } |
| 261 } | 261 } |
| 262 | 262 |
| 263 // If the definition could not be propagated, leave the variable use. | 263 // If the definition could not be propagated, leave the variable use. |
| 264 return node; | 264 return node; |
| 265 } | 265 } |
| 266 | 266 |
| 267 /// True if [exp] contains a use of a variable that was assigned to by the | |
|
Kevin Millikin (Google)
2015/06/12 12:19:10
"a variable" ==> "the variable" seems more clear.
asgerf
2015/06/15 09:26:18
Not all expressions assign to a variable, so "the"
| |
| 268 /// most recently evaluated impure expression. | |
| 269 /// | |
| 270 /// This implies that the assignment can be propagated into this use unless | |
| 271 /// the use is moved further away. | |
| 272 /// | |
| 273 /// In this case, we will refrain from moving [exp] across other impure | |
| 274 /// expressions, even when this is safe, because doing so would immediately | |
| 275 /// prevent the previous expression from propagating, canceling out the | |
| 276 /// benefit we might otherwise gain from propagating [exp]. | |
| 277 bool usesRecentlyAssignedVariable(Expression exp) { | |
| 278 if (environment.isEmpty) return false; | |
| 279 Variable variable = getLeftHand(environment.last); | |
| 280 if (variable == null) return false; | |
| 281 IsVariableUsedVisitor visitor = new IsVariableUsedVisitor(variable); | |
| 282 visitor.visitExpression(exp); | |
| 283 return visitor.wasFound; | |
| 284 } | |
| 285 | |
| 267 /// Returns true if [exp] has no side effects and has a constant value within | 286 /// Returns true if [exp] has no side effects and has a constant value within |
| 268 /// any given activation of the enclosing method. | 287 /// any given activation of the enclosing method. |
| 269 bool isEffectivelyConstant(Expression exp) { | 288 bool isEffectivelyConstant(Expression exp) { |
| 270 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional | 289 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional |
| 271 // expressions recursively. Determine if that is a valuable optimization | 290 // expressions recursively. Determine if that is a valuable optimization |
| 272 // and/or if it is better handled at the CPS level. | 291 // and/or if it is better handled at the CPS level. |
| 273 return exp is Constant || | 292 return exp is Constant || |
| 274 exp is This || | 293 exp is This || |
| 275 exp is CreateInvocationMirror || | 294 exp is CreateInvocationMirror || |
| 276 exp is InvokeStatic && exp.isEffectivelyConstant || | 295 exp is InvokeStatic && exp.isEffectivelyConstant || |
| 296 exp is ApplyBuiltinOperator || | |
| 277 exp is VariableUse && constantEnvironment.containsKey(exp.variable); | 297 exp is VariableUse && constantEnvironment.containsKey(exp.variable); |
| 278 } | 298 } |
| 279 | 299 |
| 280 /// True if [node] is an assignment that can be propagated as a constant. | 300 /// True if [node] is an assignment that can be propagated as a constant. |
| 281 bool isEffectivelyConstantAssignment(Expression node) { | 301 bool isEffectivelyConstantAssignment(Expression node) { |
| 282 return node is Assign && | 302 return node is Assign && |
| 283 node.variable.writeCount == 1 && | 303 node.variable.writeCount == 1 && |
| 284 isEffectivelyConstant(node.value); | 304 isEffectivelyConstant(node.value); |
| 285 } | 305 } |
| 286 | 306 |
| 287 Statement visitExpressionStatement(ExpressionStatement stmt) { | 307 Statement visitExpressionStatement(ExpressionStatement stmt) { |
| 288 if (isEffectivelyConstantAssignment(stmt.expression)) { | 308 if (isEffectivelyConstantAssignment(stmt.expression) && |
| 309 !usesRecentlyAssignedVariable(stmt.expression)) { | |
| 289 Assign assign = stmt.expression; | 310 Assign assign = stmt.expression; |
| 290 // Handle constant assignments specially. | 311 // Handle constant assignments specially. |
| 291 // They are always safe to propagate (though we should avoid duplication). | 312 // They are always safe to propagate (though we should avoid duplication). |
| 292 // Moreover, they should not prevent other expressions from propagating. | 313 // Moreover, they should not prevent other expressions from propagating. |
| 293 if (assign.variable.readCount <= 1) { | 314 if (assign.variable.readCount <= 1) { |
| 294 // A single-use constant should always be propagted to its use site. | 315 // A single-use constant should always be propagted to its use site. |
| 295 constantEnvironment[assign.variable] = assign.value; | 316 constantEnvironment[assign.variable] = assign.value; |
| 296 --assign.variable.writeCount; | 317 --assign.variable.writeCount; |
| 297 return visitStatement(stmt.next); | 318 return visitStatement(stmt.next); |
| 298 } else { | 319 } else { |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 612 Expression visitTypeExpression(TypeExpression node) { | 633 Expression visitTypeExpression(TypeExpression node) { |
| 613 _rewriteList(node.arguments); | 634 _rewriteList(node.arguments); |
| 614 return node; | 635 return node; |
| 615 } | 636 } |
| 616 | 637 |
| 617 Expression visitCreateInvocationMirror(CreateInvocationMirror node) { | 638 Expression visitCreateInvocationMirror(CreateInvocationMirror node) { |
| 618 _rewriteList(node.arguments); | 639 _rewriteList(node.arguments); |
| 619 return node; | 640 return node; |
| 620 } | 641 } |
| 621 | 642 |
| 643 /// True if [operator] is a binary operator that always has the same value | |
| 644 /// if its arguments are swapped. | |
| 645 bool isSymmetricOperator(BuiltinOperator operator) { | |
| 646 switch (operator) { | |
| 647 case BuiltinOperator.StrictEq: | |
| 648 case BuiltinOperator.StrictNeq: | |
| 649 case BuiltinOperator.LooseEq: | |
| 650 case BuiltinOperator.LooseNeq: | |
| 651 case BuiltinOperator.NumAnd: | |
| 652 case BuiltinOperator.NumOr: | |
| 653 case BuiltinOperator.NumXor: | |
| 654 case BuiltinOperator.NumAdd: | |
| 655 case BuiltinOperator.NumMultiply: | |
| 656 return true; | |
| 657 default: | |
| 658 return false; | |
| 659 } | |
| 660 } | |
| 661 | |
| 662 /// If [operator] is a commutable binary operator, returns the commuted | |
| 663 /// operator, possibly the operator itself, otherwise returns `null`. | |
| 664 BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) { | |
| 665 if (isSymmetricOperator(operator)) { | |
| 666 // Symmetric operators are their own commutes. | |
| 667 return operator; | |
| 668 } | |
| 669 switch(operator) { | |
| 670 case BuiltinOperator.NumLt: return BuiltinOperator.NumGt; | |
| 671 case BuiltinOperator.NumLe: return BuiltinOperator.NumGe; | |
| 672 case BuiltinOperator.NumGt: return BuiltinOperator.NumLt; | |
| 673 case BuiltinOperator.NumGe: return BuiltinOperator.NumLe; | |
| 674 default: return null; | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 /// Built-in binary operators are commuted when it is safe and can enable an | |
| 679 /// assignment propagation. For example: | |
| 680 /// | |
| 681 /// var x = foo(); | |
| 682 /// var y = bar(); | |
| 683 /// var z = y < x; | |
| 684 /// | |
| 685 /// ==> | |
| 686 /// | |
| 687 /// var z = foo() > bar(); | |
| 688 /// | |
| 689 /// foo() must be evaluated before bar(), so the propagation is only possible | |
| 690 /// by commuting the operator. | |
| 691 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | |
| 692 if (environment.isEmpty || getLeftHand(environment.last) == null) { | |
| 693 // If there is no recent assignment that might propagate, so there is no | |
| 694 // opportunity for optimization here. | |
| 695 _rewriteList(node.arguments); | |
| 696 return node; | |
| 697 } | |
| 698 Variable propagatableVariable = getLeftHand(environment.last); | |
| 699 BuiltinOperator commuted = commuteBinaryOperator(node.operator); | |
| 700 if (commuted != null) { | |
| 701 assert(node.arguments.length == 2); // Only binary operators can commute. | |
| 702 VariableUse arg1 = node.arguments[0]; | |
| 703 VariableUse arg2 = node.arguments[1]; | |
| 704 if (propagatableVariable == arg1.variable && | |
| 705 propagatableVariable != arg2.variable && | |
| 706 !constantEnvironment.containsKey(arg2.variable)) { | |
| 707 // An assignment can be propagated if we commute the operator. | |
| 708 node.operator = commuted; | |
| 709 node.arguments[0] = arg2; | |
| 710 node.arguments[1] = arg1; | |
| 711 } | |
| 712 } | |
| 713 _rewriteList(node.arguments); | |
| 714 return node; | |
| 715 } | |
| 716 | |
| 622 /// If [s] and [t] are similar statements we extract their subexpressions | 717 /// If [s] and [t] are similar statements we extract their subexpressions |
| 623 /// and returns a new statement of the same type using expressions combined | 718 /// and returns a new statement of the same type using expressions combined |
| 624 /// with the [combine] callback. For example: | 719 /// with the [combine] callback. For example: |
| 625 /// | 720 /// |
| 626 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) | 721 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) |
| 627 /// | 722 /// |
| 628 /// If [combine] returns E1 then the unified statement is equivalent to [s], | 723 /// If [combine] returns E1 then the unified statement is equivalent to [s], |
| 629 /// and if [combine] returns E2 the unified statement is equivalence to [t]. | 724 /// and if [combine] returns E2 the unified statement is equivalence to [t]. |
| 630 /// | 725 /// |
| 631 /// It is guaranteed that no side effects occur between the beginning of the | 726 /// It is guaranteed that no side effects occur between the beginning of the |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 916 } | 1011 } |
| 917 | 1012 |
| 918 /// Result of combining two expressions that do not affect reference counting. | 1013 /// Result of combining two expressions that do not affect reference counting. |
| 919 class GenericCombinedExpressions implements CombinedExpressions { | 1014 class GenericCombinedExpressions implements CombinedExpressions { |
| 920 Expression combined; | 1015 Expression combined; |
| 921 | 1016 |
| 922 GenericCombinedExpressions(this.combined); | 1017 GenericCombinedExpressions(this.combined); |
| 923 | 1018 |
| 924 void uncombine() {} | 1019 void uncombine() {} |
| 925 } | 1020 } |
| 1021 | |
| 1022 /// Looks for uses of a specific variable. | |
| 1023 /// | |
| 1024 /// Note that this visitor is only applied to expressions where all | |
|
Kevin Millikin (Google)
2015/06/12 12:19:10
You might mention this as well (that it's cheap) a
asgerf
2015/06/15 09:26:18
Added another comment at the use-site to clarify.
| |
| 1025 /// sub-expressions are known to be variable uses, so there is no risk of | |
| 1026 /// explosive reprocessing. | |
| 1027 class IsVariableUsedVisitor extends RecursiveVisitor { | |
| 1028 Variable variable; | |
| 1029 bool wasFound = false; | |
| 1030 | |
| 1031 IsVariableUsedVisitor(this.variable); | |
| 1032 | |
| 1033 visitVariableUse(VariableUse node) { | |
| 1034 if (node.variable == variable) { | |
| 1035 wasFound = true; | |
| 1036 } | |
| 1037 } | |
| 1038 | |
| 1039 visitInnerFunction(FunctionDefinition node) {} | |
| 1040 } | |
| OLD | NEW |