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

Side by Side Diff: pkg/compiler/lib/src/tree_ir/optimization/logical_rewriter.dart

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library tree_ir.optimization.logical_rewriter;
6
7 import '../../constants/values.dart' as values;
8 import '../tree_ir_nodes.dart';
9 import 'optimization.dart' show Pass;
10
11 /// Rewrites logical expressions to be more compact in the Tree IR.
12 ///
13 /// In this class an expression is said to occur in "boolean context" if
14 /// its result is immediately applied to boolean conversion.
15 ///
16 /// IF STATEMENTS:
17 ///
18 /// We apply the following two rules to [If] statements (see [visitIf]).
19 ///
20 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted)
21 /// if (!E) S1 else S2 ==> if (E) S2 else S1 (unless previous rule applied)
22 ///
23 /// NEGATION:
24 ///
25 /// De Morgan's Laws are used to rewrite negations of logical operators so
26 /// negations are closer to the root:
27 ///
28 /// !x && !y --> !(x || y)
29 ///
30 /// This is to enable other rewrites, such as branch swapping in an if. In some
31 /// contexts, the rule is reversed because we do not expect to apply a rewrite
32 /// rule to the result. For example:
33 ///
34 /// z = !(x || y) ==> z = !x && !y;
35 ///
36 /// CONDITIONALS:
37 ///
38 /// Conditionals with boolean constant operands occur frequently in the input.
39 /// They can often the re-written to logical operators, for instance:
40 ///
41 /// if (x ? y : false) S1 else S2
42 /// ==>
43 /// if (x && y) S1 else S2
44 ///
45 /// Conditionals are tricky to rewrite when they occur out of boolean context.
46 /// Here we must apply more conservative rules, such as:
47 ///
48 /// x ? true : false ==> !!x
49 ///
50 /// If the possible falsy values of the condition are known, we can sometimes
51 /// introduce a logical operator:
52 ///
53 /// !x ? y : false ==> !x && y
54 ///
55 class LogicalRewriter extends RecursiveTransformer implements Pass {
56 String get passName => 'Logical rewriter';
57
58 @override
59 void rewrite(FunctionDefinition node) {
60 node.body = visitStatement(node.body);
61 }
62
63 final FallthroughStack fallthrough = new FallthroughStack();
64
65 /// True if the given statement is equivalent to its fallthrough semantics.
66 ///
67 /// This means it will ultimately translate to an empty statement.
68 bool isFallthrough(Statement node) {
69 return node is Break && isFallthroughBreak(node) ||
70 node is Continue && isFallthroughContinue(node) ||
71 node is Return && isFallthroughReturn(node);
72 }
73
74 bool isFallthroughBreak(Break node) {
75 Statement target = fallthrough.target;
76 return node.target.binding.next == target ||
77 target is Break && target.target == node.target;
78 }
79
80 bool isFallthroughContinue(Continue node) {
81 Statement target = fallthrough.target;
82 return node.target.binding == target ||
83 target is Continue && target.target == node.target;
84 }
85
86 bool isFallthroughReturn(Return node) {
87 return isNull(node.value) && fallthrough.target == null;
88 }
89
90 bool isTerminator(Statement node) {
91 return (node is Jump || node is Return) && !isFallthrough(node) ||
92 (node is ExpressionStatement && node.next is Unreachable) ||
93 node is Throw;
94 }
95
96 Statement visitIf(If node) {
97 // If one of the branches is empty (i.e. just a fallthrough), then that
98 // branch should preferably be the 'else' so we won't have to print it.
99 // In other words, we wish to perform this rewrite:
100 // if (E) {} else {S}
101 // ==>
102 // if (!E) {S}
103 // In the tree language, empty statements do not exist yet, so we must check
104 // if one branch contains a break that can be eliminated by fallthrough.
105
106 // Rewrite each branch and keep track of which ones might fall through.
107 int usesBefore = fallthrough.useCount;
108 node.thenStatement = visitStatement(node.thenStatement);
109 int usesAfterThen = fallthrough.useCount;
110 node.elseStatement = visitStatement(node.elseStatement);
111 bool thenHasFallthrough = (fallthrough.useCount > usesBefore);
112 bool elseHasFallthrough = (fallthrough.useCount > usesAfterThen);
113
114 // Determine which branch is most beneficial as 'then' branch.
115 const int THEN = 1;
116 const int NEITHER = 0;
117 const int ELSE = -1;
118 int bestThenBranch = NEITHER;
119 if (isFallthrough(node.thenStatement) &&
120 !isFallthrough(node.elseStatement)) {
121 // Put the empty statement in the 'else' branch.
122 // if (E) {} else {S} ==> if (!E) {S}
123 bestThenBranch = ELSE;
124 } else if (isFallthrough(node.elseStatement) &&
125 !isFallthrough(node.thenStatement)) {
126 // Keep the empty statement in the 'else' branch.
127 // if (E) {S} else {}
128 bestThenBranch = THEN;
129 } else if (thenHasFallthrough && !elseHasFallthrough) {
130 // Put abrupt termination in the 'then' branch to omit 'else'.
131 // if (E) {S1} else {S2; return v} ==> if (!E) {S2; return v}; S1
132 bestThenBranch = ELSE;
133 } else if (!thenHasFallthrough && elseHasFallthrough) {
134 // Keep abrupt termination in the 'then' branch to omit 'else'.
135 // if (E) {S1; return v}; S2
136 bestThenBranch = THEN;
137 } else if (isTerminator(node.elseStatement) &&
138 !isTerminator(node.thenStatement)) {
139 // Put early termination in the 'then' branch to reduce nesting depth.
140 // if (E) {S}; return v ==> if (!E) return v; S
141 bestThenBranch = ELSE;
142 } else if (isTerminator(node.thenStatement) &&
143 !isTerminator(node.elseStatement)) {
144 // Keep early termination in the 'then' branch to reduce nesting depth.
145 // if (E) {return v;} S
146 bestThenBranch = THEN;
147 }
148
149 // Swap branches if 'else' is better as 'then'
150 if (bestThenBranch == ELSE) {
151 node.condition = new Not(node.condition);
152 Statement tmp = node.thenStatement;
153 node.thenStatement = node.elseStatement;
154 node.elseStatement = tmp;
155 }
156
157 // If neither branch is better, eliminate a negation in the condition
158 // if (!E) S1 else S2
159 // ==>
160 // if (E) S2 else S1
161 node.condition = makeCondition(node.condition, true,
162 liftNots: bestThenBranch == NEITHER);
163 if (bestThenBranch == NEITHER && node.condition is Not) {
164 node.condition = (node.condition as Not).operand;
165 Statement tmp = node.thenStatement;
166 node.thenStatement = node.elseStatement;
167 node.elseStatement = tmp;
168 }
169
170 return node;
171 }
172
173 Statement visitLabeledStatement(LabeledStatement node) {
174 fallthrough.push(node.next);
175 node.body = visitStatement(node.body);
176 fallthrough.pop();
177 node.next = visitStatement(node.next);
178 return node;
179 }
180
181 Statement visitWhileTrue(WhileTrue node) {
182 fallthrough.push(node);
183 node.body = visitStatement(node.body);
184 fallthrough.pop();
185 return node;
186 }
187
188 Statement visitFor(For node) {
189 fallthrough.push(node);
190 node.condition = makeCondition(node.condition, true, liftNots: false);
191 node.body = visitStatement(node.body);
192 fallthrough.pop();
193 node.next = visitStatement(node.next);
194 return node;
195 }
196
197 Statement visitBreak(Break node) {
198 if (isFallthroughBreak(node)) {
199 fallthrough.use();
200 }
201 return node;
202 }
203
204 Statement visitContinue(Continue node) {
205 if (isFallthroughContinue(node)) {
206 fallthrough.use();
207 }
208 return node;
209 }
210
211 Statement visitReturn(Return node) {
212 node.value = visitExpression(node.value);
213 if (isFallthroughReturn(node)) {
214 fallthrough.use();
215 }
216 return node;
217 }
218
219 Expression visitNot(Not node) {
220 return toBoolean(makeCondition(node.operand, false, liftNots: false));
221 }
222
223 /// True if the only possible falsy return value of [condition] is [value].
224 ///
225 /// If [value] is `null` or a truthy value, false is returned. This is to make
226 /// pattern matching more convenient.
227 bool matchesFalsyValue(Expression condition, values.ConstantValue value) {
228 if (value == null) return false;
229 // TODO(asgerf): Here we could really use some more type information,
230 // this is just the best we can do at the moment.
231 return isBooleanValued(condition) && value.isFalse;
232 }
233
234 /// True if the only possible truthy return value of [condition] is [value].
235 ///
236 /// If [value] is `null` or a falsy value, false is returned. This is to make
237 /// pattern matching more convenient.
238 bool matchesTruthyValue(Expression condition, values.ConstantValue value) {
239 if (value == null) return false;
240 // TODO(asgerf): Again, more type information could really beef this up.
241 return isBooleanValued(condition) && value.isTrue;
242 }
243
244 values.ConstantValue getConstant(Expression exp) {
245 return exp is Constant ? exp.value : null;
246 }
247
248 Expression visitConditional(Conditional node) {
249 // node.condition will be visited after the then and else parts, because its
250 // polarity depends on what rewrite we use.
251 node.thenExpression = visitExpression(node.thenExpression);
252 node.elseExpression = visitExpression(node.elseExpression);
253
254 // In the following, we must take care not to eliminate or introduce a
255 // boolean conversion.
256
257 // x ? true : false --> !!x
258 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) {
259 return toBoolean(makeCondition(node.condition, true, liftNots: false));
260 }
261 // x ? false : true --> !x
262 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) {
263 return toBoolean(makeCondition(node.condition, false, liftNots: false));
264 }
265
266 // x ? y : false ==> x && y (if x is truthy or false)
267 // x ? y : null ==> x && y (if x is truthy or null)
268 // x ? y : 0 ==> x && y (if x is truthy or zero) (and so on...)
269 if (matchesFalsyValue(node.condition, getConstant(node.elseExpression))) {
270 return new LogicalOperator.and(
271 visitExpression(node.condition), node.thenExpression);
272 }
273 // x ? true : y ==> x || y (if x is falsy or true)
274 // x ? 1 : y ==> x || y (if x is falsy or one) (and so on...)
275 if (matchesTruthyValue(node.condition, getConstant(node.thenExpression))) {
276 return new LogicalOperator.or(
277 visitExpression(node.condition), node.elseExpression);
278 }
279 // x ? y : true ==> !x || y
280 if (isTrue(node.elseExpression)) {
281 return new LogicalOperator.or(
282 toBoolean(makeCondition(node.condition, false, liftNots: false)),
283 node.thenExpression);
284 }
285 // x ? false : y ==> !x && y
286 if (isFalse(node.thenExpression)) {
287 return new LogicalOperator.and(
288 toBoolean(makeCondition(node.condition, false, liftNots: false)),
289 node.elseExpression);
290 }
291
292 node.condition = makeCondition(node.condition, true);
293
294 // !x ? y : z ==> x ? z : y
295 if (node.condition is Not) {
296 node.condition = (node.condition as Not).operand;
297 Expression tmp = node.thenExpression;
298 node.thenExpression = node.elseExpression;
299 node.elseExpression = tmp;
300 }
301
302 // x ? y : x ==> x && y
303 if (isSameVariable(node.condition, node.elseExpression)) {
304 destroyVariableUse(node.elseExpression);
305 return new LogicalOperator.and(node.condition, node.thenExpression);
306 }
307 // x ? x : y ==> x || y
308 if (isSameVariable(node.condition, node.thenExpression)) {
309 destroyVariableUse(node.thenExpression);
310 return new LogicalOperator.or(node.condition, node.elseExpression);
311 }
312
313 return node;
314 }
315
316 Expression visitLogicalOperator(LogicalOperator node) {
317 node.left = visitExpression(node.left);
318 node.right = visitExpression(node.right);
319 return node;
320 }
321
322 /// True if the given expression is known to evaluate to a boolean.
323 /// This will not recursively traverse [Conditional] expressions, but if
324 /// applied to the result of [visitExpression] conditionals will have been
325 /// rewritten anyway.
326 bool isBooleanValued(Expression e) {
327 return isTrue(e) ||
328 isFalse(e) ||
329 e is Not ||
330 e is LogicalOperator && isBooleanValuedLogicalOperator(e) ||
331 e is ApplyBuiltinOperator && operatorReturnsBool(e.operator) ||
332 e is TypeOperator && isBooleanValuedTypeOperator(e);
333 }
334
335 bool isBooleanValuedLogicalOperator(LogicalOperator e) {
336 return isBooleanValued(e.left) && isBooleanValued(e.right);
337 }
338
339 /// True if the given operator always returns `true` or `false`.
340 bool operatorReturnsBool(BuiltinOperator operator) {
341 switch (operator) {
342 case BuiltinOperator.StrictEq:
343 case BuiltinOperator.StrictNeq:
344 case BuiltinOperator.LooseEq:
345 case BuiltinOperator.LooseNeq:
346 case BuiltinOperator.NumLt:
347 case BuiltinOperator.NumLe:
348 case BuiltinOperator.NumGt:
349 case BuiltinOperator.NumGe:
350 case BuiltinOperator.IsNumber:
351 case BuiltinOperator.IsNotNumber:
352 case BuiltinOperator.IsFloor:
353 case BuiltinOperator.IsInteger:
354 case BuiltinOperator.IsNotInteger:
355 case BuiltinOperator.Identical:
356 return true;
357 default:
358 return false;
359 }
360 }
361
362 bool isBooleanValuedTypeOperator(TypeOperator e) {
363 return e.isTypeTest;
364 }
365
366 BuiltinOperator negateBuiltin(BuiltinOperator operator) {
367 switch (operator) {
368 case BuiltinOperator.StrictEq:
369 return BuiltinOperator.StrictNeq;
370 case BuiltinOperator.StrictNeq:
371 return BuiltinOperator.StrictEq;
372 case BuiltinOperator.LooseEq:
373 return BuiltinOperator.LooseNeq;
374 case BuiltinOperator.LooseNeq:
375 return BuiltinOperator.LooseEq;
376 case BuiltinOperator.IsNumber:
377 return BuiltinOperator.IsNotNumber;
378 case BuiltinOperator.IsNotNumber:
379 return BuiltinOperator.IsNumber;
380 case BuiltinOperator.IsInteger:
381 return BuiltinOperator.IsNotInteger;
382 case BuiltinOperator.IsNotInteger:
383 return BuiltinOperator.IsInteger;
384 case BuiltinOperator.IsUnsigned32BitInteger:
385 return BuiltinOperator.IsNotUnsigned32BitInteger;
386 case BuiltinOperator.IsNotUnsigned32BitInteger:
387 return BuiltinOperator.IsUnsigned32BitInteger;
388
389 // Because of NaN, these do not have a negated form.
390 case BuiltinOperator.NumLt:
391 case BuiltinOperator.NumLe:
392 case BuiltinOperator.NumGt:
393 case BuiltinOperator.NumGe:
394 return null;
395
396 default:
397 return null;
398 }
399 }
400
401 /// Forces a boolean conversion of the given expression.
402 Expression toBoolean(Expression e) {
403 if (isBooleanValued(e))
404 return e;
405 else
406 return new Not(new Not(e));
407 }
408
409 /// Creates an equivalent boolean expression. The expression must occur in a
410 /// context where its result is immediately subject to boolean conversion.
411 /// If [polarity] if false, the negated condition will be created instead.
412 /// If [liftNots] is true (default) then Not expressions will be lifted toward
413 /// the root of the condition so they can be eliminated by the caller.
414 Expression makeCondition(Expression e, bool polarity, {bool liftNots: true}) {
415 if (e is Not) {
416 // !!E ==> E
417 return makeCondition(e.operand, !polarity, liftNots: liftNots);
418 }
419 if (e is LogicalOperator) {
420 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y
421 e.left = makeCondition(e.left, polarity);
422 e.right = makeCondition(e.right, polarity);
423 if (!polarity) {
424 e.isAnd = !e.isAnd;
425 }
426 // !x && !y ==> !(x || y) (only if lifting nots)
427 if (e.left is Not && e.right is Not && liftNots) {
428 e.left = (e.left as Not).operand;
429 e.right = (e.right as Not).operand;
430 e.isAnd = !e.isAnd;
431 return new Not(e);
432 }
433 return e;
434 }
435 if (e is ApplyBuiltinOperator && polarity == false) {
436 BuiltinOperator negated = negateBuiltin(e.operator);
437 if (negated != null) {
438 e.operator = negated;
439 return visitExpression(e);
440 } else {
441 return new Not(visitExpression(e));
442 }
443 }
444 if (e is Conditional) {
445 // Handle polarity by: !(x ? y : z) ==> x ? !y : !z
446 // Rewrite individual branches now. The condition will be rewritten
447 // when we know what polarity to use (depends on which rewrite is used).
448 e.thenExpression = makeCondition(e.thenExpression, polarity);
449 e.elseExpression = makeCondition(e.elseExpression, polarity);
450
451 // x ? true : false ==> x
452 if (isTrue(e.thenExpression) && isFalse(e.elseExpression)) {
453 return makeCondition(e.condition, true, liftNots: liftNots);
454 }
455 // x ? false : true ==> !x
456 if (isFalse(e.thenExpression) && isTrue(e.elseExpression)) {
457 return makeCondition(e.condition, false, liftNots: liftNots);
458 }
459 // x ? true : y ==> x || y
460 if (isTrue(e.thenExpression)) {
461 return makeOr(makeCondition(e.condition, true), e.elseExpression,
462 liftNots: liftNots);
463 }
464 // x ? false : y ==> !x && y
465 if (isFalse(e.thenExpression)) {
466 return makeAnd(makeCondition(e.condition, false), e.elseExpression,
467 liftNots: liftNots);
468 }
469 // x ? y : true ==> !x || y
470 if (isTrue(e.elseExpression)) {
471 return makeOr(makeCondition(e.condition, false), e.thenExpression,
472 liftNots: liftNots);
473 }
474 // x ? y : false ==> x && y
475 if (isFalse(e.elseExpression)) {
476 return makeAnd(makeCondition(e.condition, true), e.thenExpression,
477 liftNots: liftNots);
478 }
479
480 e.condition = makeCondition(e.condition, true);
481
482 // !x ? y : z ==> x ? z : y
483 if (e.condition is Not) {
484 e.condition = (e.condition as Not).operand;
485 Expression tmp = e.thenExpression;
486 e.thenExpression = e.elseExpression;
487 e.elseExpression = tmp;
488 }
489 // x ? !y : !z ==> !(x ? y : z) (only if lifting nots)
490 if (e.thenExpression is Not && e.elseExpression is Not && liftNots) {
491 e.thenExpression = (e.thenExpression as Not).operand;
492 e.elseExpression = (e.elseExpression as Not).operand;
493 return new Not(e);
494 }
495
496 // x ? y : x ==> x && y
497 if (isSameVariable(e.condition, e.elseExpression)) {
498 destroyVariableUse(e.elseExpression);
499 return new LogicalOperator.and(e.condition, e.thenExpression);
500 }
501 // x ? x : y ==> x || y
502 if (isSameVariable(e.condition, e.thenExpression)) {
503 destroyVariableUse(e.thenExpression);
504 return new LogicalOperator.or(e.condition, e.elseExpression);
505 }
506
507 return e;
508 }
509 if (e is Constant && e.value.isBool) {
510 // !true ==> false
511 if (!polarity) {
512 values.BoolConstantValue value = e.value;
513 return new Constant.bool(value.negate());
514 }
515 return e;
516 }
517 e = visitExpression(e);
518 return polarity ? e : new Not(e);
519 }
520
521 bool isNull(Expression e) {
522 return e is Constant && e.value.isNull;
523 }
524
525 bool isTrue(Expression e) {
526 return e is Constant && e.value.isTrue;
527 }
528
529 bool isFalse(Expression e) {
530 return e is Constant && e.value.isFalse;
531 }
532
533 Expression makeAnd(Expression e1, Expression e2, {bool liftNots: true}) {
534 if (e1 is Not && e2 is Not && liftNots) {
535 return new Not(new LogicalOperator.or(e1.operand, e2.operand));
536 } else {
537 return new LogicalOperator.and(e1, e2);
538 }
539 }
540
541 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) {
542 if (e1 is Not && e2 is Not && liftNots) {
543 return new Not(new LogicalOperator.and(e1.operand, e2.operand));
544 } else {
545 return new LogicalOperator.or(e1, e2);
546 }
547 }
548
549 /// True if [e2] is known to return the same value as [e1]
550 /// (with no additional side effects) if evaluated immediately after [e1].
551 ///
552 /// Concretely, this is true if [e1] and [e2] are uses of the same variable,
553 /// or if [e2] is a use of a variable assigned by [e1].
554 bool isSameVariable(Expression e1, Expression e2) {
555 if (e1 is VariableUse) {
556 return e2 is VariableUse && e1.variable == e2.variable;
557 } else if (e1 is Assign) {
558 return e2 is VariableUse && e1.variable == e2.variable;
559 }
560 return false;
561 }
562
563 void destroyVariableUse(VariableUse node) {
564 --node.variable.readCount;
565 }
566 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/tracer.dart ('k') | pkg/compiler/lib/src/tree_ir/optimization/loop_rewriter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698