| 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..8208a916e6593761f03f6d415c2b14f0799a212a 100644
|
| --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
|
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
|
| @@ -619,6 +619,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.NumPlus:
|
| + 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:
|
|
|