| 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 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 361 return visitor.wasFound; | 361 return visitor.wasFound; |
| 362 } | 362 } |
| 363 | 363 |
| 364 /// Returns true if [exp] has no side effects and has a constant value within | 364 /// Returns true if [exp] has no side effects and has a constant value within |
| 365 /// any given activation of the enclosing method. | 365 /// any given activation of the enclosing method. |
| 366 bool isEffectivelyConstant(Expression exp) { | 366 bool isEffectivelyConstant(Expression exp) { |
| 367 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional | 367 // TODO(asgerf): Can be made more aggressive e.g. by checking conditional |
| 368 // expressions recursively. Determine if that is a valuable optimization | 368 // expressions recursively. Determine if that is a valuable optimization |
| 369 // and/or if it is better handled at the CPS level. | 369 // and/or if it is better handled at the CPS level. |
| 370 return exp is Constant || | 370 return exp is Constant || |
| 371 exp is This || | 371 exp is This || |
| 372 exp is CreateInvocationMirror || | 372 exp is CreateInvocationMirror || |
| 373 exp is CreateInstance || | 373 exp is CreateInstance || |
| 374 exp is CreateBox || | 374 exp is CreateBox || |
| 375 exp is TypeExpression || | 375 exp is TypeExpression || |
| 376 exp is GetStatic && exp.element.isFunction || | 376 exp is GetStatic && exp.element.isFunction || |
| 377 exp is Interceptor || | 377 exp is Interceptor || |
| 378 exp is ApplyBuiltinOperator || | 378 exp is ApplyBuiltinOperator || |
| 379 exp is VariableUse && constantEnvironment.containsKey(exp.variable); | 379 exp is VariableUse && constantEnvironment.containsKey(exp.variable); |
| 380 } | 380 } |
| 381 | 381 |
| 382 /// True if [node] is an assignment that can be propagated as a constant. | 382 /// True if [node] is an assignment that can be propagated as a constant. |
| 383 bool isEffectivelyConstantAssignment(Expression node) { | 383 bool isEffectivelyConstantAssignment(Expression node) { |
| 384 return node is Assign && | 384 return node is Assign && |
| 385 node.variable.writeCount == 1 && | 385 node.variable.writeCount == 1 && |
| 386 isEffectivelyConstant(node.value); | 386 isEffectivelyConstant(node.value); |
| 387 } | 387 } |
| 388 | 388 |
| 389 Statement visitExpressionStatement(ExpressionStatement inputNode) { | 389 Statement visitExpressionStatement(ExpressionStatement inputNode) { |
| 390 // Analyze chains of expression statements. | 390 // Analyze chains of expression statements. |
| 391 // To avoid deep recursion, [processExpressionStatement] returns a callback | 391 // To avoid deep recursion, [processExpressionStatement] returns a callback |
| 392 // to invoke after its successor node has been processed. | 392 // to invoke after its successor node has been processed. |
| 393 // These callbacks are stored in a list and invoked in reverse at the end. | 393 // These callbacks are stored in a list and invoked in reverse at the end. |
| 394 List<Function> stack = []; | 394 List<Function> stack = []; |
| 395 Statement node = inputNode; | 395 Statement node = inputNode; |
| 396 while (node is ExpressionStatement) { | 396 while (node is ExpressionStatement) { |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 node.elseStatement = visitStatement(node.elseStatement); | 673 node.elseStatement = visitStatement(node.elseStatement); |
| 674 }); | 674 }); |
| 675 | 675 |
| 676 node.condition = visitExpression(node.condition); | 676 node.condition = visitExpression(node.condition); |
| 677 | 677 |
| 678 inEmptyEnvironment(() { | 678 inEmptyEnvironment(() { |
| 679 tryCollapseIf(node); | 679 tryCollapseIf(node); |
| 680 }); | 680 }); |
| 681 | 681 |
| 682 Statement reduced = combineStatementsInBranches( | 682 Statement reduced = combineStatementsInBranches( |
| 683 node.thenStatement, | 683 node.thenStatement, node.elseStatement, node.condition); |
| 684 node.elseStatement, | |
| 685 node.condition); | |
| 686 if (reduced != null) { | 684 if (reduced != null) { |
| 687 return reduced; | 685 return reduced; |
| 688 } | 686 } |
| 689 | 687 |
| 690 return node; | 688 return node; |
| 691 } | 689 } |
| 692 | 690 |
| 693 Statement visitWhileTrue(WhileTrue node) { | 691 Statement visitWhileTrue(WhileTrue node) { |
| 694 // Do not propagate assignments into loops. Doing so is not safe for | 692 // Do not propagate assignments into loops. Doing so is not safe for |
| 695 // variables modified in the loop (the initial value will be propagated). | 693 // variables modified in the loop (the initial value will be propagated). |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 739 } | 737 } |
| 740 | 738 |
| 741 Expression visitTypeOperator(TypeOperator node) { | 739 Expression visitTypeOperator(TypeOperator node) { |
| 742 _rewriteList(node.typeArguments); | 740 _rewriteList(node.typeArguments); |
| 743 node.value = visitExpression(node.value); | 741 node.value = visitExpression(node.value); |
| 744 return node; | 742 return node; |
| 745 } | 743 } |
| 746 | 744 |
| 747 bool isCompoundableBuiltin(Expression e) { | 745 bool isCompoundableBuiltin(Expression e) { |
| 748 return e is ApplyBuiltinOperator && | 746 return e is ApplyBuiltinOperator && |
| 749 e.arguments.length >= 2 && | 747 e.arguments.length >= 2 && |
| 750 isCompoundableOperator(e.operator); | 748 isCompoundableOperator(e.operator); |
| 751 } | 749 } |
| 752 | 750 |
| 753 /// Converts a compoundable operator application into the right-hand side for | 751 /// Converts a compoundable operator application into the right-hand side for |
| 754 /// use in a compound assignment, discarding the left-hand value. | 752 /// use in a compound assignment, discarding the left-hand value. |
| 755 /// | 753 /// |
| 756 /// For example, for `x + y + z` it returns `y + z`. | 754 /// For example, for `x + y + z` it returns `y + z`. |
| 757 Expression contractCompoundableBuiltin(ApplyBuiltinOperator e) { | 755 Expression contractCompoundableBuiltin(ApplyBuiltinOperator e) { |
| 758 assert(isCompoundableBuiltin(e)); | 756 assert(isCompoundableBuiltin(e)); |
| 759 if (e.arguments.length > 2) { | 757 if (e.arguments.length > 2) { |
| 760 assert(e.operator == BuiltinOperator.StringConcatenate); | 758 assert(e.operator == BuiltinOperator.StringConcatenate); |
| 761 return new ApplyBuiltinOperator( | 759 return new ApplyBuiltinOperator( |
| 762 e.operator, | 760 e.operator, e.arguments.skip(1).toList(), e.sourceInformation); |
| 763 e.arguments.skip(1).toList(), | |
| 764 e.sourceInformation); | |
| 765 } else { | 761 } else { |
| 766 return e.arguments[1]; | 762 return e.arguments[1]; |
| 767 } | 763 } |
| 768 } | 764 } |
| 769 | 765 |
| 770 void destroyVariableUse(VariableUse node) { | 766 void destroyVariableUse(VariableUse node) { |
| 771 --node.variable.readCount; | 767 --node.variable.readCount; |
| 772 } | 768 } |
| 773 | 769 |
| 774 Expression visitSetField(SetField node) { | 770 Expression visitSetField(SetField node) { |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 907 } | 903 } |
| 908 } | 904 } |
| 909 | 905 |
| 910 /// If [operator] is a commutable binary operator, returns the commuted | 906 /// If [operator] is a commutable binary operator, returns the commuted |
| 911 /// operator, possibly the operator itself, otherwise returns `null`. | 907 /// operator, possibly the operator itself, otherwise returns `null`. |
| 912 BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) { | 908 BuiltinOperator commuteBinaryOperator(BuiltinOperator operator) { |
| 913 if (isSymmetricOperator(operator)) { | 909 if (isSymmetricOperator(operator)) { |
| 914 // Symmetric operators are their own commutes. | 910 // Symmetric operators are their own commutes. |
| 915 return operator; | 911 return operator; |
| 916 } | 912 } |
| 917 switch(operator) { | 913 switch (operator) { |
| 918 case BuiltinOperator.NumLt: return BuiltinOperator.NumGt; | 914 case BuiltinOperator.NumLt: |
| 919 case BuiltinOperator.NumLe: return BuiltinOperator.NumGe; | 915 return BuiltinOperator.NumGt; |
| 920 case BuiltinOperator.NumGt: return BuiltinOperator.NumLt; | 916 case BuiltinOperator.NumLe: |
| 921 case BuiltinOperator.NumGe: return BuiltinOperator.NumLe; | 917 return BuiltinOperator.NumGe; |
| 922 default: return null; | 918 case BuiltinOperator.NumGt: |
| 919 return BuiltinOperator.NumLt; |
| 920 case BuiltinOperator.NumGe: |
| 921 return BuiltinOperator.NumLe; |
| 922 default: |
| 923 return null; |
| 923 } | 924 } |
| 924 } | 925 } |
| 925 | 926 |
| 926 /// Built-in binary operators are commuted when it is safe and can enable an | 927 /// Built-in binary operators are commuted when it is safe and can enable an |
| 927 /// assignment propagation. For example: | 928 /// assignment propagation. For example: |
| 928 /// | 929 /// |
| 929 /// var x = foo(); | 930 /// var x = foo(); |
| 930 /// var y = bar(); | 931 /// var y = bar(); |
| 931 /// var z = y < x; | 932 /// var z = y < x; |
| 932 /// | 933 /// |
| 933 /// ==> | 934 /// ==> |
| 934 /// | 935 /// |
| 935 /// var z = foo() > bar(); | 936 /// var z = foo() > bar(); |
| 936 /// | 937 /// |
| 937 /// foo() must be evaluated before bar(), so the propagation is only possible | 938 /// foo() must be evaluated before bar(), so the propagation is only possible |
| 938 /// by commuting the operator. | 939 /// by commuting the operator. |
| 939 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 940 Expression visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 940 if (!environment.isEmpty && getLeftHand(environment.last) != null) { | 941 if (!environment.isEmpty && getLeftHand(environment.last) != null) { |
| 941 Variable propagatableVariable = getLeftHand(environment.last); | 942 Variable propagatableVariable = getLeftHand(environment.last); |
| 942 BuiltinOperator commuted = commuteBinaryOperator(node.operator); | 943 BuiltinOperator commuted = commuteBinaryOperator(node.operator); |
| 943 if (commuted != null) { | 944 if (commuted != null) { |
| 944 // Only binary operators can commute. | 945 // Only binary operators can commute. |
| 945 assert(node.arguments.length == 2); | 946 assert(node.arguments.length == 2); |
| 946 Expression left = node.arguments[0]; | 947 Expression left = node.arguments[0]; |
| 947 if (left is VariableUse && propagatableVariable == left.variable) { | 948 if (left is VariableUse && propagatableVariable == left.variable) { |
| 948 Expression right = node.arguments[1]; | 949 Expression right = node.arguments[1]; |
| 949 if (right is This || | 950 if (right is This || |
| 950 (right is VariableUse && | 951 (right is VariableUse && |
| 951 propagatableVariable != right.variable && | 952 propagatableVariable != right.variable && |
| 952 !constantEnvironment.containsKey(right.variable))) { | 953 !constantEnvironment.containsKey(right.variable))) { |
| 953 // An assignment can be propagated if we commute the operator. | 954 // An assignment can be propagated if we commute the operator. |
| 954 node.operator = commuted; | 955 node.operator = commuted; |
| 955 node.arguments[0] = right; | 956 node.arguments[0] = right; |
| 956 node.arguments[1] = left; | 957 node.arguments[1] = left; |
| 957 } | 958 } |
| 958 } | 959 } |
| 959 } | 960 } |
| 960 } | 961 } |
| 961 // Avoid code like `p == (q.f = null)`. JS operators with a constant operand | 962 // Avoid code like `p == (q.f = null)`. JS operators with a constant operand |
| 962 // can sometimes be compiled to a specialized instruction in the JS engine, | 963 // can sometimes be compiled to a specialized instruction in the JS engine, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 975 /// and if [combine] returns E2 the unified statement is equivalence to [t]. | 976 /// and if [combine] returns E2 the unified statement is equivalence to [t]. |
| 976 /// | 977 /// |
| 977 /// It is guaranteed that no side effects occur between the beginning of the | 978 /// It is guaranteed that no side effects occur between the beginning of the |
| 978 /// statement and the position of the combined expression. | 979 /// statement and the position of the combined expression. |
| 979 /// | 980 /// |
| 980 /// Returns null if the statements are too different. | 981 /// Returns null if the statements are too different. |
| 981 /// | 982 /// |
| 982 /// If non-null is returned, the caller MUST discard [s] and [t] and use | 983 /// If non-null is returned, the caller MUST discard [s] and [t] and use |
| 983 /// the returned statement instead. | 984 /// the returned statement instead. |
| 984 Statement combineStatementsInBranches( | 985 Statement combineStatementsInBranches( |
| 985 Statement s, | 986 Statement s, Statement t, Expression condition) { |
| 986 Statement t, | |
| 987 Expression condition) { | |
| 988 if (s is Return && t is Return) { | 987 if (s is Return && t is Return) { |
| 989 return new Return(new Conditional(condition, s.value, t.value)); | 988 return new Return(new Conditional(condition, s.value, t.value)); |
| 990 } | 989 } |
| 991 if (s is ExpressionStatement && t is ExpressionStatement) { | 990 if (s is ExpressionStatement && t is ExpressionStatement) { |
| 992 // Combine the two expressions and the two successor statements. | 991 // Combine the two expressions and the two successor statements. |
| 993 // | 992 // |
| 994 // C ? {E1 ; S1} : {E2 ; S2} | 993 // C ? {E1 ; S1} : {E2 ; S2} |
| 995 // ==> | 994 // ==> |
| 996 // (C ? E1 : E2) : combine(S1, S2) | 995 // (C ? E1 : E2) : combine(S1, S2) |
| 997 // | 996 // |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1042 /// expression if something better can be done. | 1041 /// expression if something better can be done. |
| 1043 /// | 1042 /// |
| 1044 /// In particular, assignments will be merged as follows: | 1043 /// In particular, assignments will be merged as follows: |
| 1045 /// | 1044 /// |
| 1046 /// C ? (v = E1) : (v = E2) | 1045 /// C ? (v = E1) : (v = E2) |
| 1047 /// ==> | 1046 /// ==> |
| 1048 /// v = C ? E1 : E2 | 1047 /// v = C ? E1 : E2 |
| 1049 /// | 1048 /// |
| 1050 /// The latter form is more compact and can also be inlined. | 1049 /// The latter form is more compact and can also be inlined. |
| 1051 CombinedExpressions combineAsConditional( | 1050 CombinedExpressions combineAsConditional( |
| 1052 Expression s, | 1051 Expression s, Expression t, Expression condition) { |
| 1053 Expression t, | |
| 1054 Expression condition) { | |
| 1055 if (s is Assign && t is Assign && s.variable == t.variable) { | 1052 if (s is Assign && t is Assign && s.variable == t.variable) { |
| 1056 Expression values = new Conditional(condition, s.value, t.value); | 1053 Expression values = new Conditional(condition, s.value, t.value); |
| 1057 return new CombinedAssigns(s, t, new CombinedExpressions(values)); | 1054 return new CombinedAssigns(s, t, new CombinedExpressions(values)); |
| 1058 } | 1055 } |
| 1059 return new CombinedExpressions(new Conditional(condition, s, t)); | 1056 return new CombinedExpressions(new Conditional(condition, s, t)); |
| 1060 } | 1057 } |
| 1061 | 1058 |
| 1062 /// Returns a statement equivalent to both [s] and [t], or null if [s] and | 1059 /// Returns a statement equivalent to both [s] and [t], or null if [s] and |
| 1063 /// [t] are incompatible. | 1060 /// [t] are incompatible. |
| 1064 /// If non-null is returned, the caller MUST discard [s] and [t] and use | 1061 /// If non-null is returned, the caller MUST discard [s] and [t] and use |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1076 } | 1073 } |
| 1077 if (s is Continue && t is Continue && s.target == t.target) { | 1074 if (s is Continue && t is Continue && s.target == t.target) { |
| 1078 --t.target.useCount; // Two continues become one. | 1075 --t.target.useCount; // Two continues become one. |
| 1079 return s; | 1076 return s; |
| 1080 } | 1077 } |
| 1081 if (s is Return && t is Return) { | 1078 if (s is Return && t is Return) { |
| 1082 CombinedExpressions values = combineExpressions(s.value, t.value); | 1079 CombinedExpressions values = combineExpressions(s.value, t.value); |
| 1083 if (values != null) { | 1080 if (values != null) { |
| 1084 // TODO(johnniwinther): Handle multiple source informations. | 1081 // TODO(johnniwinther): Handle multiple source informations. |
| 1085 SourceInformation sourceInformation = s.sourceInformation != null | 1082 SourceInformation sourceInformation = s.sourceInformation != null |
| 1086 ? s.sourceInformation : t.sourceInformation; | 1083 ? s.sourceInformation |
| 1084 : t.sourceInformation; |
| 1087 return new Return(values.combined, | 1085 return new Return(values.combined, |
| 1088 sourceInformation: sourceInformation); | 1086 sourceInformation: sourceInformation); |
| 1089 } | 1087 } |
| 1090 } | 1088 } |
| 1091 if (s is ExpressionStatement && t is ExpressionStatement) { | 1089 if (s is ExpressionStatement && t is ExpressionStatement) { |
| 1092 CombinedExpressions values = | 1090 CombinedExpressions values = |
| 1093 combineExpressions(s.expression, t.expression); | 1091 combineExpressions(s.expression, t.expression); |
| 1094 if (values == null) return null; | 1092 if (values == null) return null; |
| 1095 environment.add(values.combined); | 1093 environment.add(values.combined); |
| 1096 Variable leftHand = getLeftHand(values.combined); | 1094 Variable leftHand = getLeftHand(values.combined); |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1220 return polarity ? node.thenStatement : node.elseStatement; | 1218 return polarity ? node.thenStatement : node.elseStatement; |
| 1221 } | 1219 } |
| 1222 | 1220 |
| 1223 void handleForeignCode(ForeignCode node) { | 1221 void handleForeignCode(ForeignCode node) { |
| 1224 // Some arguments will get inserted in a JS code template. The arguments | 1222 // Some arguments will get inserted in a JS code template. The arguments |
| 1225 // will not always be evaluated (e.g. the second placeholder in the template | 1223 // will not always be evaluated (e.g. the second placeholder in the template |
| 1226 // '# && #'). | 1224 // '# && #'). |
| 1227 bool isNullable(int position) => node.nullableArguments[position]; | 1225 bool isNullable(int position) => node.nullableArguments[position]; |
| 1228 | 1226 |
| 1229 int safeArguments = | 1227 int safeArguments = |
| 1230 PlaceholderSafetyAnalysis.analyze(node.codeTemplate.ast, isNullable); | 1228 PlaceholderSafetyAnalysis.analyze(node.codeTemplate.ast, isNullable); |
| 1231 inEmptyEnvironment(() { | 1229 inEmptyEnvironment(() { |
| 1232 for (int i = node.arguments.length - 1; i >= safeArguments; --i) { | 1230 for (int i = node.arguments.length - 1; i >= safeArguments; --i) { |
| 1233 node.arguments[i] = visitExpression(node.arguments[i]); | 1231 node.arguments[i] = visitExpression(node.arguments[i]); |
| 1234 } | 1232 } |
| 1235 }); | 1233 }); |
| 1236 for (int i = safeArguments - 1; i >= 0; --i) { | 1234 for (int i = safeArguments - 1; i >= 0; --i) { |
| 1237 node.arguments[i] = visitExpression(node.arguments[i]); | 1235 node.arguments[i] = visitExpression(node.arguments[i]); |
| 1238 } | 1236 } |
| 1239 } | 1237 } |
| 1240 | 1238 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1384 } | 1382 } |
| 1385 | 1383 |
| 1386 /// Decrement the reference count for [e] if it is a variable use. | 1384 /// Decrement the reference count for [e] if it is a variable use. |
| 1387 void destroyPrimaryExpression(Expression e) { | 1385 void destroyPrimaryExpression(Expression e) { |
| 1388 if (e is VariableUse) { | 1386 if (e is VariableUse) { |
| 1389 --e.variable.readCount; | 1387 --e.variable.readCount; |
| 1390 } else { | 1388 } else { |
| 1391 assert(e is This); | 1389 assert(e is This); |
| 1392 } | 1390 } |
| 1393 } | 1391 } |
| OLD | NEW |