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 |