| 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 part of tree_ir.optimization; | 5 part of tree_ir.optimization; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Performs the following transformations on the tree: | 8 * Performs the following transformations on the tree: |
| 9 * - Assignment propagation | 9 * - Assignment propagation |
| 10 * - If-to-conditional conversion | 10 * - If-to-conditional conversion |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 * seen, but are only processed once to keep this transformation linear in | 30 * seen, but are only processed once to keep this transformation linear in |
| 31 * the size of the tree. | 31 * the size of the tree. |
| 32 * | 32 * |
| 33 * The transformation builds an environment containing [Assign] bindings that | 33 * The transformation builds an environment containing [Assign] bindings that |
| 34 * are in scope. These bindings have yet-untranslated definitions. When a use | 34 * are in scope. These bindings have yet-untranslated definitions. When a use |
| 35 * is encountered the transformation determines if it is safe and profitable | 35 * is encountered the transformation determines if it is safe and profitable |
| 36 * to propagate the definition to its use. If so, it is removed from the | 36 * to propagate the definition to its use. If so, it is removed from the |
| 37 * environment and the definition is recursively processed (in the | 37 * environment and the definition is recursively processed (in the |
| 38 * new environment at the use site) before being propagated. | 38 * new environment at the use site) before being propagated. |
| 39 * | 39 * |
| 40 * See [visitVariable] for the implementation of the heuristic for propagating | 40 * See [visitVariableUse] for the implementation of the heuristic for |
| 41 * a definition. | 41 * propagating a definition. |
| 42 * | 42 * |
| 43 * | 43 * |
| 44 * IF-TO-CONDITIONAL CONVERSION: | 44 * IF-TO-CONDITIONAL CONVERSION: |
| 45 * If-statement are converted to conditional expressions when possible. | 45 * If-statement are converted to conditional expressions when possible. |
| 46 * For example: | 46 * For example: |
| 47 * | 47 * |
| 48 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L } | 48 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L } |
| 49 * ==> | 49 * ==> |
| 50 * { v1 = v0 ? foo() : bar(); break L } | 50 * { v1 = v0 ? foo() : bar(); break L } |
| 51 * | 51 * |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 for (int i = node.arguments.length - 1; i >= 0; --i) { | 158 for (int i = node.arguments.length - 1; i >= 0; --i) { |
| 159 node.arguments[i] = visitStatement(node.arguments[i]); | 159 node.arguments[i] = visitStatement(node.arguments[i]); |
| 160 assert(environment.isEmpty); | 160 assert(environment.isEmpty); |
| 161 } | 161 } |
| 162 }); | 162 }); |
| 163 return node; | 163 return node; |
| 164 } | 164 } |
| 165 | 165 |
| 166 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this); | 166 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this); |
| 167 | 167 |
| 168 Expression visitVariable(Variable node) { | 168 @override |
| 169 Expression visitVariableUse(VariableUse node) { |
| 169 // Propagate constant to use site. | 170 // Propagate constant to use site. |
| 170 Expression constant = constantEnvironment[node]; | 171 Expression constant = constantEnvironment[node.variable]; |
| 171 if (constant != null) return constant; | 172 if (constant != null) return constant; |
| 172 | 173 |
| 173 // Propagate a variable's definition to its use site if: | 174 // Propagate a variable's definition to its use site if: |
| 174 // 1. It has a single use, to avoid code growth and potential duplication | 175 // 1. It has a single use, to avoid code growth and potential duplication |
| 175 // of side effects, AND | 176 // of side effects, AND |
| 176 // 2. It was the most recent expression evaluated so that we do not | 177 // 2. It was the most recent expression evaluated so that we do not |
| 177 // reorder expressions with side effects. | 178 // reorder expressions with side effects. |
| 178 if (!environment.isEmpty && | 179 if (!environment.isEmpty && |
| 179 environment.last.variable == node && | 180 environment.last.variable == node.variable && |
| 180 environment.last.hasExactlyOneUse) { | 181 environment.last.hasExactlyOneUse) { |
| 181 return visitExpression(environment.removeLast().definition); | 182 return visitExpression(environment.removeLast().definition); |
| 182 } | 183 } |
| 184 |
| 183 // If the definition could not be propagated, leave the variable use. | 185 // If the definition could not be propagated, leave the variable use. |
| 184 return node; | 186 return node; |
| 185 } | 187 } |
| 186 | 188 |
| 187 /// Returns true if [exp] has no side effects and has a constant value within | 189 /// Returns true if [exp] has no side effects and has a constant value within |
| 188 /// any given activation of the enclosing method. | 190 /// any given activation of the enclosing method. |
| 189 bool isEffectivelyConstant(Expression exp) { | 191 bool isEffectivelyConstant(Expression exp) { |
| 190 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional | 192 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional |
| 191 // expressions recursively. Determine if that is a valuable optimization | 193 // expressions recursively. Determine if that is a valuable optimization |
| 192 // and/or if it is better handled at the CPS level. | 194 // and/or if it is better handled at the CPS level. |
| 193 return exp is Constant || | 195 return exp is Constant || |
| 194 exp is This || | 196 exp is This || |
| 195 exp is ReifyTypeVar || | 197 exp is ReifyTypeVar || |
| 196 exp is Variable && constantEnvironment.containsKey(exp); | 198 exp is VariableUse && constantEnvironment.containsKey(exp.variable); |
| 197 } | 199 } |
| 198 | 200 |
| 199 Statement visitAssign(Assign node) { | 201 Statement visitAssign(Assign node) { |
| 200 if (isEffectivelyConstant(node.definition) && | 202 if (isEffectivelyConstant(node.definition) && |
| 201 node.variable.writeCount == 1) { | 203 node.variable.writeCount == 1) { |
| 202 // Handle constant assignments specially. | 204 // Handle constant assignments specially. |
| 203 // They are always safe to propagate (though we should avoid duplication). | 205 // They are always safe to propagate (though we should avoid duplication). |
| 204 // Moreover, they should not prevent other expressions from propagating. | 206 // Moreover, they should not prevent other expressions from propagating. |
| 205 if (node.variable.readCount <= 1) { | 207 if (node.variable.readCount <= 1) { |
| 206 // A single-use constant should always be propagted to its use site. | 208 // A single-use constant should always be propagted to its use site. |
| (...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 551 return new Return(e); | 553 return new Return(e); |
| 552 } | 554 } |
| 553 } | 555 } |
| 554 return null; | 556 return null; |
| 555 } | 557 } |
| 556 | 558 |
| 557 /// Returns an expression equivalent to both [e1] and [e2]. | 559 /// Returns an expression equivalent to both [e1] and [e2]. |
| 558 /// If non-null is returned, the caller must discard [e1] and [e2] and use | 560 /// If non-null is returned, the caller must discard [e1] and [e2] and use |
| 559 /// the resulting expression in the tree. | 561 /// the resulting expression in the tree. |
| 560 static Expression combineExpressions(Expression e1, Expression e2) { | 562 static Expression combineExpressions(Expression e1, Expression e2) { |
| 561 if (e1 is Variable && e1 == e2) { | 563 if (e1 is VariableUse && e2 is VariableUse && e1.variable == e2.variable) { |
| 562 --e1.readCount; // Two references become one. | 564 --e1.variable.readCount; // Two references become one. |
| 563 return e1; | 565 return e1; |
| 564 } | 566 } |
| 565 if (e1 is Constant && e2 is Constant && e1.value == e2.value) { | 567 if (e1 is Constant && e2 is Constant && e1.value == e2.value) { |
| 566 return e1; | 568 return e1; |
| 567 } | 569 } |
| 568 return null; | 570 return null; |
| 569 } | 571 } |
| 570 | 572 |
| 571 /// Try to collapse nested ifs using && and || expressions. | 573 /// Try to collapse nested ifs using && and || expressions. |
| 572 /// For example: | 574 /// For example: |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 646 } | 648 } |
| 647 | 649 |
| 648 Expression makeCondition(Expression e, bool polarity) { | 650 Expression makeCondition(Expression e, bool polarity) { |
| 649 return polarity ? e : new Not(e); | 651 return polarity ? e : new Not(e); |
| 650 } | 652 } |
| 651 | 653 |
| 652 Statement getBranch(If node, bool polarity) { | 654 Statement getBranch(If node, bool polarity) { |
| 653 return polarity ? node.thenStatement : node.elseStatement; | 655 return polarity ? node.thenStatement : node.elseStatement; |
| 654 } | 656 } |
| 655 } | 657 } |
| OLD | NEW |