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.logical_rewriter; | 5 library tree_ir.optimization.logical_rewriter; |
6 | 6 |
7 import '../tree_ir_nodes.dart'; | 7 import '../tree_ir_nodes.dart'; |
8 import 'optimization.dart' show Pass; | 8 import 'optimization.dart' show Pass; |
9 import '../../constants/values.dart' as values; | 9 import '../../constants/values.dart' as values; |
10 | 10 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
54 /// | 54 /// |
55 class LogicalRewriter extends RecursiveTransformer | 55 class LogicalRewriter extends RecursiveTransformer |
56 implements Pass { | 56 implements Pass { |
57 String get passName => 'Logical rewriter'; | 57 String get passName => 'Logical rewriter'; |
58 | 58 |
59 @override | 59 @override |
60 void rewrite(FunctionDefinition node) { | 60 void rewrite(FunctionDefinition node) { |
61 node.body = visitStatement(node.body); | 61 node.body = visitStatement(node.body); |
62 } | 62 } |
63 | 63 |
64 /// Statement to be executed next by natural fallthrough. Although fallthrough | 64 final FallthroughStack fallthrough = new FallthroughStack(); |
65 /// is not introduced in this phase, we need to reason about fallthrough when | |
66 /// evaluating the benefit of swapping the branches of an [If]. | |
67 Statement fallthrough; | |
68 | 65 |
69 @override | 66 @override |
70 void visitInnerFunction(FunctionDefinition node) { | 67 void visitInnerFunction(FunctionDefinition node) { |
71 new LogicalRewriter().rewrite(node); | 68 new LogicalRewriter().rewrite(node); |
72 } | 69 } |
73 | 70 |
74 Statement visitLabeledStatement(LabeledStatement node) { | 71 /// True if the given statement is equivalent to its fallthrough semantics. |
75 Statement savedFallthrough = fallthrough; | 72 /// |
76 fallthrough = node.next; | 73 /// This means it will ultimately translate to an empty statement. |
77 node.body = visitStatement(node.body); | 74 bool isFallthrough(Statement node) { |
78 fallthrough = savedFallthrough; | 75 return node is Break && isFallthroughBreak(node) || |
79 node.next = visitStatement(node.next); | 76 node is Continue && isFallthroughContinue(node) || |
80 return node; | 77 node is Return && isFallthroughReturn(node); |
81 } | 78 } |
82 | 79 |
83 bool isFallthroughBreak(Statement node) { | 80 bool isFallthroughBreak(Break node) { |
84 return node is Break && node.target.binding.next == fallthrough; | 81 Statement target = fallthrough.target; |
| 82 return node.target.binding.next == target || |
| 83 target is Break && target.target == node.target; |
| 84 } |
| 85 |
| 86 bool isFallthroughContinue(Continue node) { |
| 87 Statement target = fallthrough.target; |
| 88 return node.target.binding == target || |
| 89 target is Continue && target.target == node.target; |
| 90 } |
| 91 |
| 92 bool isFallthroughReturn(Return node) { |
| 93 return isNull(node.value) && fallthrough.target == null; |
| 94 } |
| 95 |
| 96 bool isTerminator(Statement node) { |
| 97 return (node is Jump || node is Return) && !isFallthrough(node) || |
| 98 (node is ExpressionStatement && node.next is Unreachable); |
85 } | 99 } |
86 | 100 |
87 Statement visitIf(If node) { | 101 Statement visitIf(If node) { |
88 // If one of the branches is empty (i.e. just a fallthrough), then that | 102 // If one of the branches is empty (i.e. just a fallthrough), then that |
89 // branch should preferably be the 'else' so we won't have to print it. | 103 // branch should preferably be the 'else' so we won't have to print it. |
90 // In other words, we wish to perform this rewrite: | 104 // In other words, we wish to perform this rewrite: |
91 // if (E) {} else {S} | 105 // if (E) {} else {S} |
92 // ==> | 106 // ==> |
93 // if (!E) {S} | 107 // if (!E) {S} |
94 // In the tree language, empty statements do not exist yet, so we must check | 108 // In the tree language, empty statements do not exist yet, so we must check |
95 // if one branch contains a break that can be eliminated by fallthrough. | 109 // if one branch contains a break that can be eliminated by fallthrough. |
96 | 110 |
97 // Swap branches if then is a fallthrough break. | 111 // Rewrite each branch and keep track of which ones might fall through. |
98 if (isFallthroughBreak(node.thenStatement)) { | 112 int usesBefore = fallthrough.useCount; |
| 113 node.thenStatement = visitStatement(node.thenStatement); |
| 114 int usesAfterThen = fallthrough.useCount; |
| 115 node.elseStatement = visitStatement(node.elseStatement); |
| 116 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); |
| 117 bool elseHasFallthrough = (fallthrough.useCount > usesAfterThen); |
| 118 |
| 119 // Determine which branch is most beneficial as 'then' branch. |
| 120 const int THEN = 1; |
| 121 const int NEITHER = 0; |
| 122 const int ELSE = -1; |
| 123 int bestThenBranch = NEITHER; |
| 124 if (isFallthrough(node.thenStatement) && |
| 125 !isFallthrough(node.elseStatement)) { |
| 126 // Put the empty statement in the 'else' branch. |
| 127 // if (E) {} else {S} ==> if (!E) {S} |
| 128 bestThenBranch = ELSE; |
| 129 } else if (isFallthrough(node.elseStatement) && |
| 130 !isFallthrough(node.thenStatement)) { |
| 131 // Keep the empty statement in the 'else' branch. |
| 132 // if (E) {S} else {} |
| 133 bestThenBranch = THEN; |
| 134 } else if (thenHasFallthrough && !elseHasFallthrough) { |
| 135 // Put abrupt termination in the 'then' branch to omit 'else'. |
| 136 // if (E) {S1} else {S2; return v} ==> if (!E) {S2; return v}; S1 |
| 137 bestThenBranch = ELSE; |
| 138 } else if (!thenHasFallthrough && elseHasFallthrough) { |
| 139 // Keep abrupt termination in the 'then' branch to omit 'else'. |
| 140 // if (E) {S1; return v}; S2 |
| 141 bestThenBranch = THEN; |
| 142 } else if (isTerminator(node.elseStatement) && |
| 143 !isTerminator(node.thenStatement)) { |
| 144 // Put early termination in the 'then' branch to reduce nesting depth. |
| 145 // if (E) {S}; return v ==> if (!E) return v; S |
| 146 bestThenBranch = ELSE; |
| 147 } else if (isTerminator(node.thenStatement) && |
| 148 !isTerminator(node.elseStatement)) { |
| 149 // Keep early termination in the 'then' branch to reduce nesting depth. |
| 150 // if (E) {return v;} S |
| 151 bestThenBranch = THEN; |
| 152 } |
| 153 |
| 154 // Swap branches if 'else' is better as 'then' |
| 155 if (bestThenBranch == ELSE) { |
99 node.condition = new Not(node.condition); | 156 node.condition = new Not(node.condition); |
100 Statement tmp = node.thenStatement; | 157 Statement tmp = node.thenStatement; |
101 node.thenStatement = node.elseStatement; | 158 node.thenStatement = node.elseStatement; |
102 node.elseStatement = tmp; | 159 node.elseStatement = tmp; |
103 } | 160 } |
104 | 161 |
105 // Can the else part be eliminated? | 162 // If neither branch is better, eliminate a negation in the condition |
106 // (Either due to the above swap or if the break was already there). | |
107 bool emptyElse = isFallthroughBreak(node.elseStatement); | |
108 | |
109 node.condition = makeCondition(node.condition, true, liftNots: !emptyElse); | |
110 node.thenStatement = visitStatement(node.thenStatement); | |
111 node.elseStatement = visitStatement(node.elseStatement); | |
112 | |
113 // If neither branch is empty, eliminate a negation in the condition | |
114 // if (!E) S1 else S2 | 163 // if (!E) S1 else S2 |
115 // ==> | 164 // ==> |
116 // if (E) S2 else S1 | 165 // if (E) S2 else S1 |
117 if (!emptyElse && node.condition is Not) { | 166 node.condition = makeCondition(node.condition, true, |
| 167 liftNots: bestThenBranch == NEITHER); |
| 168 if (bestThenBranch == NEITHER && node.condition is Not) { |
118 node.condition = (node.condition as Not).operand; | 169 node.condition = (node.condition as Not).operand; |
119 Statement tmp = node.thenStatement; | 170 Statement tmp = node.thenStatement; |
120 node.thenStatement = node.elseStatement; | 171 node.thenStatement = node.elseStatement; |
121 node.elseStatement = tmp; | 172 node.elseStatement = tmp; |
122 } | 173 } |
123 | 174 |
124 return node; | 175 return node; |
125 } | 176 } |
126 | 177 |
| 178 Statement visitLabeledStatement(LabeledStatement node) { |
| 179 fallthrough.push(node.next); |
| 180 node.body = visitStatement(node.body); |
| 181 fallthrough.pop(); |
| 182 node.next = visitStatement(node.next); |
| 183 return node; |
| 184 } |
| 185 |
| 186 Statement visitWhileTrue(WhileTrue node) { |
| 187 fallthrough.push(node); |
| 188 node.body = visitStatement(node.body); |
| 189 fallthrough.pop(); |
| 190 return node; |
| 191 } |
| 192 |
127 Statement visitWhileCondition(WhileCondition node) { | 193 Statement visitWhileCondition(WhileCondition node) { |
| 194 fallthrough.push(node); |
128 node.condition = makeCondition(node.condition, true, liftNots: false); | 195 node.condition = makeCondition(node.condition, true, liftNots: false); |
129 node.body = visitStatement(node.body); | 196 node.body = visitStatement(node.body); |
| 197 fallthrough.pop(); |
130 node.next = visitStatement(node.next); | 198 node.next = visitStatement(node.next); |
131 return node; | 199 return node; |
132 } | 200 } |
133 | 201 |
| 202 Statement visitBreak(Break node) { |
| 203 if (isFallthroughBreak(node)) { |
| 204 fallthrough.use(); |
| 205 } |
| 206 return node; |
| 207 } |
| 208 |
| 209 Statement visitContinue(Continue node) { |
| 210 if (isFallthroughContinue(node)) { |
| 211 fallthrough.use(); |
| 212 } |
| 213 return node; |
| 214 } |
| 215 |
| 216 Statement visitReturn(Return node) { |
| 217 node.value = visitExpression(node.value); |
| 218 if (isFallthroughReturn(node)) { |
| 219 fallthrough.use(); |
| 220 } |
| 221 return node; |
| 222 } |
| 223 |
134 Expression visitNot(Not node) { | 224 Expression visitNot(Not node) { |
135 return toBoolean(makeCondition(node.operand, false, liftNots: false)); | 225 return toBoolean(makeCondition(node.operand, false, liftNots: false)); |
136 } | 226 } |
137 | 227 |
138 /// True if the only possible falsy return value of [condition] is [value]. | 228 /// True if the only possible falsy return value of [condition] is [value]. |
139 /// | 229 /// |
140 /// If [value] is `null` or a truthy value, false is returned. This is to make | 230 /// If [value] is `null` or a truthy value, false is returned. This is to make |
141 /// pattern matching more convenient. | 231 /// pattern matching more convenient. |
142 bool matchesFalsyValue(Expression condition, values.ConstantValue value) { | 232 bool matchesFalsyValue(Expression condition, values.ConstantValue value) { |
143 if (value == null) return false; | 233 if (value == null) return false; |
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 if (!polarity) { | 474 if (!polarity) { |
385 values.BoolConstantValue value = e.value; | 475 values.BoolConstantValue value = e.value; |
386 return new Constant.bool(value.negate()); | 476 return new Constant.bool(value.negate()); |
387 } | 477 } |
388 return e; | 478 return e; |
389 } | 479 } |
390 e = visitExpression(e); | 480 e = visitExpression(e); |
391 return polarity ? e : new Not(e); | 481 return polarity ? e : new Not(e); |
392 } | 482 } |
393 | 483 |
| 484 bool isNull(Expression e) { |
| 485 return e is Constant && e.value.isNull; |
| 486 } |
| 487 |
394 bool isTrue(Expression e) { | 488 bool isTrue(Expression e) { |
395 return e is Constant && e.value.isTrue; | 489 return e is Constant && e.value.isTrue; |
396 } | 490 } |
397 | 491 |
398 bool isFalse(Expression e) { | 492 bool isFalse(Expression e) { |
399 return e is Constant && e.value.isFalse; | 493 return e is Constant && e.value.isFalse; |
400 } | 494 } |
401 | 495 |
402 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) { | 496 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) { |
403 if (e1 is Not && e2 is Not && liftNots) { | 497 if (e1 is Not && e2 is Not && liftNots) { |
404 return new Not(new LogicalOperator.or(e1.operand, e2.operand)); | 498 return new Not(new LogicalOperator.or(e1.operand, e2.operand)); |
405 } else { | 499 } else { |
406 return new LogicalOperator.and(e1, e2); | 500 return new LogicalOperator.and(e1, e2); |
407 } | 501 } |
408 } | 502 } |
409 | 503 |
410 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) { | 504 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) { |
411 if (e1 is Not && e2 is Not && liftNots) { | 505 if (e1 is Not && e2 is Not && liftNots) { |
412 return new Not(new LogicalOperator.and(e1.operand, e2.operand)); | 506 return new Not(new LogicalOperator.and(e1.operand, e2.operand)); |
413 } else { | 507 } else { |
414 return new LogicalOperator.or(e1, e2); | 508 return new LogicalOperator.or(e1, e2); |
415 } | 509 } |
416 } | 510 } |
417 } | 511 } |
418 | 512 |
OLD | NEW |