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 library tree_ir.optimization.statement_rewriter; | 5 library tree_ir.optimization.statement_rewriter; |
6 | 6 |
7 import 'optimization.dart' show Pass; | 7 import 'optimization.dart' show Pass; |
8 import '../tree_ir_nodes.dart'; | 8 import '../tree_ir_nodes.dart'; |
9 | 9 |
10 /** | 10 /** |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 * separated two ifs. | 109 * separated two ifs. |
110 */ | 110 */ |
111 class StatementRewriter extends Transformer implements Pass { | 111 class StatementRewriter extends Transformer implements Pass { |
112 String get passName => 'Statement rewriter'; | 112 String get passName => 'Statement rewriter'; |
113 | 113 |
114 @override | 114 @override |
115 void rewrite(FunctionDefinition node) { | 115 void rewrite(FunctionDefinition node) { |
116 node.body = visitStatement(node.body); | 116 node.body = visitStatement(node.body); |
117 } | 117 } |
118 | 118 |
119 /// True if targeting Dart. | |
120 final bool isDartMode; | |
121 | |
122 /// The most recently evaluated impure expressions, with the most recent | 119 /// The most recently evaluated impure expressions, with the most recent |
123 /// expression being last. | 120 /// expression being last. |
124 /// | 121 /// |
125 /// Most importantly, this contains [Assign] expressions that we attempt to | 122 /// Most importantly, this contains [Assign] expressions that we attempt to |
126 /// inline at their use site. It also contains other impure expressions that | 123 /// inline at their use site. It also contains other impure expressions that |
127 /// we can propagate to a variable use if they are known to return the value | 124 /// we can propagate to a variable use if they are known to return the value |
128 /// of that variable. | 125 /// of that variable. |
129 /// | 126 /// |
| 127 /// Assignments with constant right-hand sides (see [isEffectivelyConstant]) |
| 128 /// are not considered impure and are put in [constantEnvironment] instead. |
| 129 /// |
130 /// Except for [Conditional]s, expressions in the environment have | 130 /// Except for [Conditional]s, expressions in the environment have |
131 /// not been processed, and all their subexpressions must therefore be | 131 /// not been processed, and all their subexpressions must therefore be |
132 /// variables uses. | 132 /// variables uses. |
133 List<Expression> environment = <Expression>[]; | 133 List<Expression> environment = <Expression>[]; |
134 | 134 |
135 /// Binding environment for variables that are assigned to effectively | 135 /// Binding environment for variables that are assigned to effectively |
136 /// constant expressions (see [isEffectivelyConstant]). | 136 /// constant expressions (see [isEffectivelyConstant]). |
137 final Map<Variable, Expression> constantEnvironment; | 137 final Map<Variable, Expression> constantEnvironment; |
138 | 138 |
139 /// Substitution map for labels. Any break to a label L should be substituted | 139 /// Substitution map for labels. Any break to a label L should be substituted |
140 /// for a break to L' if L maps to L'. | 140 /// for a break to L' if L maps to L'. |
141 Map<Label, Jump> labelRedirects = <Label, Jump>{}; | 141 Map<Label, Jump> labelRedirects = <Label, Jump>{}; |
142 | 142 |
143 /// Number of uses of the given variable that are still unseen. | 143 /// Number of uses of the given variable that are still unseen. |
144 /// Used to detect the first use of a variable (since we do backwards | 144 /// Used to detect the first use of a variable (since we do backwards |
145 /// traversal, the first use is the last one seen). | 145 /// traversal, the first use is the last one seen). |
146 Map<Variable, int> unseenUses = <Variable, int>{}; | 146 Map<Variable, int> unseenUses = <Variable, int>{}; |
147 | 147 |
148 /// Rewriter for methods. | 148 /// Rewriter for methods. |
149 StatementRewriter({this.isDartMode}) | 149 StatementRewriter() : constantEnvironment = <Variable, Expression>{}; |
150 : constantEnvironment = <Variable, Expression>{} { | |
151 assert(isDartMode != null); | |
152 } | |
153 | 150 |
154 /// Rewriter for nested functions. | 151 /// Rewriter for nested functions. |
155 StatementRewriter.nested(StatementRewriter parent) | 152 StatementRewriter.nested(StatementRewriter parent) |
156 : constantEnvironment = parent.constantEnvironment, | 153 : constantEnvironment = parent.constantEnvironment, |
157 unseenUses = parent.unseenUses, | 154 unseenUses = parent.unseenUses; |
158 isDartMode = parent.isDartMode; | |
159 | 155 |
160 /// A set of labels that can be safely inlined at their use. | 156 /// A set of labels that can be safely inlined at their use. |
161 /// | 157 /// |
162 /// The successor statements for labeled statements that have only one break | 158 /// The successor statements for labeled statements that have only one break |
163 /// from them are normally rewritten inline at the site of the break. This | 159 /// from them are normally rewritten inline at the site of the break. This |
164 /// is not safe if the code would be moved inside the scope of an exception | 160 /// is not safe if the code would be moved inside the scope of an exception |
165 /// handler (i.e., if the code would be moved into a try from outside it). | 161 /// handler (i.e., if the code would be moved into a try from outside it). |
166 Set<Label> safeForInlining = new Set<Label>(); | 162 Set<Label> safeForInlining = new Set<Label>(); |
167 | 163 |
168 /// Returns the redirect target of [jump] or [jump] itself if it should not | 164 /// Returns the redirect target of [jump] or [jump] itself if it should not |
(...skipping 12 matching lines...) Expand all Loading... |
181 } | 177 } |
182 | 178 |
183 /// Left-hand side of the given assignment, or `null` if not an assignment. | 179 /// Left-hand side of the given assignment, or `null` if not an assignment. |
184 Variable getLeftHand(Expression e) { | 180 Variable getLeftHand(Expression e) { |
185 return e is Assign ? e.variable : null; | 181 return e is Assign ? e.variable : null; |
186 } | 182 } |
187 | 183 |
188 /// If the given expression always returns the value of one of its | 184 /// If the given expression always returns the value of one of its |
189 /// subexpressions, returns that subexpression, otherwise `null`. | 185 /// subexpressions, returns that subexpression, otherwise `null`. |
190 Expression getValueSubexpression(Expression e) { | 186 Expression getValueSubexpression(Expression e) { |
191 if (isDartMode && | |
192 e is InvokeMethod && | |
193 (e.selector.isSetter || e.selector.isIndexSet)) { | |
194 return e.arguments.last; | |
195 } | |
196 if (e is SetField) return e.value; | 187 if (e is SetField) return e.value; |
197 return null; | 188 return null; |
198 } | 189 } |
199 | 190 |
200 /// If the given expression always returns the value of one of its | 191 /// If the given expression always returns the value of one of its |
201 /// subexpressions, and that subexpression is a variable use, returns that | 192 /// subexpressions, and that subexpression is a variable use, returns that |
202 /// variable. Otherwise `null`. | 193 /// variable. Otherwise `null`. |
203 Variable getRightHand(Expression e) { | 194 Variable getRightHand(Expression e) { |
204 Expression value = getValueSubexpression(e); | 195 Expression value = getValueSubexpression(e); |
205 return value is VariableUse ? value.variable : null; | 196 return value is VariableUse ? value.variable : null; |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 environment.removeLast(); | 248 environment.removeLast(); |
258 --node.variable.readCount; | 249 --node.variable.readCount; |
259 return visitExpression(binding); | 250 return visitExpression(binding); |
260 } | 251 } |
261 } | 252 } |
262 | 253 |
263 // If the definition could not be propagated, leave the variable use. | 254 // If the definition could not be propagated, leave the variable use. |
264 return node; | 255 return node; |
265 } | 256 } |
266 | 257 |
| 258 /// True if [exp] contains a use of a variable that was assigned to by the |
| 259 /// most recently evaluated impure expression (constant assignments are not |
| 260 /// considered impure). |
| 261 /// |
| 262 /// This implies that the assignment can be propagated into this use unless |
| 263 /// the use is moved further away. |
| 264 /// |
| 265 /// In this case, we will refrain from moving [exp] across other impure |
| 266 /// expressions, even when this is safe, because doing so would immediately |
| 267 /// prevent the previous expression from propagating, canceling out the |
| 268 /// benefit we might otherwise gain from propagating [exp]. |
| 269 /// |
| 270 /// [exp] must be an unprocessed expression, i.e. either a [Conditional] or |
| 271 /// an expression whose subexpressions are all variable uses. |
| 272 bool usesRecentlyAssignedVariable(Expression exp) { |
| 273 if (environment.isEmpty) return false; |
| 274 Variable variable = getLeftHand(environment.last); |
| 275 if (variable == null) return false; |
| 276 IsVariableUsedVisitor visitor = new IsVariableUsedVisitor(variable); |
| 277 visitor.visitExpression(exp); |
| 278 return visitor.wasFound; |
| 279 } |
| 280 |
267 /// Returns true if [exp] has no side effects and has a constant value within | 281 /// Returns true if [exp] has no side effects and has a constant value within |
268 /// any given activation of the enclosing method. | 282 /// any given activation of the enclosing method. |
269 bool isEffectivelyConstant(Expression exp) { | 283 bool isEffectivelyConstant(Expression exp) { |
270 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional | 284 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional |
271 // expressions recursively. Determine if that is a valuable optimization | 285 // expressions recursively. Determine if that is a valuable optimization |
272 // and/or if it is better handled at the CPS level. | 286 // and/or if it is better handled at the CPS level. |
273 return exp is Constant || | 287 return exp is Constant || |
274 exp is This || | 288 exp is This || |
275 exp is CreateInvocationMirror || | 289 exp is CreateInvocationMirror || |
276 exp is InvokeStatic && exp.isEffectivelyConstant || | 290 exp is InvokeStatic && exp.isEffectivelyConstant || |
| 291 exp is ApplyBuiltinOperator || |
277 exp is VariableUse && constantEnvironment.containsKey(exp.variable); | 292 exp is VariableUse && constantEnvironment.containsKey(exp.variable); |
278 } | 293 } |
279 | 294 |
280 /// True if [node] is an assignment that can be propagated as a constant. | 295 /// True if [node] is an assignment that can be propagated as a constant. |
281 bool isEffectivelyConstantAssignment(Expression node) { | 296 bool isEffectivelyConstantAssignment(Expression node) { |
282 return node is Assign && | 297 return node is Assign && |
283 node.variable.writeCount == 1 && | 298 node.variable.writeCount == 1 && |
284 isEffectivelyConstant(node.value); | 299 isEffectivelyConstant(node.value); |
285 } | 300 } |
286 | 301 |
287 Statement visitExpressionStatement(ExpressionStatement stmt) { | 302 Statement visitExpressionStatement(ExpressionStatement stmt) { |
288 if (isEffectivelyConstantAssignment(stmt.expression)) { | 303 if (isEffectivelyConstantAssignment(stmt.expression) && |
| 304 !usesRecentlyAssignedVariable(stmt.expression)) { |
289 Assign assign = stmt.expression; | 305 Assign assign = stmt.expression; |
290 // Handle constant assignments specially. | 306 // Handle constant assignments specially. |
291 // They are always safe to propagate (though we should avoid duplication). | 307 // They are always safe to propagate (though we should avoid duplication). |
292 // Moreover, they should not prevent other expressions from propagating. | 308 // Moreover, they should not prevent other expressions from propagating. |
293 if (assign.variable.readCount <= 1) { | 309 if (assign.variable.readCount <= 1) { |
294 // A single-use constant should always be propagted to its use site. | 310 // A single-use constant should always be propagted to its use site. |
295 constantEnvironment[assign.variable] = assign.value; | 311 constantEnvironment[assign.variable] = assign.value; |
296 --assign.variable.writeCount; | 312 --assign.variable.writeCount; |
297 return visitStatement(stmt.next); | 313 return visitStatement(stmt.next); |
298 } else { | 314 } else { |
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
612 Expression visitTypeExpression(TypeExpression node) { | 628 Expression visitTypeExpression(TypeExpression node) { |
613 _rewriteList(node.arguments); | 629 _rewriteList(node.arguments); |
614 return node; | 630 return node; |
615 } | 631 } |
616 | 632 |
617 Expression visitCreateInvocationMirror(CreateInvocationMirror node) { | 633 Expression visitCreateInvocationMirror(CreateInvocationMirror node) { |
618 _rewriteList(node.arguments); | 634 _rewriteList(node.arguments); |
619 return node; | 635 return node; |
620 } | 636 } |
621 | 637 |
| 638 /// True if [operator] is a binary operator that always has the same value |
| 639 /// if its arguments are swapped. |
| 640 bool isSymmetricOperator(BuiltinOperator operator) { |
| 641 switch (operator) { |
| 642 case BuiltinOperator.StrictEq: |
| 643 case BuiltinOperator.StrictNeq: |
| 644 case BuiltinOperator.LooseEq: |
| 645 case BuiltinOperator.LooseNeq: |
| 646 case BuiltinOperator.NumAnd: |
| 647 case BuiltinOperator.NumOr: |
| 648 case BuiltinOperator.NumXor: |
| 649 case BuiltinOperator.NumAdd: |
| 650 case BuiltinOperator.NumMultiply: |
| 651 return true; |
| 652 default: |
| 653 return false; |
| 654 } |
| 655 } |
| 656 |
| 657 /// If [operator] is a commutable binary operator, returns the commuted |
| 658 /// operator, possibly the operator itself, otherwise returns `null`. |
| 659 BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) { |
| 660 if (isSymmetricOperator(operator)) { |
| 661 // Symmetric operators are their own commutes. |
| 662 return operator; |
| 663 } |
| 664 switch(operator) { |
| 665 case BuiltinOperator.NumLt: return BuiltinOperator.NumGt; |
| 666 case BuiltinOperator.NumLe: return BuiltinOperator.NumGe; |
| 667 case BuiltinOperator.NumGt: return BuiltinOperator.NumLt; |
| 668 case BuiltinOperator.NumGe: return BuiltinOperator.NumLe; |
| 669 default: return null; |
| 670 } |
| 671 } |
| 672 |
| 673 /// Built-in binary operators are commuted when it is safe and can enable an |
| 674 /// assignment propagation. For example: |
| 675 /// |
| 676 /// var x = foo(); |
| 677 /// var y = bar(); |
| 678 /// var z = y < x; |
| 679 /// |
| 680 /// ==> |
| 681 /// |
| 682 /// var z = foo() > bar(); |
| 683 /// |
| 684 /// foo() must be evaluated before bar(), so the propagation is only possible |
| 685 /// by commuting the operator. |
| 686 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 687 if (environment.isEmpty || getLeftHand(environment.last) == null) { |
| 688 // If there is no recent assignment that might propagate, so there is no |
| 689 // opportunity for optimization here. |
| 690 _rewriteList(node.arguments); |
| 691 return node; |
| 692 } |
| 693 Variable propagatableVariable = getLeftHand(environment.last); |
| 694 BuiltinOperator commuted = commuteBinaryOperator(node.operator); |
| 695 if (commuted != null) { |
| 696 assert(node.arguments.length == 2); // Only binary operators can commute. |
| 697 VariableUse arg1 = node.arguments[0]; |
| 698 VariableUse arg2 = node.arguments[1]; |
| 699 if (propagatableVariable == arg1.variable && |
| 700 propagatableVariable != arg2.variable && |
| 701 !constantEnvironment.containsKey(arg2.variable)) { |
| 702 // An assignment can be propagated if we commute the operator. |
| 703 node.operator = commuted; |
| 704 node.arguments[0] = arg2; |
| 705 node.arguments[1] = arg1; |
| 706 } |
| 707 } |
| 708 _rewriteList(node.arguments); |
| 709 return node; |
| 710 } |
| 711 |
622 /// If [s] and [t] are similar statements we extract their subexpressions | 712 /// If [s] and [t] are similar statements we extract their subexpressions |
623 /// and returns a new statement of the same type using expressions combined | 713 /// and returns a new statement of the same type using expressions combined |
624 /// with the [combine] callback. For example: | 714 /// with the [combine] callback. For example: |
625 /// | 715 /// |
626 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) | 716 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) |
627 /// | 717 /// |
628 /// If [combine] returns E1 then the unified statement is equivalent to [s], | 718 /// If [combine] returns E1 then the unified statement is equivalent to [s], |
629 /// and if [combine] returns E2 the unified statement is equivalence to [t]. | 719 /// and if [combine] returns E2 the unified statement is equivalence to [t]. |
630 /// | 720 /// |
631 /// It is guaranteed that no side effects occur between the beginning of the | 721 /// It is guaranteed that no side effects occur between the beginning of the |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
916 } | 1006 } |
917 | 1007 |
918 /// Result of combining two expressions that do not affect reference counting. | 1008 /// Result of combining two expressions that do not affect reference counting. |
919 class GenericCombinedExpressions implements CombinedExpressions { | 1009 class GenericCombinedExpressions implements CombinedExpressions { |
920 Expression combined; | 1010 Expression combined; |
921 | 1011 |
922 GenericCombinedExpressions(this.combined); | 1012 GenericCombinedExpressions(this.combined); |
923 | 1013 |
924 void uncombine() {} | 1014 void uncombine() {} |
925 } | 1015 } |
| 1016 |
| 1017 /// Looks for uses of a specific variable. |
| 1018 /// |
| 1019 /// Note that this visitor is only applied to expressions where all |
| 1020 /// sub-expressions are known to be variable uses, so there is no risk of |
| 1021 /// explosive reprocessing. |
| 1022 class IsVariableUsedVisitor extends RecursiveVisitor { |
| 1023 Variable variable; |
| 1024 bool wasFound = false; |
| 1025 |
| 1026 IsVariableUsedVisitor(this.variable); |
| 1027 |
| 1028 visitVariableUse(VariableUse node) { |
| 1029 if (node.variable == variable) { |
| 1030 wasFound = true; |
| 1031 } |
| 1032 } |
| 1033 |
| 1034 visitInnerFunction(FunctionDefinition node) {} |
| 1035 } |
OLD | NEW |