Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart

Issue 284213002: dart2dart: Logical operators and related rewrite rules in dart_tree. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Flattening of nested ifs is now iterated. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 dart_tree; 5 library dart_tree;
6 6
7 import '../dart2jslib.dart' as dart2js; 7 import '../dart2jslib.dart' as dart2js;
8 import '../elements/elements.dart' 8 import '../elements/elements.dart'
9 show Element, FunctionElement, FunctionSignature, ParameterElement, 9 show Element, FunctionElement, FunctionSignature, ParameterElement,
10 ClassElement; 10 ClassElement;
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 178
179 final bool isPure = false; // invokes toString 179 final bool isPure = false; // invokes toString
180 180
181 accept(Visitor visitor) => visitor.visitConcatenateStrings(this); 181 accept(Visitor visitor) => visitor.visitConcatenateStrings(this);
182 } 182 }
183 183
184 /** 184 /**
185 * A constant. 185 * A constant.
186 */ 186 */
187 class Constant extends Expression { 187 class Constant extends Expression {
188 final dart2js.Constant value; 188 dart2js.Constant value;
189 189
190 Constant(this.value); 190 Constant(this.value);
191 191
192 final bool isPure = true; 192 final bool isPure = true;
193 193
194 accept(Visitor visitor) => visitor.visitConstant(this); 194 accept(Visitor visitor) => visitor.visitConstant(this);
195 } 195 }
196 196
197 /// A conditional expression. 197 /// A conditional expression.
198 class Conditional extends Expression { 198 class Conditional extends Expression {
199 Expression condition; 199 Expression condition;
200 Expression thenExpression; 200 Expression thenExpression;
201 Expression elseExpression; 201 Expression elseExpression;
202 202
203 Conditional(this.condition, this.thenExpression, this.elseExpression); 203 Conditional(this.condition, this.thenExpression, this.elseExpression);
204 204
205 // TODO(asgerf): Repeatedly computing isPure is potentially expensive, 205 // TODO(asgerf): Repeatedly computing isPure is potentially expensive,
206 // but caching isPure in a field is dangerous because a subexpression could 206 // but caching isPure in a field is dangerous because a subexpression could
207 // become impure during a transformation (e.g. assignment propagation). 207 // become impure during a transformation (e.g. assignment propagation).
208 // Improve the situation somehow. 208 // Improve the situation somehow.
209 bool get isPure => condition.isPure && 209 bool get isPure => condition.isPure &&
210 thenExpression.isPure && 210 thenExpression.isPure &&
211 elseExpression.isPure; 211 elseExpression.isPure;
212 212
213 accept(Visitor visitor) => visitor.visitConditional(this); 213 accept(Visitor visitor) => visitor.visitConditional(this);
214 } 214 }
215 215
216 /// An && or || expression. The operator is internally represented as a boolean
217 /// [isAnd] to simplify rewriting of logical operators.
218 class LogicalOperator extends Expression {
219 Expression left;
220 bool isAnd;
221 Expression right;
222
223 LogicalOperator(this.left, this.right, this.isAnd);
224 LogicalOperator.and(this.left, this.right) : isAnd = true;
225 LogicalOperator.or(this.left, this.right) : isAnd = false;
226
227 String get operator => isAnd ? '&&' : '||';
228
229 bool get isPure => left.isPure && right.isPure;
230
231 accept(Visitor visitor) => visitor.visitLogicalOperator(this);
232 }
233
234 /// Logical negation.
235 class Not extends Expression {
236 Expression operand;
237
238 Not(this.operand);
239
240 bool get isPure => operand.isPure;
241
242 accept(Visitor visitor) => visitor.visitNot(this);
243 }
244
216 /** 245 /**
217 * A labeled statement. Breaks to the label within the labeled statement 246 * A labeled statement. Breaks to the label within the labeled statement
218 * target the successor statement. 247 * target the successor statement.
219 */ 248 */
220 class LabeledStatement extends Statement { 249 class LabeledStatement extends Statement {
221 Statement next; 250 Statement next;
222 final Label label; 251 final Label label;
223 Statement body; 252 Statement body;
224 253
225 LabeledStatement(this.label, this.body, this.next) { 254 LabeledStatement(this.label, this.body, this.next) {
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 352
324 abstract class Visitor<S, E> { 353 abstract class Visitor<S, E> {
325 E visitExpression(Expression e) => e.accept(this); 354 E visitExpression(Expression e) => e.accept(this);
326 E visitVariable(Variable node); 355 E visitVariable(Variable node);
327 E visitInvokeStatic(InvokeStatic node); 356 E visitInvokeStatic(InvokeStatic node);
328 E visitInvokeMethod(InvokeMethod node); 357 E visitInvokeMethod(InvokeMethod node);
329 E visitInvokeConstructor(InvokeConstructor node); 358 E visitInvokeConstructor(InvokeConstructor node);
330 E visitConcatenateStrings(ConcatenateStrings node); 359 E visitConcatenateStrings(ConcatenateStrings node);
331 E visitConstant(Constant node); 360 E visitConstant(Constant node);
332 E visitConditional(Conditional node); 361 E visitConditional(Conditional node);
362 E visitLogicalOperator(LogicalOperator node);
363 E visitNot(Not node);
333 364
334 S visitStatement(Statement s) => s.accept(this); 365 S visitStatement(Statement s) => s.accept(this);
335 S visitLabeledStatement(LabeledStatement node); 366 S visitLabeledStatement(LabeledStatement node);
336 S visitAssign(Assign node); 367 S visitAssign(Assign node);
337 S visitReturn(Return node); 368 S visitReturn(Return node);
338 S visitBreak(Break node); 369 S visitBreak(Break node);
339 S visitIf(If node); 370 S visitIf(If node);
340 S visitExpressionStatement(ExpressionStatement node); 371 S visitExpressionStatement(ExpressionStatement node);
341 } 372 }
342 373
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
581 compiler.internalError(compiler.currentElement, 'Unexpected IR node.'); 612 compiler.internalError(compiler.currentElement, 'Unexpected IR node.');
582 return null; 613 return null;
583 } 614 }
584 615
585 Expression visitIsTrue(ir.IsTrue node) { 616 Expression visitIsTrue(ir.IsTrue node) {
586 return variables[node.value.definition]; 617 return variables[node.value.definition];
587 } 618 }
588 } 619 }
589 620
590 /** 621 /**
591 * Performs the following three transformations on the tree: 622 * Performs the following three transformations on the tree:
Kevin Millikin (Google) 2014/05/19 11:36:41 following three transformations ==> following tran
asgerf 2014/05/19 13:29:07 Done.
592 * - Assignment propagation 623 * - Assignment propagation
593 * - If-to-conditional conversion 624 * - If-to-conditional conversion
625 * - Flatten nested ifs
594 * - Break inlining 626 * - Break inlining
627 * - Redirect breaks
595 * 628 *
596 * The above transformations are performed in the same phase because each 629 * The above transformations are performed in the same phase because each
597 * transformation can introduce redexes of one of the others. 630 * transformation can introduce redexes of one of the others.
598 * 631 *
599 * 632 *
600 * ASSIGNMENT PROPAGATION: 633 * ASSIGNMENT PROPAGATION:
601 * Single-use definitions are propagated to their use site when possible. 634 * Single-use definitions are propagated to their use site when possible.
602 * For example: 635 * For example:
603 * 636 *
604 * { v0 = foo(); return v0; } 637 * { v0 = foo(); return v0; }
(...skipping 24 matching lines...) Expand all
629 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L } 662 * if (v0) { v1 = foo(); break L } else { v1 = bar(); break L }
630 * ==> 663 * ==>
631 * { v1 = v0 ? foo() : bar(); break L } 664 * { v1 = v0 ? foo() : bar(); break L }
632 * 665 *
633 * This can lead to inlining of L, which in turn can lead to further propagation 666 * This can lead to inlining of L, which in turn can lead to further propagation
634 * of the variable v1. 667 * of the variable v1.
635 * 668 *
636 * See [visitIf]. 669 * See [visitIf].
637 * 670 *
638 * 671 *
672 * FLATTEN NESTED IFS:
673 * An if inside an if is converted to an if with a logical operator.
674 * For example:
675 *
676 * if (E1) { if (E2) {S} else break L } else break L
677 * ==>
678 * if (E1 && E2) {S} else break L
679 *
680 * This may lead to inlining of L.
681 *
682 *
639 * BREAK INLINING: 683 * BREAK INLINING:
640 * Single-use labels are inlined at [Break] statements. 684 * Single-use labels are inlined at [Break] statements.
641 * For example: 685 * For example:
642 * 686 *
643 * L0: { v0 = foo(); break L0 }; return v0; 687 * L0: { v0 = foo(); break L0 }; return v0;
644 * ==> 688 * ==>
645 * v0 = foo(); return v0; 689 * v0 = foo(); return v0;
646 * 690 *
647 * This can lead to propagation of v0. 691 * This can lead to propagation of v0.
648 * 692 *
649 * See [visitBreak] and [visitLabeledStatement]. 693 * See [visitBreak] and [visitLabeledStatement].
694 *
695 *
696 * REDIRECT BREAKS:
697 * Labeled statements whose next is a break become flattened and all breaks
698 * to their label are redirected.
699 * For example:
700 *
701 * L0: {... break L0 ...}; break L1
702 * ==>
703 * {... break L1 ...}
704 *
705 * This may trigger a flattening of nested ifs in case the eliminated label
706 * separated two ifs.
650 */ 707 */
651 class TreeRewriter extends Visitor<Statement, Expression> { 708 class TreeRewriter extends Visitor<Statement, Expression> {
652 // The binding environment. The rightmost element of the list is the nearest 709 // The binding environment. The rightmost element of the list is the nearest
653 // enclosing binding. 710 // enclosing binding.
654 // We use null to mark an impure expressions that does not bind a variable. 711 // We use null to mark an impure expressions that does not bind a variable.
655 List<Assign> environment; 712 List<Assign> environment;
656 713
657 void apply(FunctionDefinition definition) { 714 /// Substitution map for labels. Any break to a label L should be substituted
715 /// for a break to L' if L maps to L'.
716 Map<Label,Label> labelRedirects = <Label,Label>{};
Kevin Millikin (Google) 2014/05/19 11:36:41 Space after comma: Map<Label, Label>
asgerf 2014/05/19 13:29:07 Done.
717
718 /// Returns the redirect target of [label] or [label] itself if it should not
719 /// be redirected.
720 Label redirect(Label label) {
721 Label newTarget = labelRedirects[label];
722 return newTarget != null ? newTarget : label;
723 }
724
725 void rewrite(FunctionDefinition definition) {
658 environment = <Assign>[]; 726 environment = <Assign>[];
659 definition.body = visitStatement(definition.body); 727 definition.body = visitStatement(definition.body);
660 728
661 // TODO(kmillikin): Allow definitions that are not propagated. Here, 729 // TODO(kmillikin): Allow definitions that are not propagated. Here,
662 // this means rebuilding the binding with a recursively unnamed definition, 730 // this means rebuilding the binding with a recursively unnamed definition,
663 // or else introducing a variable definition and an assignment. 731 // or else introducing a variable definition and an assignment.
664 assert(environment.isEmpty); 732 assert(environment.isEmpty);
665 } 733 }
666 734
667 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this); 735 Expression visitExpression(Expression e) => e.processed ? e : e.accept(this);
(...skipping 29 matching lines...) Expand all
697 } else if (!environment[i].definition.isPure) { 765 } else if (!environment[i].definition.isPure) {
698 // Once the first impure definition is seen, impure definitions should 766 // Once the first impure definition is seen, impure definitions should
699 // no longer be propagated. Continue searching for a pure definition. 767 // no longer be propagated. Continue searching for a pure definition.
700 seenImpure = true; 768 seenImpure = true;
701 } 769 }
702 } 770 }
703 // If the definition could not be propagated, leave the variable use. 771 // If the definition could not be propagated, leave the variable use.
704 return node; 772 return node;
705 } 773 }
706 774
707 Statement visitLabeledStatement(LabeledStatement node) {
708 node.body = visitStatement(node.body);
709 if (node.label.breakCount == 0) {
710 // If the break was inlined, eliminate the label.
711 return node.body;
712 }
713 node.next = visitStatement(node.next);
714 return node;
715 }
716 775
717 Statement visitAssign(Assign node) { 776 Statement visitAssign(Assign node) {
718 environment.add(node); 777 environment.add(node);
719 Statement next = visitStatement(node.next); 778 Statement next = visitStatement(node.next);
720 779
721 if (!environment.isEmpty && environment.last == node) { 780 if (!environment.isEmpty && environment.last == node) {
722 // The definition could not be propagated. Residualize the let binding. 781 // The definition could not be propagated. Residualize the let binding.
723 node.next = next; 782 node.next = next;
724 environment.removeLast(); 783 environment.removeLast();
725 node.definition = visitExpression(node.definition); 784 node.definition = visitExpression(node.definition);
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
763 node.condition = visitExpression(node.condition); 822 node.condition = visitExpression(node.condition);
764 823
765 environment.add(null); // impure expressions may not propagate across branch 824 environment.add(null); // impure expressions may not propagate across branch
766 node.thenExpression = visitExpression(node.thenExpression); 825 node.thenExpression = visitExpression(node.thenExpression);
767 node.elseExpression = visitExpression(node.elseExpression); 826 node.elseExpression = visitExpression(node.elseExpression);
768 environment.removeLast(); 827 environment.removeLast();
769 828
770 return node; 829 return node;
771 } 830 }
772 831
832 Expression visitLogicalOperator(LogicalOperator node) {
833 node.left = visitExpression(node.left);
834
835 environment.add(null); // impure expressions may not propagate across branch
836 node.right = visitExpression(node.right);
837 environment.removeLast();
838
839 return node;
840 }
841
842 Expression visitNot(Not node) {
843 node.operand = visitExpression(node.operand);
844 return node;
845 }
846
773 Statement visitReturn(Return node) { 847 Statement visitReturn(Return node) {
774 node.value = visitExpression(node.value); 848 node.value = visitExpression(node.value);
775 return node; 849 return node;
776 } 850 }
777 851
778 852
779 Statement visitBreak(Break node) { 853 Statement visitBreak(Break node) {
854 // Redirect through chain of breaks.
855 // Note that breakCount was accounted for at visitLabeledStatement.
856 node.target = redirect(node.target);
780 if (node.target.breakCount == 1) { 857 if (node.target.breakCount == 1) {
781 --node.target.breakCount; 858 --node.target.breakCount;
782 return visitStatement(node.target.binding.next); 859 return visitStatement(node.target.binding.next);
783 } 860 }
784 return node; 861 return node;
785 } 862 }
786 863
864 Statement visitLabeledStatement(LabeledStatement node) {
865 if (node.next is Break) {
866 // Eliminate label if next is just a break statement
867 // Breaks to this label are redirected to the outer label.
868 // Note that breakCount for the two labels is updated proactively here
869 // so breaks can reliably tell if they should inline their target.
870 Break next = node.next;
871 Label newTarget = labelRedirects[node.label] = redirect(next.target);
Kevin Millikin (Google) 2014/05/19 11:36:41 More readable: Label newTarget = redirect(next.ta
asgerf 2014/05/19 13:29:07 Done.
872 newTarget.breakCount += node.label.breakCount;
873 node.label.breakCount = 0;
874 Statement result = visitStatement(node.body);
875 labelRedirects.remove(node.label); // Save some space.
Kevin Millikin (Google) 2014/05/19 11:36:41 I wouldn't bother doing this here (or at all).
876 return result;
877 }
878
879 node.body = visitStatement(node.body);
880
881 if (node.label.breakCount == 0) {
882 // Eliminate the label if next was inlined at a break
883 return node.body;
884 }
885
886 node.next = visitStatement(node.next);
887 return node;
888 }
889
787 Statement visitIf(If node) { 890 Statement visitIf(If node) {
788 node.condition = visitExpression(node.condition); 891 node.condition = visitExpression(node.condition);
789 892
790 environment.add(null); // impure expressions may not propagate across branch 893 environment.add(null); // impure expressions may not propagate across branch
791 node.thenStatement = visitStatement(node.thenStatement); 894 node.thenStatement = visitStatement(node.thenStatement);
792 node.elseStatement = visitStatement(node.elseStatement); 895 node.elseStatement = visitStatement(node.elseStatement);
793 environment.removeLast(); 896 environment.removeLast();
794 897
898 // Repeatedly try to collapse nested ifs.
899 // The transformation is shrinking (destroys an if) so it remains linear.
900 // Here is an example where more than one iteration is required:
901 //
902 // if (E1)
903 // if (E2) break L2 else break L1
904 // else
905 // break L1
906 //
907 // L1.target ::=
908 // if (E3) S else break L2
909 //
910 // After first collapse:
911 //
912 // if (E1 && E2)
913 // break L2
914 // else
915 // {if (E3) S else break L2} (inlined from break L1)
916 //
917 // We can then do another collapse using the inlined nested if.
918 var changed = true;
919 while (changed) {
920 changed = false;
921 changed = changed || tryCollapseIf(node, true, true);
922 changed = changed || tryCollapseIf(node, true, false);
923 changed = changed || tryCollapseIf(node, false, true);
924 changed = changed || tryCollapseIf(node, false, false);
925 }
926
795 Statement reduced = combineStatementsWithSubexpressions( 927 Statement reduced = combineStatementsWithSubexpressions(
796 node.thenStatement, 928 node.thenStatement,
797 node.elseStatement, 929 node.elseStatement,
798 (t,f) => new Conditional(node.condition, t, f)..processed = true); 930 (t,f) => new Conditional(node.condition, t, f)..processed = true);
799 if (reduced != null) { 931 if (reduced != null) {
800 if (reduced.next is Break) { 932 if (reduced.next is Break) {
801 // In case the break can now be inlined. 933 // In case the break can now be inlined.
802 reduced = visitStatement(reduced); 934 reduced = visitStatement(reduced);
803 } 935 }
804 return reduced; 936 return reduced;
(...skipping 11 matching lines...) Expand all
816 if (!node.expression.isPure) { 948 if (!node.expression.isPure) {
817 environment.add(null); // insert impurity marker (TODO: refactor) 949 environment.add(null); // insert impurity marker (TODO: refactor)
818 } 950 }
819 node.next = visitStatement(node.next); 951 node.next = visitStatement(node.next);
820 if (!node.expression.isPure) { 952 if (!node.expression.isPure) {
821 environment.removeLast(); 953 environment.removeLast();
822 } 954 }
823 return node; 955 return node;
824 } 956 }
825 957
826
827 /// If [s] and [t] are similar statements we extract their subexpressions 958 /// If [s] and [t] are similar statements we extract their subexpressions
828 /// and returns a new statement of the same type using expressions combined 959 /// and returns a new statement of the same type using expressions combined
829 /// with the [combine] callback. For example: 960 /// with the [combine] callback. For example:
830 /// 961 ///
831 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) 962 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2)
832 /// 963 ///
833 /// If [combine] returns E1 then the unified statement is equivalent to [s], 964 /// If [combine] returns E1 then the unified statement is equivalent to [s],
834 /// and if [combine] returns E2 the unified statement is equivalence to [t]. 965 /// and if [combine] returns E2 the unified statement is equivalence to [t].
835 /// 966 ///
836 /// It is guaranteed that no side effects occur between the beginning of the 967 /// It is guaranteed that no side effects occur between the beginning of the
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
888 if (e1 == e2) { // Detect same variable reference 1019 if (e1 == e2) { // Detect same variable reference
889 // TODO(asgerf): This might turn the variable into a single-use, 1020 // TODO(asgerf): This might turn the variable into a single-use,
890 // but we currently don't discover this. 1021 // but we currently don't discover this.
891 return true; 1022 return true;
892 } 1023 }
893 if (e1 is Constant && e2 is Constant) { 1024 if (e1 is Constant && e2 is Constant) {
894 return e1.value == e2.value; 1025 return e1.value == e2.value;
895 } 1026 }
896 return false; 1027 return false;
897 } 1028 }
1029
1030 /// Try to collapse nested ifs using && and || expressions.
1031 /// For example:
1032 ///
1033 /// if (E1) { if (E2) S else break L } else break L
1034 /// ==>
1035 /// if (E1 && E2) S else break L
1036 ///
1037 /// [branch1] and [branch2] control the position of the S statement.
1038 ///
1039 /// Returns true if another collapse redex might have been introduced.
1040 bool tryCollapseIf(If outerIf, bool branch1, bool branch2) {
Kevin Millikin (Google) 2014/05/19 11:36:41 I don't think the caller should have to enumerate
1041 // NOTE: We name variables here as if S is in the then-then position.
1042 Statement outerThen = getBranch(outerIf, branch1);
1043 Statement outerElse = getBranch(outerIf, !branch1);
1044 if (outerThen is If && outerElse is Break) {
1045 If innerIf = outerThen;
1046 Statement innerThen = getBranch(innerIf, branch2);
1047 Statement innerElse = getBranch(innerIf, !branch2);
1048 if (innerElse is Break && innerElse.target == outerElse.target) {
1049 // We always put S in the then branch of the result, and adjust the
1050 // condition expression if S was actually found in the else branch(es).
1051 outerIf.condition = new LogicalOperator.and(
1052 makeCondition(outerIf.condition, branch1),
1053 makeCondition(innerIf.condition, branch2));
1054 outerIf.thenStatement = innerThen;
1055 --innerElse.target.breakCount;
1056
1057 // Try to inline the remaining break
1058 environment.add(null); // Do not propagate impure definitions
1059 outerIf.elseStatement = visitStatement(outerElse);
1060 environment.removeLast();
1061
1062 return outerIf.elseStatement is If && innerThen is Break;
1063 }
1064 }
1065 return false;
1066 }
1067
1068 Expression makeCondition(Expression e, bool polarity) {
1069 return polarity ? e : new Not(e);
1070 }
1071
1072 Statement getBranch(If node, bool polarity) {
1073 return polarity ? node.thenStatement : node.elseStatement;
1074 }
898 } 1075 }
899 1076
1077
1078
1079 /// Rewrites logical expressions to be more compact.
1080 ///
1081 /// In this class an expression is said to occur in "boolean context" if
1082 /// its result is immediately applied to boolean conversion.
1083 ///
1084 /// IF STATEMENTS:
1085 ///
1086 /// We apply the following two rule to [If] statements (see [visitIf]).
Kevin Millikin (Google) 2014/05/19 11:36:41 two rule ==> rules
1087 ///
1088 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted)
1089 /// if (!E) S1 else S2 ==> if (E) S2 else S1 (unless previous rule applied)
1090 ///
1091 /// NEGATION:
1092 ///
1093 /// De Morgan's Laws are used to rewrite negations of logical operators so
1094 /// negations are closer to the root:
1095 ///
1096 /// !x && !y --> !(x || y)
1097 ///
1098 /// This is to enable other rewrites, such branch swapping in an if. In some
Kevin Millikin (Google) 2014/05/19 11:36:41 such branch ==> such as branch
1099 /// contexts, the rule is reversed because we do not expect to apply a rewrite
1100 /// rule to the result. For example:
1101 ///
1102 /// z = !(x || y) ==> z = !x && !y;
1103 ///
1104 /// CONDITIONALS:
1105 ///
1106 /// Conditionals with boolean constant operands occur frequently in the input.
1107 /// They can often the re-written to logical operators, for instance:
1108 ///
1109 /// if (x ? y : false) S1 else S2
1110 /// ==>
1111 /// if (x && y) S1 else S2
1112 ///
1113 /// Conditionals are tricky to rewrite when they occur out of boolean context.
1114 /// Here we must apply more conservative rules, such as:
1115 ///
1116 /// x ? true : false ==> !!x
1117 ///
1118 /// If an operand is known to be a boolean, we can introduce a logical operator:
1119 ///
1120 /// x ? y : false ==> x && y (if y is known to be a boolean)
1121 ///
1122 /// The following sequence of rewrites demonstrates the merit of these rules:
1123 ///
1124 /// x ? (y ? true : false) : false
1125 /// x ? !!y : false (double negation introduced by [toBoolean])
1126 /// x && !!y (!!y validated by [isBooleanValued])
1127 /// x && y (double negation removed by [putInBooleanContext])
1128 ///
1129 class LogicalRewriter extends Visitor<Statement, Expression> {
1130
1131 /// Statement to be executed next by natural fallthrough. Although fallthrough
1132 /// is not introduced in this phase, we need to reason about fallthrough when
1133 /// evaluating the benefit of swapping the branches of an [If].
1134 Statement fallthrough;
1135
1136 void rewrite(FunctionDefinition definition) {
1137 definition.body = visitStatement(definition.body);
1138 }
1139
1140 Statement visitLabeledStatement(LabeledStatement node) {
1141 Statement savedFallthrough = fallthrough;
1142 fallthrough = node.next;
1143 node.body = visitStatement(node.body);
1144 fallthrough = savedFallthrough;
1145 node.next = visitStatement(node.next);
1146 return node;
1147 }
1148
1149 Statement visitAssign(Assign node) {
1150 node.definition = visitExpression(node.definition);
1151 node.next = visitStatement(node.next);
1152 return node;
1153 }
1154
1155 Statement visitReturn(Return node) {
1156 node.value = visitExpression(node.value);
1157 return node;
1158 }
1159
1160 Statement visitBreak(Break node) {
1161 return node;
1162 }
1163
1164 bool isFallthroughBreak(Statement node) {
1165 return node is Break && node.target.binding.next == fallthrough;
1166 }
1167
1168 Statement visitIf(If node) {
1169 // If one of the branches is empty (i.e. just a fallthrough), then that
1170 // branch should preferrably be the 'else' so we won't have to print it.
1171 // In other words, we wish to perform this rewrite:
1172 // if (E) {} else {S}
1173 // ==>
1174 // if (!E) {S}
1175 // In the tree language, empty statements do not exist yet, so we must check
1176 // if one branch contains a break that can be eliminated by fallthrough.
1177
1178 // Swap branches if then is a fallthrough break.
1179 if (isFallthroughBreak(node.thenStatement)) {
1180 node.condition = new Not(node.condition);
1181 Statement tmp = node.thenStatement;
1182 node.thenStatement = node.elseStatement;
1183 node.elseStatement = tmp;
1184 }
1185
1186 // Can the else part be eliminated?
1187 // (Either due to the above swap or if the break was already there).
1188 bool emptyElse = isFallthroughBreak(node.elseStatement);
1189
1190 node.condition = makeCondition(node.condition, true, liftNots: !emptyElse);
1191 node.thenStatement = visitStatement(node.thenStatement);
1192 node.elseStatement = visitStatement(node.elseStatement);
1193
1194 // If neither branch is empty, eliminate a negation in the condition
1195 // if (!E) S1 else S2
1196 // ==>
1197 // if (E) S2 else S1
1198 if (!emptyElse && node.condition is Not) {
1199 node.condition = (node.condition as Not).operand;
1200 Statement tmp = node.thenStatement;
1201 node.thenStatement = node.elseStatement;
1202 node.elseStatement = tmp;
1203 }
1204
1205 return node;
1206 }
1207
1208 Statement visitExpressionStatement(ExpressionStatement node) {
1209 // TODO(asgerf): in non-checked mode we can remove Not from the expression.
1210 node.expression = visitExpression(node.expression);
1211 node.next = visitStatement(node.next);
1212 return node;
1213 }
1214
1215
1216 Expression visitVariable(Variable node) {
1217 return node;
1218 }
1219
1220 Expression visitInvokeStatic(InvokeStatic node) {
1221 for (int i=0; i<node.arguments.length; i++) {
Kevin Millikin (Google) 2014/05/19 11:36:41 Spaces around = and <. Also below. Maybe we shou
asgerf 2014/05/19 13:29:07 Good idea. I'll look at that in another CL.
1222 node.arguments[i] = visitExpression(node.arguments[i]);
1223 }
1224 return node;
1225 }
1226
1227 Expression visitInvokeMethod(InvokeMethod node) {
1228 node.receiver = visitExpression(node.receiver);
1229 for (int i=0; i<node.arguments.length; i++) {
1230 node.arguments[i] = visitExpression(node.arguments[i]);
1231 }
1232 return node;
1233 }
1234
1235 Expression visitInvokeConstructor(InvokeConstructor node) {
1236 for (int i=0; i<node.arguments.length; i++) {
1237 node.arguments[i] = visitExpression(node.arguments[i]);
1238 }
1239 return node;
1240 }
1241
1242 Expression visitConcatenateStrings(ConcatenateStrings node) {
1243 for (int i=0; i<node.arguments.length; i++) {
1244 node.arguments[i] = visitExpression(node.arguments[i]);
1245 }
1246 return node;
1247 }
1248
1249 Expression visitConstant(Constant node) {
1250 return node;
1251 }
1252
1253 Expression visitNot(Not node) {
1254 return toBoolean(makeCondition(node.operand, false, liftNots: false));
1255 }
1256
1257 Expression visitConditional(Conditional node) {
1258 // node.condition will be visited after the then and else parts, because its
1259 // polarity depends on what rewrite we use.
1260 node.thenExpression = visitExpression(node.thenExpression);
1261 node.elseExpression = visitExpression(node.elseExpression);
1262
1263 // In the following, we must take care not to eliminate or introduce a
1264 // boolean conversion.
1265
1266 // x ? true : false --> !!x
1267 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) {
1268 return toBoolean(makeCondition(node.condition, true, liftNots: false));
1269 }
1270 // x ? false : true --> !x
1271 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) {
1272 return toBoolean(makeCondition(node.condition, false, liftNots: false));
1273 }
1274
1275 // x ? y : false ==> x && y (if y is known to be a boolean)
1276 if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) {
1277 return new LogicalOperator.and(
1278 makeCondition(node.condition, true, liftNots:false),
1279 putInBooleanContext(node.thenExpression));
1280 }
1281 // x ? y : true ==> !x || y (if y is known to be a boolean)
1282 if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) {
1283 return new LogicalOperator.or(
1284 makeCondition(node.condition, false, liftNots: false),
1285 putInBooleanContext(node.thenExpression));
1286 }
1287 // x ? true : y ==> x || y (if y if known to be boolean)
1288 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) {
1289 return new LogicalOperator.or(
1290 makeCondition(node.condition, true, liftNots: false),
1291 putInBooleanContext(node.elseExpression));
1292 }
1293 // x ? false : y ==> !x && y (if y is known to be a boolean)
1294 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) {
1295 return new LogicalOperator.and(
1296 makeCondition(node.condition, false, liftNots: false),
1297 putInBooleanContext(node.elseExpression));
1298 }
1299
1300 node.condition = makeCondition(node.condition, true);
1301
1302 // !x ? y : z ==> x ? z : y
1303 if (node.condition is Not) {
1304 node.condition = (node.condition as Not).operand;
1305 Expression tmp = node.thenExpression;
1306 node.thenExpression = node.elseExpression;
1307 node.elseExpression = tmp;
1308 }
1309
1310 return node;
1311 }
1312 Expression visitLogicalOperator(LogicalOperator node) {
Kevin Millikin (Google) 2014/05/19 11:36:41 Blank line between methods.
1313 node.left = makeCondition(node.left, true);
1314 node.right = makeCondition(node.right, true);
1315 return node;
1316 }
1317
1318 /// True if the given expression is known to evaluate to a boolean.
1319 /// This will not recursively traverse [Conditional] expressions, but if
1320 /// applied to the result of [visitExpression] conditionals will have been
1321 /// rewritten anyway.
1322 bool isBooleanValued(Expression e) {
1323 return isTrue(e) || isFalse(e) || e is Not || e is LogicalOperator;
1324 }
1325
1326 /// Rewrite an expression that was originally processed in a non-boolean
1327 /// context.
1328 Expression putInBooleanContext(Expression e) {
1329 if (e is Not && e.operand is Not) {
1330 return (e.operand as Not).operand;
1331 } else {
1332 return e;
1333 }
1334 }
1335
1336 /// Forces a boolean conversion of the given expression.
1337 Expression toBoolean(Expression e) {
1338 if (isBooleanValued(e))
1339 return e;
1340 else
1341 return new Not(new Not(e));
1342 }
1343
1344 /// Creates an equivalent boolean expression. The expression must occur in a
Kevin Millikin (Google) 2014/05/19 11:36:41 equivalent ==> equivalent simplified, perhaps? Re
1345 /// context where its result is immediately subject to boolean conversion.
1346 /// If [polarity] if false, the negated condition will be created instead.
1347 /// If [liftNots] is true (default) then Not expressions will be lifted toward
1348 /// the root the condition so they can be eliminated by the caller.
1349 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) {
1350 if (e is Not) {
1351 // !!E ==> E
1352 return makeCondition(e.operand, !polarity, liftNots: liftNots);
1353 }
1354 if (e is LogicalOperator) {
1355 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y
1356 e.left = makeCondition(e.left, polarity);
1357 e.right = makeCondition(e.right, polarity);
1358 if (!polarity) {
1359 e.isAnd = !e.isAnd;
1360 }
1361 // !x && !y ==> !(x || y) (only if lifting nots)
1362 if (e.left is Not && e.right is Not && liftNots) {
1363 e.left = (e.left as Not).operand;
1364 e.right = (e.right as Not).operand;
1365 e.isAnd = !e.isAnd;
1366 return new Not(e);
1367 }
1368 return e;
1369 }
1370 if (e is Conditional) {
1371 // x ? true : false ==> x
1372 if (isTrue(e.thenExpression) && isFalse(e.elseExpression)) {
1373 return makeCondition(e.condition, polarity);
1374 }
1375 // x ? false : true ==> !x
1376 if (isFalse(e.thenExpression) && isTrue(e.elseExpression)) {
1377 return makeCondition(e.condition, !polarity);
1378 }
1379 // x ? true : y ==> x || y
Kevin Millikin (Google) 2014/05/19 11:36:41 I could well be missing something here, but I thin
1380 if (isTrue(e.thenExpression)) {
1381 return new LogicalOperator.or(
1382 makeCondition(e.condition, polarity),
1383 makeCondition(e.elseExpression, polarity));
1384 }
1385 // x ? false : y ==> !x && y
Kevin Millikin (Google) 2014/05/19 11:36:41 Likewise, if !polarity this should be ||. That is
1386 if (isFalse(e.thenExpression)) {
1387 return new LogicalOperator.and(
1388 makeCondition(e.condition, !polarity),
1389 makeCondition(e.elseExpression, polarity));
1390 }
1391 // x ? y : true ==> !x || y
1392 if (isTrue(e.elseExpression)) {
1393 return new LogicalOperator.or(
1394 makeCondition(e.condition, !polarity),
1395 makeCondition(e.thenExpression, polarity));
1396 }
1397 // x ? y : false ==> x && y
1398 if (isFalse(e.elseExpression)) {
1399 return new LogicalOperator.and(
1400 makeCondition(e.condition, polarity),
1401 makeCondition(e.thenExpression, polarity));
1402 }
1403
1404 // Rewrite individual subconditions
1405 // Handle polarity by: !(x ? y : z) ==> x ? !y : !z
1406 e.condition = makeCondition(e.condition, true);
1407 e.thenExpression = makeCondition(e.thenExpression, polarity);
1408 e.elseExpression = makeCondition(e.elseExpression, polarity);
1409
1410 // !x ? y : z ==> x ? z : y
1411 if (e.condition is Not) {
1412 e.condition = (e.condition as Not).operand;
1413 Expression tmp = e.thenExpression;
1414 e.thenExpression = e.elseExpression;
1415 e.elseExpression = tmp;
1416 }
1417 // x ? !y : !z ==> !(x ? y : z) (only if lifting nots)
1418 if (e.thenExpression is Not && e.elseExpression is Not && liftNots) {
1419 e.thenExpression = (e.thenExpression as Not).operand;
1420 e.elseExpression = (e.elseExpression as Not).operand;
1421 return new Not(e);
1422 }
1423 return e;
1424 }
1425 if (e is Constant && e.value is dart2js.BoolConstant) {
1426 // !true ==> false
1427 if (!polarity) {
1428 e.value = (e.value as dart2js.BoolConstant).negate();
1429 }
1430 return e;
1431 }
1432 e = visitExpression(e);
1433 return polarity ? e : new Not(e);
1434 }
1435
1436 bool isTrue(Expression e) {
1437 return e is Constant && e.value is dart2js.TrueConstant;
1438 }
1439
1440 bool isFalse(Expression e) {
1441 return e is Constant && e.value is dart2js.FalseConstant;
1442 }
1443
1444 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698