Chromium Code Reviews| Index: pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| index 27638c40409bb44c24ab51d8054a0529a0fad51f..e7a888b8c357a3d90e4f81a8d63d56fd11b94c67 100644 |
| --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| @@ -264,6 +264,25 @@ class StatementRewriter extends Transformer implements Pass { |
| return node; |
| } |
| + /// 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"
|
| + /// most recently evaluated impure expression. |
| + /// |
| + /// This implies that the assignment can be propagated into this use unless |
| + /// the use is moved further away. |
| + /// |
| + /// In this case, we will refrain from moving [exp] across other impure |
| + /// expressions, even when this is safe, because doing so would immediately |
| + /// prevent the previous expression from propagating, canceling out the |
| + /// benefit we might otherwise gain from propagating [exp]. |
| + bool usesRecentlyAssignedVariable(Expression exp) { |
| + if (environment.isEmpty) return false; |
| + Variable variable = getLeftHand(environment.last); |
| + if (variable == null) return false; |
| + IsVariableUsedVisitor visitor = new IsVariableUsedVisitor(variable); |
| + visitor.visitExpression(exp); |
| + return visitor.wasFound; |
| + } |
| + |
| /// Returns true if [exp] has no side effects and has a constant value within |
| /// any given activation of the enclosing method. |
| bool isEffectivelyConstant(Expression exp) { |
| @@ -274,6 +293,7 @@ class StatementRewriter extends Transformer implements Pass { |
| exp is This || |
| exp is CreateInvocationMirror || |
| exp is InvokeStatic && exp.isEffectivelyConstant || |
| + exp is ApplyBuiltinOperator || |
| exp is VariableUse && constantEnvironment.containsKey(exp.variable); |
| } |
| @@ -285,7 +305,8 @@ class StatementRewriter extends Transformer implements Pass { |
| } |
| Statement visitExpressionStatement(ExpressionStatement stmt) { |
| - if (isEffectivelyConstantAssignment(stmt.expression)) { |
| + if (isEffectivelyConstantAssignment(stmt.expression) && |
| + !usesRecentlyAssignedVariable(stmt.expression)) { |
| Assign assign = stmt.expression; |
| // Handle constant assignments specially. |
| // They are always safe to propagate (though we should avoid duplication). |
| @@ -619,6 +640,80 @@ class StatementRewriter extends Transformer implements Pass { |
| return node; |
| } |
| + /// True if [operator] is a binary operator that always has the same value |
| + /// if its arguments are swapped. |
| + bool isSymmetricOperator(BuiltinOperator operator) { |
| + switch (operator) { |
| + case BuiltinOperator.StrictEq: |
| + case BuiltinOperator.StrictNeq: |
| + case BuiltinOperator.LooseEq: |
| + case BuiltinOperator.LooseNeq: |
| + case BuiltinOperator.NumAnd: |
| + case BuiltinOperator.NumOr: |
| + case BuiltinOperator.NumXor: |
| + case BuiltinOperator.NumAdd: |
| + case BuiltinOperator.NumMultiply: |
| + return true; |
| + default: |
| + return false; |
| + } |
| + } |
| + |
| + /// If [operator] is a commutable binary operator, returns the commuted |
| + /// operator, possibly the operator itself, otherwise returns `null`. |
| + BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) { |
| + if (isSymmetricOperator(operator)) { |
| + // Symmetric operators are their own commutes. |
| + return operator; |
| + } |
| + switch(operator) { |
| + case BuiltinOperator.NumLt: return BuiltinOperator.NumGt; |
| + case BuiltinOperator.NumLe: return BuiltinOperator.NumGe; |
| + case BuiltinOperator.NumGt: return BuiltinOperator.NumLt; |
| + case BuiltinOperator.NumGe: return BuiltinOperator.NumLe; |
| + default: return null; |
| + } |
| + } |
| + |
| + /// Built-in binary operators are commuted when it is safe and can enable an |
| + /// assignment propagation. For example: |
| + /// |
| + /// var x = foo(); |
| + /// var y = bar(); |
| + /// var z = y < x; |
| + /// |
| + /// ==> |
| + /// |
| + /// var z = foo() > bar(); |
| + /// |
| + /// foo() must be evaluated before bar(), so the propagation is only possible |
| + /// by commuting the operator. |
| + Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| + if (environment.isEmpty || getLeftHand(environment.last) == null) { |
| + // If there is no recent assignment that might propagate, so there is no |
| + // opportunity for optimization here. |
| + _rewriteList(node.arguments); |
| + return node; |
| + } |
| + Variable propagatableVariable = getLeftHand(environment.last); |
| + BuiltinOperator commuted = commuteBinaryOperator(node.operator); |
| + if (commuted != null) { |
| + assert(node.arguments.length == 2); // Only binary operators can commute. |
| + VariableUse arg1 = node.arguments[0]; |
| + VariableUse arg2 = node.arguments[1]; |
| + if (propagatableVariable == arg1.variable && |
| + propagatableVariable != arg2.variable && |
| + !constantEnvironment.containsKey(arg2.variable)) { |
| + // An assignment can be propagated if we commute the operator. |
| + node.operator = commuted; |
| + node.arguments[0] = arg2; |
| + node.arguments[1] = arg1; |
| + } |
| + } |
| + _rewriteList(node.arguments); |
| + return node; |
| + } |
| + |
| /// If [s] and [t] are similar statements we extract their subexpressions |
| /// and returns a new statement of the same type using expressions combined |
| /// with the [combine] callback. For example: |
| @@ -923,3 +1018,23 @@ class GenericCombinedExpressions implements CombinedExpressions { |
| void uncombine() {} |
| } |
| + |
| +/// Looks for uses of a specific variable. |
| +/// |
| +/// 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.
|
| +/// sub-expressions are known to be variable uses, so there is no risk of |
| +/// explosive reprocessing. |
| +class IsVariableUsedVisitor extends RecursiveVisitor { |
| + Variable variable; |
| + bool wasFound = false; |
| + |
| + IsVariableUsedVisitor(this.variable); |
| + |
| + visitVariableUse(VariableUse node) { |
| + if (node.variable == variable) { |
| + wasFound = true; |
| + } |
| + } |
| + |
| + visitInnerFunction(FunctionDefinition node) {} |
| +} |