| 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 import '../../io/source_information.dart'; | 9 import '../../io/source_information.dart'; |
| 10 import '../../elements/elements.dart'; | 10 import '../../elements/elements.dart'; |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 /// Assignments with constant right-hand sides (see [isEffectivelyConstant]) | 160 /// Assignments with constant right-hand sides (see [isEffectivelyConstant]) |
| 161 /// are not considered impure and are put in [constantEnvironment] instead. | 161 /// are not considered impure and are put in [constantEnvironment] instead. |
| 162 /// | 162 /// |
| 163 /// Except for [Conditional]s, expressions in the environment have | 163 /// Except for [Conditional]s, expressions in the environment have |
| 164 /// not been processed, and all their subexpressions must therefore be | 164 /// not been processed, and all their subexpressions must therefore be |
| 165 /// variables uses. | 165 /// variables uses. |
| 166 List<Expression> environment = <Expression>[]; | 166 List<Expression> environment = <Expression>[]; |
| 167 | 167 |
| 168 /// Binding environment for variables that are assigned to effectively | 168 /// Binding environment for variables that are assigned to effectively |
| 169 /// constant expressions (see [isEffectivelyConstant]). | 169 /// constant expressions (see [isEffectivelyConstant]). |
| 170 Map<Variable, Expression> constantEnvironment; | 170 Map<Variable, Expression> constantEnvironment = <Variable, Expression>{}; |
| 171 | 171 |
| 172 /// Substitution map for labels. Any break to a label L should be substituted | 172 /// Substitution map for labels. Any break to a label L should be substituted |
| 173 /// for a break to L' if L maps to L'. | 173 /// for a break to L' if L maps to L'. |
| 174 Map<Label, Jump> labelRedirects = <Label, Jump>{}; | 174 Map<Label, Jump> labelRedirects = <Label, Jump>{}; |
| 175 | 175 |
| 176 /// Number of uses of the given variable that are still unseen. | 176 /// Number of uses of the given variable that are still unseen. |
| 177 /// Used to detect the first use of a variable (since we do backwards | 177 /// Used to detect the first use of a variable (since we do backwards |
| 178 /// traversal, the first use is the last one seen). | 178 /// traversal, the first use is the last one seen). |
| 179 Map<Variable, int> unseenUses = <Variable, int>{}; | 179 Map<Variable, int> unseenUses = <Variable, int>{}; |
| 180 | 180 |
| 181 /// Number of assignments to a given variable that dominate the current | 181 /// Number of assignments to a given variable that dominate the current |
| 182 /// position. | 182 /// position. |
| 183 /// | 183 /// |
| 184 /// Pure expressions will not be inlined if it uses a variable with more than | 184 /// Pure expressions will not be inlined if it uses a variable with more than |
| 185 /// one dominating assignment, because the reaching definition of the used | 185 /// one dominating assignment, because the reaching definition of the used |
| 186 /// variable might have changed since it was put in the environment. | 186 /// variable might have changed since it was put in the environment. |
| 187 final Map<Variable, int> dominatingAssignments = <Variable, int>{}; | 187 final Map<Variable, int> dominatingAssignments = <Variable, int>{}; |
| 188 | 188 |
| 189 /// Rewriter for methods. | |
| 190 StatementRewriter() : constantEnvironment = <Variable, Expression>{}; | |
| 191 | |
| 192 /// Rewriter for nested functions. | |
| 193 StatementRewriter.nested(StatementRewriter parent) | |
| 194 : constantEnvironment = parent.constantEnvironment, | |
| 195 unseenUses = parent.unseenUses; | |
| 196 | |
| 197 /// A set of labels that can be safely inlined at their use. | 189 /// A set of labels that can be safely inlined at their use. |
| 198 /// | 190 /// |
| 199 /// The successor statements for labeled statements that have only one break | 191 /// The successor statements for labeled statements that have only one break |
| 200 /// from them are normally rewritten inline at the site of the break. This | 192 /// from them are normally rewritten inline at the site of the break. This |
| 201 /// is not safe if the code would be moved inside the scope of an exception | 193 /// is not safe if the code would be moved inside the scope of an exception |
| 202 /// handler (i.e., if the code would be moved into a try from outside it). | 194 /// handler (i.e., if the code would be moved into a try from outside it). |
| 203 Set<Label> safeForInlining = new Set<Label>(); | 195 Set<Label> safeForInlining = new Set<Label>(); |
| 204 | 196 |
| 197 /// If the top element is true, assignments of form "x = CONST" may be |
| 198 /// propagated into a following occurence of CONST. This may confuse the JS |
| 199 /// engine so it is disabled in some cases. |
| 200 final List<bool> allowRhsPropagation = <bool>[true]; |
| 201 |
| 202 bool get isRhsPropagationAllowed => allowRhsPropagation.last; |
| 203 |
| 205 /// Returns the redirect target of [jump] or [jump] itself if it should not | 204 /// Returns the redirect target of [jump] or [jump] itself if it should not |
| 206 /// be redirected. | 205 /// be redirected. |
| 207 Jump redirect(Jump jump) { | 206 Jump redirect(Jump jump) { |
| 208 Jump newJump = labelRedirects[jump.target]; | 207 Jump newJump = labelRedirects[jump.target]; |
| 209 return newJump != null ? newJump : jump; | 208 return newJump != null ? newJump : jump; |
| 210 } | 209 } |
| 211 | 210 |
| 212 void inEmptyEnvironment(void action(), {bool keepConstants: true}) { | 211 void inEmptyEnvironment(void action(), {bool keepConstants: true}) { |
| 213 List oldEnvironment = environment; | 212 List oldEnvironment = environment; |
| 214 Map oldConstantEnvironment = constantEnvironment; | 213 Map oldConstantEnvironment = constantEnvironment; |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 320 return visitExpression(binding); | 319 return visitExpression(binding); |
| 321 } | 320 } |
| 322 | 321 |
| 323 // Is the most recently evaluated impure expression known to have the | 322 // Is the most recently evaluated impure expression known to have the |
| 324 // value of this variable? | 323 // value of this variable? |
| 325 // | 324 // |
| 326 // If so, we can replace this use with the impure expression, e.g: | 325 // If so, we can replace this use with the impure expression, e.g: |
| 327 // | 326 // |
| 328 // { E.foo = x; bar(x) } ==> bar(E.foo = x) | 327 // { E.foo = x; bar(x) } ==> bar(E.foo = x) |
| 329 // | 328 // |
| 330 if (getRightHandVariable(binding) == node.variable) { | 329 if (isRhsPropagationAllowed && |
| 330 getRightHandVariable(binding) == node.variable) { |
| 331 environment.removeLast(); | 331 environment.removeLast(); |
| 332 --node.variable.readCount; | 332 --node.variable.readCount; |
| 333 return visitExpression(binding); | 333 return visitExpression(binding); |
| 334 } | 334 } |
| 335 } | 335 } |
| 336 | 336 |
| 337 // If the definition could not be propagated, leave the variable use. | 337 // If the definition could not be propagated, leave the variable use. |
| 338 return node; | 338 return node; |
| 339 } | 339 } |
| 340 | 340 |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 460 return stmt; | 460 return stmt; |
| 461 } else { | 461 } else { |
| 462 // Expression was propagated into the successor. | 462 // Expression was propagated into the successor. |
| 463 return stmt.next; | 463 return stmt.next; |
| 464 } | 464 } |
| 465 }; | 465 }; |
| 466 } | 466 } |
| 467 } | 467 } |
| 468 | 468 |
| 469 Expression visitAssign(Assign node) { | 469 Expression visitAssign(Assign node) { |
| 470 allowRhsPropagation.add(true); |
| 470 node.value = visitExpression(node.value); | 471 node.value = visitExpression(node.value); |
| 472 allowRhsPropagation.removeLast(); |
| 471 // Remove assignments to variables without any uses. This can happen | 473 // Remove assignments to variables without any uses. This can happen |
| 472 // because the assignment was propagated into its use, e.g: | 474 // because the assignment was propagated into its use, e.g: |
| 473 // | 475 // |
| 474 // { x = foo(); bar(x) } ==> bar(x = foo()) ==> bar(foo()) | 476 // { x = foo(); bar(x) } ==> bar(x = foo()) ==> bar(foo()) |
| 475 // | 477 // |
| 476 if (node.variable.readCount == 0) { | 478 if (node.variable.readCount == 0) { |
| 477 --node.variable.writeCount; | 479 --node.variable.writeCount; |
| 478 return node.value; | 480 return node.value; |
| 479 } | 481 } |
| 480 return node; | 482 return node; |
| 481 } | 483 } |
| 482 | 484 |
| 483 /// Process nodes right-to-left, the opposite of evaluation order in the case | 485 /// Process nodes right-to-left, the opposite of evaluation order in the case |
| 484 /// of argument lists.. | 486 /// of argument lists.. |
| 485 void _rewriteList(List<Node> nodes) { | 487 void _rewriteList(List<Node> nodes, {bool rhsPropagation: true}) { |
| 488 allowRhsPropagation.add(rhsPropagation); |
| 486 for (int i = nodes.length - 1; i >= 0; --i) { | 489 for (int i = nodes.length - 1; i >= 0; --i) { |
| 487 nodes[i] = visitExpression(nodes[i]); | 490 nodes[i] = visitExpression(nodes[i]); |
| 488 } | 491 } |
| 492 allowRhsPropagation.removeLast(); |
| 489 } | 493 } |
| 490 | 494 |
| 491 Expression visitInvokeStatic(InvokeStatic node) { | 495 Expression visitInvokeStatic(InvokeStatic node) { |
| 492 _rewriteList(node.arguments); | 496 _rewriteList(node.arguments); |
| 493 return node; | 497 return node; |
| 494 } | 498 } |
| 495 | 499 |
| 496 Expression visitInvokeMethod(InvokeMethod node) { | 500 Expression visitInvokeMethod(InvokeMethod node) { |
| 497 if (node.receiverIsNotNull) { | 501 if (node.receiverIsNotNull) { |
| 498 _rewriteList(node.arguments); | 502 _rewriteList(node.arguments); |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 714 node.tryBody = visitStatement(node.tryBody); | 718 node.tryBody = visitStatement(node.tryBody); |
| 715 safeForInlining = saved; | 719 safeForInlining = saved; |
| 716 node.catchParameters.forEach(pushDominatingAssignment); | 720 node.catchParameters.forEach(pushDominatingAssignment); |
| 717 node.catchBody = visitStatement(node.catchBody); | 721 node.catchBody = visitStatement(node.catchBody); |
| 718 node.catchParameters.forEach(popDominatingAssignment); | 722 node.catchParameters.forEach(popDominatingAssignment); |
| 719 }); | 723 }); |
| 720 return node; | 724 return node; |
| 721 } | 725 } |
| 722 | 726 |
| 723 Expression visitConstant(Constant node) { | 727 Expression visitConstant(Constant node) { |
| 724 if (!environment.isEmpty) { | 728 if (isRhsPropagationAllowed && !environment.isEmpty) { |
| 725 Constant constant = getRightHandConstant(environment.last); | 729 Constant constant = getRightHandConstant(environment.last); |
| 726 if (constant != null && constant.value == node.value) { | 730 if (constant != null && constant.value == node.value) { |
| 727 return visitExpression(environment.removeLast()); | 731 return visitExpression(environment.removeLast()); |
| 728 } | 732 } |
| 729 } | 733 } |
| 730 return node; | 734 return node; |
| 731 } | 735 } |
| 732 | 736 |
| 733 Expression visitThis(This node) { | 737 Expression visitThis(This node) { |
| 734 return node; | 738 return node; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 748 return node; | 752 return node; |
| 749 } | 753 } |
| 750 | 754 |
| 751 Expression visitTypeOperator(TypeOperator node) { | 755 Expression visitTypeOperator(TypeOperator node) { |
| 752 _rewriteList(node.typeArguments); | 756 _rewriteList(node.typeArguments); |
| 753 node.value = visitExpression(node.value); | 757 node.value = visitExpression(node.value); |
| 754 return node; | 758 return node; |
| 755 } | 759 } |
| 756 | 760 |
| 757 Expression visitSetField(SetField node) { | 761 Expression visitSetField(SetField node) { |
| 762 allowRhsPropagation.add(true); |
| 758 node.value = visitExpression(node.value); | 763 node.value = visitExpression(node.value); |
| 759 node.object = visitExpression(node.object); | 764 node.object = visitExpression(node.object); |
| 765 allowRhsPropagation.removeLast(); |
| 760 return node; | 766 return node; |
| 761 } | 767 } |
| 762 | 768 |
| 763 Expression visitGetField(GetField node) { | 769 Expression visitGetField(GetField node) { |
| 764 node.object = visitExpression(node.object); | 770 node.object = visitExpression(node.object); |
| 765 return node; | 771 return node; |
| 766 } | 772 } |
| 767 | 773 |
| 768 Expression visitGetStatic(GetStatic node) { | 774 Expression visitGetStatic(GetStatic node) { |
| 769 return node; | 775 return node; |
| 770 } | 776 } |
| 771 | 777 |
| 772 Expression visitSetStatic(SetStatic node) { | 778 Expression visitSetStatic(SetStatic node) { |
| 779 allowRhsPropagation.add(true); |
| 773 node.value = visitExpression(node.value); | 780 node.value = visitExpression(node.value); |
| 781 allowRhsPropagation.removeLast(); |
| 774 return node; | 782 return node; |
| 775 } | 783 } |
| 776 | 784 |
| 777 Expression visitGetTypeTestProperty(GetTypeTestProperty node) { | 785 Expression visitGetTypeTestProperty(GetTypeTestProperty node) { |
| 778 node.object = visitExpression(node.object); | 786 node.object = visitExpression(node.object); |
| 779 return node; | 787 return node; |
| 780 } | 788 } |
| 781 | 789 |
| 782 Expression visitCreateBox(CreateBox node) { | 790 Expression visitCreateBox(CreateBox node) { |
| 783 return node; | 791 return node; |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 /// var y = bar(); | 881 /// var y = bar(); |
| 874 /// var z = y < x; | 882 /// var z = y < x; |
| 875 /// | 883 /// |
| 876 /// ==> | 884 /// ==> |
| 877 /// | 885 /// |
| 878 /// var z = foo() > bar(); | 886 /// var z = foo() > bar(); |
| 879 /// | 887 /// |
| 880 /// foo() must be evaluated before bar(), so the propagation is only possible | 888 /// foo() must be evaluated before bar(), so the propagation is only possible |
| 881 /// by commuting the operator. | 889 /// by commuting the operator. |
| 882 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 890 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 883 if (environment.isEmpty || getLeftHand(environment.last) == null) { | 891 if (!environment.isEmpty && getLeftHand(environment.last) != null) { |
| 884 // If there is no recent assignment that might propagate, so there is no | 892 Variable propagatableVariable = getLeftHand(environment.last); |
| 885 // opportunity for optimization here. | 893 BuiltinOperator commuted = commuteBinaryOperator(node.operator); |
| 886 _rewriteList(node.arguments); | 894 if (commuted != null) { |
| 887 return node; | 895 // Only binary operators can commute. |
| 888 } | 896 assert(node.arguments.length == 2); |
| 889 Variable propagatableVariable = getLeftHand(environment.last); | 897 Expression left = node.arguments[0]; |
| 890 BuiltinOperator commuted = commuteBinaryOperator(node.operator); | 898 if (left is VariableUse && propagatableVariable == left.variable) { |
| 891 if (commuted != null) { | 899 Expression right = node.arguments[1]; |
| 892 assert(node.arguments.length == 2); // Only binary operators can commute. | 900 if (right is This || |
| 893 Expression left = node.arguments[0]; | 901 (right is VariableUse && |
| 894 if (left is VariableUse && propagatableVariable == left.variable) { | 902 propagatableVariable != right.variable && |
| 895 Expression right = node.arguments[1]; | 903 !constantEnvironment.containsKey(right.variable))) { |
| 896 if (right is This || | 904 // An assignment can be propagated if we commute the operator. |
| 897 (right is VariableUse && | 905 node.operator = commuted; |
| 898 propagatableVariable != right.variable && | 906 node.arguments[0] = right; |
| 899 !constantEnvironment.containsKey(right.variable))) { | 907 node.arguments[1] = left; |
| 900 // An assignment can be propagated if we commute the operator. | 908 } |
| 901 node.operator = commuted; | |
| 902 node.arguments[0] = right; | |
| 903 node.arguments[1] = left; | |
| 904 } | 909 } |
| 905 } | 910 } |
| 906 } | 911 } |
| 907 _rewriteList(node.arguments); | 912 // Avoid code like `p == (q.f = null)`. JS operators with a constant operand |
| 913 // can sometimes be compiled to a specialized instruction in the JS engine, |
| 914 // so retain syntactically constant operands. |
| 915 _rewriteList(node.arguments, rhsPropagation: false); |
| 908 return node; | 916 return node; |
| 909 } | 917 } |
| 910 | 918 |
| 911 /// If [s] and [t] are similar statements we extract their subexpressions | 919 /// If [s] and [t] are similar statements we extract their subexpressions |
| 912 /// and returns a new statement of the same type using expressions combined | 920 /// and returns a new statement of the same type using expressions combined |
| 913 /// with the [combine] callback. For example: | 921 /// with the [combine] callback. For example: |
| 914 /// | 922 /// |
| 915 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) | 923 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) |
| 916 /// | 924 /// |
| 917 /// If [combine] returns E1 then the unified statement is equivalent to [s], | 925 /// If [combine] returns E1 then the unified statement is equivalent to [s], |
| (...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1308 VariableUseCallback callback; | 1316 VariableUseCallback callback; |
| 1309 | 1317 |
| 1310 VariableUseVisitor(this.callback); | 1318 VariableUseVisitor(this.callback); |
| 1311 | 1319 |
| 1312 visitVariableUse(VariableUse use) => callback(use); | 1320 visitVariableUse(VariableUse use) => callback(use); |
| 1313 | 1321 |
| 1314 static void visit(Expression node, VariableUseCallback callback) { | 1322 static void visit(Expression node, VariableUseCallback callback) { |
| 1315 new VariableUseVisitor(callback).visitExpression(node); | 1323 new VariableUseVisitor(callback).visitExpression(node); |
| 1316 } | 1324 } |
| 1317 } | 1325 } |
| OLD | NEW |