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 1c96301711577753d98346431fcc4e8fb7d3cc06..63206914a1a046f15a2d4fc7dacff7b96e81cab6 100644 |
| --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart |
| @@ -752,16 +752,26 @@ class StatementRewriter extends Transformer implements Pass { |
| return node; |
| } |
| - bool sameVariable(Expression e1, Expression e2) { |
| - return e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable; |
| - } |
| - |
| bool isCompoundableBuiltin(Expression e) { |
| return e is ApplyBuiltinOperator && |
| - e.arguments.length == 2 && |
| + e.arguments.length >= 2 && |
| isCompoundableOperator(e.operator); |
| } |
| + /// Converts a compoundable operator application into the right-hand side for |
| + /// use in a compound assignment, discarding the left-hand value. |
| + /// |
| + /// For example, for `x + y + z` it returns `y + z`. |
| + Expression contractCompoundableBuiltin(ApplyBuiltinOperator e) { |
| + assert(isCompoundableBuiltin(e)); |
| + if (e.arguments.length > 2) { |
| + assert(e.operator == BuiltinOperator.StringConcatenate); |
| + return new ApplyBuiltinOperator(e.operator, e.arguments.skip(1).toList()); |
| + } else { |
| + return e.arguments[1]; |
| + } |
| + } |
| + |
| void destroyVariableUse(VariableUse node) { |
| --node.variable.readCount; |
| } |
| @@ -772,13 +782,12 @@ class StatementRewriter extends Transformer implements Pass { |
| if (isCompoundableBuiltin(node.value)) { |
| ApplyBuiltinOperator rhs = node.value; |
| Expression left = rhs.arguments[0]; |
| - Expression right = rhs.arguments[1]; |
| if (left is GetField && |
| left.field == node.field && |
| - sameVariable(left.object, node.object)) { |
| - destroyVariableUse(left.object); |
| + samePrimary(left.object, node.object)) { |
| + destroyPrimaryExpression(left.object); |
| node.compound = rhs.operator; |
| - node.value = right; |
| + node.value = contractCompoundableBuiltin(rhs); |
| } |
| } |
| node.object = visitExpression(node.object); |
| @@ -798,6 +807,16 @@ class StatementRewriter extends Transformer implements Pass { |
| Expression visitSetStatic(SetStatic node) { |
| allowRhsPropagation.add(true); |
| node.value = visitExpression(node.value); |
| + if (isCompoundableBuiltin(node.value)) { |
| + ApplyBuiltinOperator rhs = node.value; |
| + Expression left = rhs.arguments[0]; |
| + if (left is GetStatic && |
| + left.element == node.element && |
| + !left.useLazyGetter) { |
| + node.compound = rhs.operator; |
| + node.value = contractCompoundableBuiltin(rhs); |
| + } |
| + } |
| allowRhsPropagation.removeLast(); |
| return node; |
| } |
| @@ -857,14 +876,13 @@ class StatementRewriter extends Transformer implements Pass { |
| if (isCompoundableBuiltin(node.value)) { |
| ApplyBuiltinOperator rhs = node.value; |
| Expression left = rhs.arguments[0]; |
| - Expression right = rhs.arguments[1]; |
| if (left is GetIndex && |
| - sameVariable(left.object, node.object) && |
| - sameVariable(left.index, node.index)) { |
| - destroyVariableUse(left.object); |
| - destroyVariableUse(left.index); |
| + samePrimary(left.object, node.object) && |
| + samePrimary(left.index, node.index)) { |
| + destroyPrimaryExpression(left.object); |
| + destroyPrimaryExpression(left.index); |
| node.compound = rhs.operator; |
| - node.value = right; |
| + node.value = contractCompoundableBuiltin(rhs); |
| } |
| } |
| node.index = visitExpression(node.index); |
| @@ -1036,9 +1054,34 @@ class StatementRewriter extends Transformer implements Pass { |
| Expression s, |
| Expression t, |
| Expression condition) { |
| - if (s is Assign && t is Assign && s.variable == t.variable) { |
| - Expression values = new Conditional(condition, s.value, t.value); |
| - return new CombinedAssigns(s, t, new CombinedExpressions(values)); |
| + if (s is Assign && t is Assign && |
| + s.variable == t.variable) { |
| + return new CombinedAssigns(s, t, |
| + combineAsConditional(s.value, t.value, condition)); |
| + } |
| + if (s is SetStatic && |
| + t is SetStatic && |
| + s.element == t.element) { |
| + return new CombinedSetStatics(s, t, |
| + combineAsConditional(s.value, t.value, condition)); |
| + } |
| + if (s is SetField && |
| + t is SetField && |
| + s.compound == null && |
| + t.compound == null && |
| + s.field == t.field && |
| + samePrimary(s.object, t.object)) { |
| + return new CombinedSetFields(s, t, |
| + combineAsConditional(s.value, t.value, condition)); |
| + } |
| + if (s is SetIndex && |
| + t is SetIndex && |
| + s.compound == null && |
| + t.compound == null && |
| + samePrimary(s.object, t.object) && |
| + samePrimary(s.index, t.index)) { |
| + return new CombinedSetIndexes(s, t, |
| + combineAsConditional(s.value, t.value, condition)); |
| } |
| return new CombinedExpressions(new Conditional(condition, s, t)); |
| } |
| @@ -1281,12 +1324,12 @@ abstract class CombinedExpressions { |
| factory CombinedExpressions(Expression e) = GenericCombinedExpressions; |
| } |
| -/// Combines assignments of form `[variable] := E1` and `[variable] := E2` into |
| -/// a single assignment of form `[variable] := combine(E1, E2)`. |
| +/// Combines assignments of form `v := E1` and `v := E2` into |
| +/// a single assignment of form `v := combine(E1, E2)`. |
|
sra1
2016/01/22 03:22:04
We probably want to avoid this where one arm is a
asgerf
2016/01/22 18:54:47
Good point. I looked at the IR generated by V8 in
|
| class CombinedAssigns implements CombinedExpressions { |
| Assign assign1, assign2; |
| CombinedExpressions value; |
| - Expression combined; |
| + Assign combined; |
| CombinedAssigns(this.assign1, this.assign2, this.value) { |
| assert(assign1.variable == assign2.variable); |
| @@ -1300,10 +1343,74 @@ class CombinedAssigns implements CombinedExpressions { |
| } |
| } |
| +/// Combines `static = E1` and `static = E2` into `static = combine(E1, E2)`. |
| +class CombinedSetStatics implements CombinedExpressions { |
| + SetStatic assign1, assign2; |
| + CombinedExpressions value; |
| + SetStatic combined; |
| + |
| + CombinedSetStatics(this.assign1, this.assign2, this.value) { |
| + assert(assign1.element == assign2.element); |
| + // TODO(asgerf): Can we combine source information from the two assignments? |
| + combined = new SetStatic(assign1.element, value.combined, |
| + assign1.sourceInformation); |
| + } |
| + |
| + void uncombine() { |
| + value.uncombine(); |
| + } |
| +} |
| + |
| +/// Combines `v.field = E1` and `v.field = E2` into `v.field = combine(E1, E2)`. |
| +class CombinedSetFields implements CombinedExpressions { |
| + SetField assign1, assign2; |
| + CombinedExpressions value; |
| + SetField combined; |
| + |
| + CombinedSetFields(this.assign1, this.assign2, this.value) { |
| + assert(samePrimary(assign1.object, assign2.object)); |
| + assert(assign1.field == assign2.field); |
| + assert(assign1.compound == null); |
| + assert(assign2.compound == null); |
| + destroyPrimaryExpression(assign2.object); |
| + combined = new SetField(assign1.object, assign1.field, value.combined); |
| + } |
| + |
| + void uncombine() { |
| + value.uncombine(); |
| + restorePrimaryExpression(assign2.object); |
| + } |
| +} |
| + |
| +/// Combines `v[index] = E1` and `v[index] = E2` into |
| +/// `v[index] = combine(E1, E2)`. |
| +class CombinedSetIndexes implements CombinedExpressions { |
| + SetIndex assign1, assign2; |
| + CombinedExpressions value; |
| + SetIndex combined; |
| + |
| + CombinedSetIndexes(this.assign1, this.assign2, this.value) { |
| + assert(samePrimary(assign1.object, assign2.object)); |
| + assert(samePrimary(assign1.index, assign2.index)); |
| + assert(assign1.compound == null); |
| + assert(assign2.compound == null); |
| + destroyPrimaryExpression(assign2.object); |
| + destroyPrimaryExpression(assign2.index); |
| + combined = new SetIndex(assign1.object, assign1.index, value.combined, |
| + compound: assign1.compound); |
| + } |
| + |
| + void uncombine() { |
| + value.uncombine(); |
| + restorePrimaryExpression(assign2.object); |
| + restorePrimaryExpression(assign2.index); |
| + } |
| +} |
| + |
| /// Combines two variable uses into one. |
| class CombinedUses implements CombinedExpressions { |
| VariableUse use1, use2; |
| - Expression combined; |
| + VariableUse combined; |
| CombinedUses(this.use1, this.use2) { |
| assert(use1.variable == use2.variable); |
| @@ -1356,3 +1463,31 @@ class VariableUseVisitor extends RecursiveVisitor { |
| new VariableUseVisitor(callback).visitExpression(node); |
| } |
| } |
| + |
| +bool sameVariable(Expression e1, Expression e2) { |
| + return e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable; |
| +} |
| + |
| +/// True if [e1] and [e2] are primary expressions (expressions without |
| +/// subexpressions) with the same value. |
| +bool samePrimary(Expression e1, Expression e2) { |
| + return sameVariable(e1, e2) || (e1 is This && e2 is This); |
| +} |
| + |
| +/// Decrement the reference count for [e] if it is a variable use. |
| +void destroyPrimaryExpression(Expression e) { |
| + if (e is VariableUse) { |
| + --e.variable.readCount; |
| + } else { |
| + assert(e is This); |
| + } |
| +} |
| + |
| +/// Increment the reference count for [e] if it is a variable use. |
| +void restorePrimaryExpression(Expression e) { |
| + if (e is VariableUse) { |
| + ++e.variable.readCount; |
| + } else { |
| + assert(e is This); |
| + } |
| +} |