| 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..b44ca6dfb819005b7d894da1825c9bae9cbd7607 100644
|
| --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
|
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
|
| @@ -116,9 +116,6 @@ class StatementRewriter extends Transformer implements Pass {
|
| node.body = visitStatement(node.body);
|
| }
|
|
|
| - /// True if targeting Dart.
|
| - final bool isDartMode;
|
| -
|
| /// The most recently evaluated impure expressions, with the most recent
|
| /// expression being last.
|
| ///
|
| @@ -127,6 +124,9 @@ class StatementRewriter extends Transformer implements Pass {
|
| /// we can propagate to a variable use if they are known to return the value
|
| /// of that variable.
|
| ///
|
| + /// Assignments with constant right-hand sides (see [isEffectivelyConstant])
|
| + /// are not considered impure and are put in [constantEnvironment] instead.
|
| + ///
|
| /// Except for [Conditional]s, expressions in the environment have
|
| /// not been processed, and all their subexpressions must therefore be
|
| /// variables uses.
|
| @@ -146,16 +146,12 @@ class StatementRewriter extends Transformer implements Pass {
|
| Map<Variable, int> unseenUses = <Variable, int>{};
|
|
|
| /// Rewriter for methods.
|
| - StatementRewriter({this.isDartMode})
|
| - : constantEnvironment = <Variable, Expression>{} {
|
| - assert(isDartMode != null);
|
| - }
|
| + StatementRewriter() : constantEnvironment = <Variable, Expression>{};
|
|
|
| /// Rewriter for nested functions.
|
| StatementRewriter.nested(StatementRewriter parent)
|
| : constantEnvironment = parent.constantEnvironment,
|
| - unseenUses = parent.unseenUses,
|
| - isDartMode = parent.isDartMode;
|
| + unseenUses = parent.unseenUses;
|
|
|
| /// A set of labels that can be safely inlined at their use.
|
| ///
|
| @@ -188,11 +184,6 @@ class StatementRewriter extends Transformer implements Pass {
|
| /// If the given expression always returns the value of one of its
|
| /// subexpressions, returns that subexpression, otherwise `null`.
|
| Expression getValueSubexpression(Expression e) {
|
| - if (isDartMode &&
|
| - e is InvokeMethod &&
|
| - (e.selector.isSetter || e.selector.isIndexSet)) {
|
| - return e.arguments.last;
|
| - }
|
| if (e is SetField) return e.value;
|
| return null;
|
| }
|
| @@ -264,6 +255,29 @@ class StatementRewriter extends Transformer implements Pass {
|
| return node;
|
| }
|
|
|
| + /// True if [exp] contains a use of a variable that was assigned to by the
|
| + /// most recently evaluated impure expression (constant assignments are not
|
| + /// considered impure).
|
| + ///
|
| + /// 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].
|
| + ///
|
| + /// [exp] must be an unprocessed expression, i.e. either a [Conditional] or
|
| + /// an expression whose subexpressions are all variable uses.
|
| + 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 +288,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 +300,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 +635,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 +1013,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
|
| +/// 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) {}
|
| +}
|
|
|