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 |