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

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: Bugfix in iteration of tryCollapseIf 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 26 matching lines...) Expand all
37 abstract class Node { 37 abstract class Node {
38 } 38 }
39 39
40 /** 40 /**
41 * The base class of [Expression]s. 41 * The base class of [Expression]s.
42 */ 42 */
43 abstract class Expression extends Node { 43 abstract class Expression extends Node {
44 bool get isPure; 44 bool get isPure;
45 accept(Visitor v); 45 accept(Visitor v);
46 46
47 /// Temporary variable used by [TreeRewriter]. 47 /// Temporary variable used by [StatementRewriter].
48 /// If set to true, this expression has already had enclosing assignments 48 /// If set to true, this expression has already had enclosing assignments
49 /// propagated into its variables, and should not be processed again. 49 /// propagated into its variables, and should not be processed again.
50 /// It is only set for expressions that are known to be in risk of redundant 50 /// It is only set for expressions that are known to be in risk of redundant
51 /// processing. 51 /// processing.
52 bool processed = false; 52 bool processed = false;
53 } 53 }
54 54
55 abstract class Statement extends Node { 55 abstract class Statement extends Node {
56 Statement get next; 56 Statement get next;
57 void set next(Statement s); 57 void set next(Statement s);
(...skipping 120 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 transformations on the tree:
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 all eliminate statements from the tree, and may
597 * transformation can introduce redexes of one of the others. 630 * introduce redexes of each other.
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; }
605 * ==> 638 * ==>
606 * return foo() 639 * return foo()
607 * 640 *
(...skipping 21 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 StatementRewriter 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>{};
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 = redirect(next.target);
872 labelRedirects[node.label] = newTarget;
873 newTarget.breakCount += node.label.breakCount;
874 node.label.breakCount = 0;
875 Statement result = visitStatement(node.body);
876 labelRedirects.remove(node.label); // Save some space.
877 return result;
878 }
879
880 node.body = visitStatement(node.body);
881
882 if (node.label.breakCount == 0) {
883 // Eliminate the label if next was inlined at a break
884 return node.body;
885 }
886
887 node.next = visitStatement(node.next);
888 return node;
889 }
890
787 Statement visitIf(If node) { 891 Statement visitIf(If node) {
788 node.condition = visitExpression(node.condition); 892 node.condition = visitExpression(node.condition);
789 893
790 environment.add(null); // impure expressions may not propagate across branch 894 environment.add(null); // impure expressions may not propagate across branch
791 node.thenStatement = visitStatement(node.thenStatement); 895 node.thenStatement = visitStatement(node.thenStatement);
792 node.elseStatement = visitStatement(node.elseStatement); 896 node.elseStatement = visitStatement(node.elseStatement);
793 environment.removeLast(); 897 environment.removeLast();
794 898
899 tryCollapseIf(node);
900
795 Statement reduced = combineStatementsWithSubexpressions( 901 Statement reduced = combineStatementsWithSubexpressions(
796 node.thenStatement, 902 node.thenStatement,
797 node.elseStatement, 903 node.elseStatement,
798 (t,f) => new Conditional(node.condition, t, f)..processed = true); 904 (t,f) => new Conditional(node.condition, t, f)..processed = true);
799 if (reduced != null) { 905 if (reduced != null) {
800 if (reduced.next is Break) { 906 if (reduced.next is Break) {
801 // In case the break can now be inlined. 907 // In case the break can now be inlined.
802 reduced = visitStatement(reduced); 908 reduced = visitStatement(reduced);
803 } 909 }
804 return reduced; 910 return reduced;
(...skipping 11 matching lines...) Expand all
816 if (!node.expression.isPure) { 922 if (!node.expression.isPure) {
817 environment.add(null); // insert impurity marker (TODO: refactor) 923 environment.add(null); // insert impurity marker (TODO: refactor)
818 } 924 }
819 node.next = visitStatement(node.next); 925 node.next = visitStatement(node.next);
820 if (!node.expression.isPure) { 926 if (!node.expression.isPure) {
821 environment.removeLast(); 927 environment.removeLast();
822 } 928 }
823 return node; 929 return node;
824 } 930 }
825 931
826
827 /// If [s] and [t] are similar statements we extract their subexpressions 932 /// If [s] and [t] are similar statements we extract their subexpressions
828 /// and returns a new statement of the same type using expressions combined 933 /// and returns a new statement of the same type using expressions combined
829 /// with the [combine] callback. For example: 934 /// with the [combine] callback. For example:
830 /// 935 ///
831 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2) 936 /// combineStatements(Return E1, Return E2) = Return combine(E1, E2)
832 /// 937 ///
833 /// If [combine] returns E1 then the unified statement is equivalent to [s], 938 /// 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]. 939 /// and if [combine] returns E2 the unified statement is equivalence to [t].
835 /// 940 ///
836 /// It is guaranteed that no side effects occur between the beginning of the 941 /// 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 993 if (e1 == e2) { // Detect same variable reference
889 // TODO(asgerf): This might turn the variable into a single-use, 994 // TODO(asgerf): This might turn the variable into a single-use,
890 // but we currently don't discover this. 995 // but we currently don't discover this.
891 return true; 996 return true;
892 } 997 }
893 if (e1 is Constant && e2 is Constant) { 998 if (e1 is Constant && e2 is Constant) {
894 return e1.value == e2.value; 999 return e1.value == e2.value;
895 } 1000 }
896 return false; 1001 return false;
897 } 1002 }
1003
1004 /// Try to collapse nested ifs using && and || expressions.
1005 /// For example:
1006 ///
1007 /// if (E1) { if (E2) S else break L } else break L
1008 /// ==>
1009 /// if (E1 && E2) S else break L
1010 ///
1011 /// [branch1] and [branch2] control the position of the S statement.
1012 ///
1013 /// Returns true if another collapse redex might have been introduced.
1014 void tryCollapseIf(If node) {
1015 // Repeatedly try to collapse nested ifs.
1016 // The transformation is shrinking (destroys an if) so it remains linear.
1017 // Here is an example where more than one iteration is required:
1018 //
1019 // if (E1)
1020 // if (E2) break L2 else break L1
1021 // else
1022 // break L1
1023 //
1024 // L1.target ::=
1025 // if (E3) S else break L2
1026 //
1027 // After first collapse:
1028 //
1029 // if (E1 && E2)
1030 // break L2
1031 // else
1032 // {if (E3) S else break L2} (inlined from break L1)
1033 //
1034 // We can then do another collapse using the inlined nested if.
1035 bool changed = true;
1036 while (changed) {
1037 changed = false;
1038 if (tryCollapseIfAux(node, true, true)) {
1039 changed = true;
1040 }
1041 if (tryCollapseIfAux(node, true, false)) {
1042 changed = true;
1043 }
1044 if (tryCollapseIfAux(node, false, true)) {
1045 changed = true;
1046 }
1047 if (tryCollapseIfAux(node, false, false)) {
1048 changed = true;
1049 }
1050 }
1051 }
1052
1053 bool tryCollapseIfAux(If outerIf, bool branch1, bool branch2) {
1054 // NOTE: We name variables here as if S is in the then-then position.
1055 Statement outerThen = getBranch(outerIf, branch1);
1056 Statement outerElse = getBranch(outerIf, !branch1);
1057 if (outerThen is If && outerElse is Break) {
1058 If innerIf = outerThen;
1059 Statement innerThen = getBranch(innerIf, branch2);
1060 Statement innerElse = getBranch(innerIf, !branch2);
1061 if (innerElse is Break && innerElse.target == outerElse.target) {
1062 // We always put S in the then branch of the result, and adjust the
1063 // condition expression if S was actually found in the else branch(es).
1064 outerIf.condition = new LogicalOperator.and(
1065 makeCondition(outerIf.condition, branch1),
1066 makeCondition(innerIf.condition, branch2));
1067 outerIf.thenStatement = innerThen;
1068 --innerElse.target.breakCount;
1069
1070 // Try to inline the remaining break
1071 environment.add(null); // Do not propagate impure definitions
1072 outerIf.elseStatement = visitStatement(outerElse);
1073 environment.removeLast();
1074
1075 return outerIf.elseStatement is If && innerThen is Break;
1076 }
1077 }
1078 return false;
1079 }
1080
1081 Expression makeCondition(Expression e, bool polarity) {
1082 return polarity ? e : new Not(e);
1083 }
1084
1085 Statement getBranch(If node, bool polarity) {
1086 return polarity ? node.thenStatement : node.elseStatement;
1087 }
898 } 1088 }
899 1089
1090
1091
1092 /// Rewrites logical expressions to be more compact.
1093 ///
1094 /// In this class an expression is said to occur in "boolean context" if
1095 /// its result is immediately applied to boolean conversion.
1096 ///
1097 /// IF STATEMENTS:
1098 ///
1099 /// We apply the following two rules to [If] statements (see [visitIf]).
1100 ///
1101 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted)
1102 /// if (!E) S1 else S2 ==> if (E) S2 else S1 (unless previous rule applied)
1103 ///
1104 /// NEGATION:
1105 ///
1106 /// De Morgan's Laws are used to rewrite negations of logical operators so
1107 /// negations are closer to the root:
1108 ///
1109 /// !x && !y --> !(x || y)
1110 ///
1111 /// This is to enable other rewrites, such as branch swapping in an if. In some
1112 /// contexts, the rule is reversed because we do not expect to apply a rewrite
1113 /// rule to the result. For example:
1114 ///
1115 /// z = !(x || y) ==> z = !x && !y;
1116 ///
1117 /// CONDITIONALS:
1118 ///
1119 /// Conditionals with boolean constant operands occur frequently in the input.
1120 /// They can often the re-written to logical operators, for instance:
1121 ///
1122 /// if (x ? y : false) S1 else S2
1123 /// ==>
1124 /// if (x && y) S1 else S2
1125 ///
1126 /// Conditionals are tricky to rewrite when they occur out of boolean context.
1127 /// Here we must apply more conservative rules, such as:
1128 ///
1129 /// x ? true : false ==> !!x
1130 ///
1131 /// If an operand is known to be a boolean, we can introduce a logical operator:
1132 ///
1133 /// x ? y : false ==> x && y (if y is known to be a boolean)
1134 ///
1135 /// The following sequence of rewrites demonstrates the merit of these rules:
1136 ///
1137 /// x ? (y ? true : false) : false
1138 /// x ? !!y : false (double negation introduced by [toBoolean])
1139 /// x && !!y (!!y validated by [isBooleanValued])
1140 /// x && y (double negation removed by [putInBooleanContext])
1141 ///
1142 class LogicalRewriter extends Visitor<Statement, Expression> {
1143
1144 /// Statement to be executed next by natural fallthrough. Although fallthrough
1145 /// is not introduced in this phase, we need to reason about fallthrough when
1146 /// evaluating the benefit of swapping the branches of an [If].
1147 Statement fallthrough;
1148
1149 void rewrite(FunctionDefinition definition) {
1150 definition.body = visitStatement(definition.body);
1151 }
1152
1153 Statement visitLabeledStatement(LabeledStatement node) {
1154 Statement savedFallthrough = fallthrough;
1155 fallthrough = node.next;
1156 node.body = visitStatement(node.body);
1157 fallthrough = savedFallthrough;
1158 node.next = visitStatement(node.next);
1159 return node;
1160 }
1161
1162 Statement visitAssign(Assign node) {
1163 node.definition = visitExpression(node.definition);
1164 node.next = visitStatement(node.next);
1165 return node;
1166 }
1167
1168 Statement visitReturn(Return node) {
1169 node.value = visitExpression(node.value);
1170 return node;
1171 }
1172
1173 Statement visitBreak(Break node) {
1174 return node;
1175 }
1176
1177 bool isFallthroughBreak(Statement node) {
1178 return node is Break && node.target.binding.next == fallthrough;
1179 }
1180
1181 Statement visitIf(If node) {
1182 // If one of the branches is empty (i.e. just a fallthrough), then that
1183 // branch should preferrably be the 'else' so we won't have to print it.
1184 // In other words, we wish to perform this rewrite:
1185 // if (E) {} else {S}
1186 // ==>
1187 // if (!E) {S}
1188 // In the tree language, empty statements do not exist yet, so we must check
1189 // if one branch contains a break that can be eliminated by fallthrough.
1190
1191 // Swap branches if then is a fallthrough break.
1192 if (isFallthroughBreak(node.thenStatement)) {
1193 node.condition = new Not(node.condition);
1194 Statement tmp = node.thenStatement;
1195 node.thenStatement = node.elseStatement;
1196 node.elseStatement = tmp;
1197 }
1198
1199 // Can the else part be eliminated?
1200 // (Either due to the above swap or if the break was already there).
1201 bool emptyElse = isFallthroughBreak(node.elseStatement);
1202
1203 node.condition = makeCondition(node.condition, true, liftNots: !emptyElse);
1204 node.thenStatement = visitStatement(node.thenStatement);
1205 node.elseStatement = visitStatement(node.elseStatement);
1206
1207 // If neither branch is empty, eliminate a negation in the condition
1208 // if (!E) S1 else S2
1209 // ==>
1210 // if (E) S2 else S1
1211 if (!emptyElse && node.condition is Not) {
1212 node.condition = (node.condition as Not).operand;
1213 Statement tmp = node.thenStatement;
1214 node.thenStatement = node.elseStatement;
1215 node.elseStatement = tmp;
1216 }
1217
1218 return node;
1219 }
1220
1221 Statement visitExpressionStatement(ExpressionStatement node) {
1222 // TODO(asgerf): in non-checked mode we can remove Not from the expression.
1223 node.expression = visitExpression(node.expression);
1224 node.next = visitStatement(node.next);
1225 return node;
1226 }
1227
1228
1229 Expression visitVariable(Variable node) {
1230 return node;
1231 }
1232
1233 Expression visitInvokeStatic(InvokeStatic node) {
1234 for (int i = 0; i < node.arguments.length; i++) {
1235 node.arguments[i] = visitExpression(node.arguments[i]);
1236 }
1237 return node;
1238 }
1239
1240 Expression visitInvokeMethod(InvokeMethod node) {
1241 node.receiver = visitExpression(node.receiver);
1242 for (int i = 0; i < node.arguments.length; i++) {
1243 node.arguments[i] = visitExpression(node.arguments[i]);
1244 }
1245 return node;
1246 }
1247
1248 Expression visitInvokeConstructor(InvokeConstructor node) {
1249 for (int i = 0; i < node.arguments.length; i++) {
1250 node.arguments[i] = visitExpression(node.arguments[i]);
1251 }
1252 return node;
1253 }
1254
1255 Expression visitConcatenateStrings(ConcatenateStrings node) {
1256 for (int i = 0; i < node.arguments.length; i++) {
1257 node.arguments[i] = visitExpression(node.arguments[i]);
1258 }
1259 return node;
1260 }
1261
1262 Expression visitConstant(Constant node) {
1263 return node;
1264 }
1265
1266 Expression visitNot(Not node) {
1267 return toBoolean(makeCondition(node.operand, false, liftNots: false));
1268 }
1269
1270 Expression visitConditional(Conditional node) {
1271 // node.condition will be visited after the then and else parts, because its
1272 // polarity depends on what rewrite we use.
1273 node.thenExpression = visitExpression(node.thenExpression);
1274 node.elseExpression = visitExpression(node.elseExpression);
1275
1276 // In the following, we must take care not to eliminate or introduce a
1277 // boolean conversion.
1278
1279 // x ? true : false --> !!x
1280 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) {
1281 return toBoolean(makeCondition(node.condition, true, liftNots: false));
1282 }
1283 // x ? false : true --> !x
1284 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) {
1285 return toBoolean(makeCondition(node.condition, false, liftNots: false));
1286 }
1287
1288 // x ? y : false ==> x && y (if y is known to be a boolean)
1289 if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) {
1290 return new LogicalOperator.and(
1291 makeCondition(node.condition, true, liftNots:false),
1292 putInBooleanContext(node.thenExpression));
1293 }
1294 // x ? y : true ==> !x || y (if y is known to be a boolean)
1295 if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) {
1296 return new LogicalOperator.or(
1297 makeCondition(node.condition, false, liftNots: false),
1298 putInBooleanContext(node.thenExpression));
1299 }
1300 // x ? true : y ==> x || y (if y if known to be boolean)
1301 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) {
1302 return new LogicalOperator.or(
1303 makeCondition(node.condition, true, liftNots: false),
1304 putInBooleanContext(node.elseExpression));
1305 }
1306 // x ? false : y ==> !x && y (if y is known to be a boolean)
1307 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) {
1308 return new LogicalOperator.and(
1309 makeCondition(node.condition, false, liftNots: false),
1310 putInBooleanContext(node.elseExpression));
1311 }
1312
1313 node.condition = makeCondition(node.condition, true);
1314
1315 // !x ? y : z ==> x ? z : y
1316 if (node.condition is Not) {
1317 node.condition = (node.condition as Not).operand;
1318 Expression tmp = node.thenExpression;
1319 node.thenExpression = node.elseExpression;
1320 node.elseExpression = tmp;
1321 }
1322
1323 return node;
1324 }
1325
1326 Expression visitLogicalOperator(LogicalOperator node) {
1327 node.left = makeCondition(node.left, true);
1328 node.right = makeCondition(node.right, true);
1329 return node;
1330 }
1331
1332 /// True if the given expression is known to evaluate to a boolean.
1333 /// This will not recursively traverse [Conditional] expressions, but if
1334 /// applied to the result of [visitExpression] conditionals will have been
1335 /// rewritten anyway.
1336 bool isBooleanValued(Expression e) {
1337 return isTrue(e) || isFalse(e) || e is Not || e is LogicalOperator;
1338 }
1339
1340 /// Rewrite an expression that was originally processed in a non-boolean
1341 /// context.
1342 Expression putInBooleanContext(Expression e) {
1343 if (e is Not && e.operand is Not) {
1344 return (e.operand as Not).operand;
1345 } else {
1346 return e;
1347 }
1348 }
1349
1350 /// Forces a boolean conversion of the given expression.
1351 Expression toBoolean(Expression e) {
1352 if (isBooleanValued(e))
1353 return e;
1354 else
1355 return new Not(new Not(e));
1356 }
1357
1358 /// Creates an equivalent boolean expression. The expression must occur in a
1359 /// context where its result is immediately subject to boolean conversion.
1360 /// If [polarity] if false, the negated condition will be created instead.
1361 /// If [liftNots] is true (default) then Not expressions will be lifted toward
1362 /// the root the condition so they can be eliminated by the caller.
1363 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) {
1364 if (e is Not) {
1365 // !!E ==> E
1366 return makeCondition(e.operand, !polarity, liftNots: liftNots);
1367 }
1368 if (e is LogicalOperator) {
1369 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y
1370 e.left = makeCondition(e.left, polarity);
1371 e.right = makeCondition(e.right, polarity);
1372 if (!polarity) {
1373 e.isAnd = !e.isAnd;
1374 }
1375 // !x && !y ==> !(x || y) (only if lifting nots)
1376 if (e.left is Not && e.right is Not && liftNots) {
1377 e.left = (e.left as Not).operand;
1378 e.right = (e.right as Not).operand;
1379 e.isAnd = !e.isAnd;
1380 return new Not(e);
1381 }
1382 return e;
1383 }
1384 if (e is Conditional) {
1385 // Handle polarity by: !(x ? y : z) ==> x ? !y : !z
1386 // Rewrite individual branches now. The condition will be rewritten
1387 // when we know what polarity to use (depends on which rewrite is used).
1388 e.thenExpression = makeCondition(e.thenExpression, polarity);
1389 e.elseExpression = makeCondition(e.elseExpression, polarity);
1390
1391 // x ? true : false ==> x
1392 if (isTrue(e.thenExpression) && isFalse(e.elseExpression)) {
1393 return makeCondition(e.condition, true, liftNots: liftNots);
1394 }
1395 // x ? false : true ==> !x
1396 if (isFalse(e.thenExpression) && isTrue(e.elseExpression)) {
1397 return makeCondition(e.condition, false, liftNots: liftNots);
1398 }
1399 // x ? true : y ==> x || y
1400 if (isTrue(e.thenExpression)) {
1401 return makeOr(makeCondition(e.condition, true),
1402 e.elseExpression,
1403 liftNots: liftNots);
1404 }
1405 // x ? false : y ==> !x && y
1406 if (isFalse(e.thenExpression)) {
1407 return makeAnd(makeCondition(e.condition, false),
1408 e.elseExpression,
1409 liftNots: liftNots);
1410 }
1411 // x ? y : true ==> !x || y
1412 if (isTrue(e.elseExpression)) {
1413 return makeOr(makeCondition(e.condition, false),
1414 e.thenExpression,
1415 liftNots: liftNots);
1416 }
1417 // x ? y : false ==> x && y
1418 if (isFalse(e.elseExpression)) {
1419 return makeAnd(makeCondition(e.condition, true),
1420 e.thenExpression,
1421 liftNots: liftNots);
1422 }
1423
1424 e.condition = makeCondition(e.condition, true);
1425
1426 // !x ? y : z ==> x ? z : y
1427 if (e.condition is Not) {
1428 e.condition = (e.condition as Not).operand;
1429 Expression tmp = e.thenExpression;
1430 e.thenExpression = e.elseExpression;
1431 e.elseExpression = tmp;
1432 }
1433 // x ? !y : !z ==> !(x ? y : z) (only if lifting nots)
1434 if (e.thenExpression is Not && e.elseExpression is Not && liftNots) {
1435 e.thenExpression = (e.thenExpression as Not).operand;
1436 e.elseExpression = (e.elseExpression as Not).operand;
1437 return new Not(e);
1438 }
1439 return e;
1440 }
1441 if (e is Constant && e.value is dart2js.BoolConstant) {
1442 // !true ==> false
1443 if (!polarity) {
1444 e.value = (e.value as dart2js.BoolConstant).negate();
1445 }
1446 return e;
1447 }
1448 e = visitExpression(e);
1449 return polarity ? e : new Not(e);
1450 }
1451
1452 bool isTrue(Expression e) {
1453 return e is Constant && e.value is dart2js.TrueConstant;
1454 }
1455
1456 bool isFalse(Expression e) {
1457 return e is Constant && e.value is dart2js.FalseConstant;
1458 }
1459
1460 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) {
1461 if (e1 is Not && e2 is Not && liftNots) {
1462 return new Not(new LogicalOperator.or(e1.operand, e2.operand));
1463 } else {
1464 return new LogicalOperator.and(e1, e2);
1465 }
1466 }
1467
1468 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) {
1469 if (e1 is Not && e2 is Not && liftNots) {
1470 return new Not(new LogicalOperator.and(e1.operand, e2.operand));
1471 } else {
1472 return new LogicalOperator.or(e1, e2);
1473 }
1474 }
1475
1476 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698