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

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

Issue 1185333002: dart2js cps: Use JS semantics for logical operators in the tree IR. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Renamed method Created 5 years, 6 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
« no previous file with comments | « no previous file | tests/language/conditional_rewrite_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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
11 // TODO(asgerf): Update this class to use JS semantics for && and ||.
12
13 /// Rewrites logical expressions to be more compact in the Tree IR. 11 /// Rewrites logical expressions to be more compact in the Tree IR.
14 /// 12 ///
15 /// In this class an expression is said to occur in "boolean context" if 13 /// In this class an expression is said to occur in "boolean context" if
16 /// its result is immediately applied to boolean conversion. 14 /// its result is immediately applied to boolean conversion.
17 /// 15 ///
18 /// IF STATEMENTS: 16 /// IF STATEMENTS:
19 /// 17 ///
20 /// We apply the following two rules to [If] statements (see [visitIf]). 18 /// We apply the following two rules to [If] statements (see [visitIf]).
21 /// 19 ///
22 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted) 20 /// if (E) {} else S ==> if (!E) S else {} (else can be omitted)
(...skipping 19 matching lines...) Expand all
42 /// 40 ///
43 /// if (x ? y : false) S1 else S2 41 /// if (x ? y : false) S1 else S2
44 /// ==> 42 /// ==>
45 /// if (x && y) S1 else S2 43 /// if (x && y) S1 else S2
46 /// 44 ///
47 /// Conditionals are tricky to rewrite when they occur out of boolean context. 45 /// Conditionals are tricky to rewrite when they occur out of boolean context.
48 /// Here we must apply more conservative rules, such as: 46 /// Here we must apply more conservative rules, such as:
49 /// 47 ///
50 /// x ? true : false ==> !!x 48 /// x ? true : false ==> !!x
51 /// 49 ///
52 /// If an operand is known to be a boolean, we can introduce a logical operator: 50 /// If the possible falsy values of the condition are known, we can sometimes
51 /// introduce a logical operator:
53 /// 52 ///
54 /// x ? y : false ==> x && y (if y is known to be a boolean) 53 /// !x ? y : false ==> !x && y
55 ///
56 /// The following sequence of rewrites demonstrates the merit of these rules:
57 ///
58 /// x ? (y ? true : false) : false
59 /// x ? !!y : false (double negation introduced by [toBoolean])
60 /// x && !!y (!!y validated by [isBooleanValued])
61 /// x && y (double negation removed by [putInBooleanContext])
62 /// 54 ///
63 class LogicalRewriter extends RecursiveTransformer 55 class LogicalRewriter extends RecursiveTransformer
64 implements Pass { 56 implements Pass {
65 String get passName => 'Logical rewriter'; 57 String get passName => 'Logical rewriter';
66 58
67 @override 59 @override
68 void rewrite(FunctionDefinition node) { 60 void rewrite(FunctionDefinition node) {
69 node.body = visitStatement(node.body); 61 node.body = visitStatement(node.body);
70 } 62 }
71 63
(...skipping 15 matching lines...) Expand all
87 node.next = visitStatement(node.next); 79 node.next = visitStatement(node.next);
88 return node; 80 return node;
89 } 81 }
90 82
91 bool isFallthroughBreak(Statement node) { 83 bool isFallthroughBreak(Statement node) {
92 return node is Break && node.target.binding.next == fallthrough; 84 return node is Break && node.target.binding.next == fallthrough;
93 } 85 }
94 86
95 Statement visitIf(If node) { 87 Statement visitIf(If node) {
96 // If one of the branches is empty (i.e. just a fallthrough), then that 88 // If one of the branches is empty (i.e. just a fallthrough), then that
97 // branch should preferrably be the 'else' so we won't have to print it. 89 // branch should preferably be the 'else' so we won't have to print it.
98 // In other words, we wish to perform this rewrite: 90 // In other words, we wish to perform this rewrite:
99 // if (E) {} else {S} 91 // if (E) {} else {S}
100 // ==> 92 // ==>
101 // if (!E) {S} 93 // if (!E) {S}
102 // In the tree language, empty statements do not exist yet, so we must check 94 // In the tree language, empty statements do not exist yet, so we must check
103 // if one branch contains a break that can be eliminated by fallthrough. 95 // if one branch contains a break that can be eliminated by fallthrough.
104 96
105 // Swap branches if then is a fallthrough break. 97 // Swap branches if then is a fallthrough break.
106 if (isFallthroughBreak(node.thenStatement)) { 98 if (isFallthroughBreak(node.thenStatement)) {
107 node.condition = new Not(node.condition); 99 node.condition = new Not(node.condition);
(...skipping 28 matching lines...) Expand all
136 node.condition = makeCondition(node.condition, true, liftNots: false); 128 node.condition = makeCondition(node.condition, true, liftNots: false);
137 node.body = visitStatement(node.body); 129 node.body = visitStatement(node.body);
138 node.next = visitStatement(node.next); 130 node.next = visitStatement(node.next);
139 return node; 131 return node;
140 } 132 }
141 133
142 Expression visitNot(Not node) { 134 Expression visitNot(Not node) {
143 return toBoolean(makeCondition(node.operand, false, liftNots: false)); 135 return toBoolean(makeCondition(node.operand, false, liftNots: false));
144 } 136 }
145 137
138 /// True if the only possible falsy return value of [condition] is [value].
139 ///
140 /// If [value] is `null` or a truthy value, false is returned. This is to make
141 /// pattern matching more convenient.
142 bool matchesFalsyValue(Expression condition, values.ConstantValue value) {
143 if (value == null) return false;
144 // TODO(asgerf): Here we could really use some more type information,
145 // this is just the best we can do at the moment.
146 return isBooleanValued(condition) && value.isFalse;
147 }
148
149 /// True if the only possible truthy return value of [condition] is [value].
150 ///
151 /// If [value] is `null` or a falsy value, false is returned. This is to make
152 /// pattern matching more convenient.
153 bool matchesTruthyValue(Expression condition, values.ConstantValue value) {
154 if (value == null) return false;
155 // TODO(asgerf): Again, more type information could really beef this up.
156 return isBooleanValued(condition) && value.isTrue;
157 }
158
159 values.ConstantValue getConstant(Expression exp) {
160 return exp is Constant ? exp.value : null;
161 }
162
146 Expression visitConditional(Conditional node) { 163 Expression visitConditional(Conditional node) {
147 // node.condition will be visited after the then and else parts, because its 164 // node.condition will be visited after the then and else parts, because its
148 // polarity depends on what rewrite we use. 165 // polarity depends on what rewrite we use.
149 node.thenExpression = visitExpression(node.thenExpression); 166 node.thenExpression = visitExpression(node.thenExpression);
150 node.elseExpression = visitExpression(node.elseExpression); 167 node.elseExpression = visitExpression(node.elseExpression);
151 168
152 // In the following, we must take care not to eliminate or introduce a 169 // In the following, we must take care not to eliminate or introduce a
153 // boolean conversion. 170 // boolean conversion.
154 171
155 // x ? true : false --> !!x 172 // x ? true : false --> !!x
156 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) { 173 if (isTrue(node.thenExpression) && isFalse(node.elseExpression)) {
157 return toBoolean(makeCondition(node.condition, true, liftNots: false)); 174 return toBoolean(makeCondition(node.condition, true, liftNots: false));
158 } 175 }
159 // x ? false : true --> !x 176 // x ? false : true --> !x
160 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) { 177 if (isFalse(node.thenExpression) && isTrue(node.elseExpression)) {
161 return toBoolean(makeCondition(node.condition, false, liftNots: false)); 178 return toBoolean(makeCondition(node.condition, false, liftNots: false));
162 } 179 }
163 180
164 // x ? y : false ==> x && y (if y is known to be a boolean) 181 // x ? y : false ==> x && y (if x is truthy or false)
165 if (isBooleanValued(node.thenExpression) && isFalse(node.elseExpression)) { 182 // x ? y : null ==> x && y (if x is truthy or null)
183 // x ? y : 0 ==> x && y (if x is truthy or zero) (and so on...)
184 if (matchesFalsyValue(node.condition, getConstant(node.elseExpression))) {
166 return new LogicalOperator.and( 185 return new LogicalOperator.and(
167 makeCondition(node.condition, true, liftNots:false), 186 visitExpression(node.condition),
168 putInBooleanContext(node.thenExpression)); 187 node.thenExpression);
169 } 188 }
170 // x ? y : true ==> !x || y (if y is known to be a boolean) 189 // x ? true : y ==> x || y (if x is falsy or true)
171 if (isBooleanValued(node.thenExpression) && isTrue(node.elseExpression)) { 190 // x ? 1 : y ==> x || y (if x is falsy or one) (and so on...)
191 if (matchesTruthyValue(node.condition, getConstant(node.thenExpression))) {
172 return new LogicalOperator.or( 192 return new LogicalOperator.or(
173 makeCondition(node.condition, false, liftNots: false), 193 visitExpression(node.condition),
174 putInBooleanContext(node.thenExpression)); 194 node.elseExpression);
175 } 195 }
176 // x ? true : y ==> x || y (if y if known to be boolean) 196 // x ? y : true ==> !x || y
177 if (isBooleanValued(node.elseExpression) && isTrue(node.thenExpression)) { 197 if (isTrue(node.elseExpression)) {
178 return new LogicalOperator.or( 198 return new LogicalOperator.or(
179 makeCondition(node.condition, true, liftNots: false), 199 toBoolean(makeCondition(node.condition, false, liftNots: false)),
180 putInBooleanContext(node.elseExpression)); 200 node.thenExpression);
181 } 201 }
182 // x ? false : y ==> !x && y (if y is known to be a boolean) 202 // x ? false : y ==> !x && y
183 if (isBooleanValued(node.elseExpression) && isFalse(node.thenExpression)) { 203 if (isFalse(node.thenExpression)) {
184 return new LogicalOperator.and( 204 return new LogicalOperator.and(
185 makeCondition(node.condition, false, liftNots: false), 205 toBoolean(makeCondition(node.condition, false, liftNots: false)),
186 putInBooleanContext(node.elseExpression)); 206 node.elseExpression);
187 } 207 }
188 208
189 node.condition = makeCondition(node.condition, true); 209 node.condition = makeCondition(node.condition, true);
190 210
191 // !x ? y : z ==> x ? z : y 211 // !x ? y : z ==> x ? z : y
192 if (node.condition is Not) { 212 if (node.condition is Not) {
193 node.condition = (node.condition as Not).operand; 213 node.condition = (node.condition as Not).operand;
194 Expression tmp = node.thenExpression; 214 Expression tmp = node.thenExpression;
195 node.thenExpression = node.elseExpression; 215 node.thenExpression = node.elseExpression;
196 node.elseExpression = tmp; 216 node.elseExpression = tmp;
197 } 217 }
198 218
199 return node; 219 return node;
200 } 220 }
201 221
202 Expression visitLogicalOperator(LogicalOperator node) { 222 Expression visitLogicalOperator(LogicalOperator node) {
203 node.left = makeCondition(node.left, true); 223 node.left = visitExpression(node.left);
204 node.right = makeCondition(node.right, true); 224 node.right = visitExpression(node.right);
205 return node; 225 return node;
206 } 226 }
207 227
208 /// True if the given expression is known to evaluate to a boolean. 228 /// True if the given expression is known to evaluate to a boolean.
209 /// This will not recursively traverse [Conditional] expressions, but if 229 /// This will not recursively traverse [Conditional] expressions, but if
210 /// applied to the result of [visitExpression] conditionals will have been 230 /// applied to the result of [visitExpression] conditionals will have been
211 /// rewritten anyway. 231 /// rewritten anyway.
212 bool isBooleanValued(Expression e) { 232 bool isBooleanValued(Expression e) {
213 return isTrue(e) || 233 return isTrue(e) ||
214 isFalse(e) || 234 isFalse(e) ||
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 case BuiltinOperator.NumLe: 266 case BuiltinOperator.NumLe:
247 case BuiltinOperator.NumGt: 267 case BuiltinOperator.NumGt:
248 case BuiltinOperator.NumGe: 268 case BuiltinOperator.NumGe:
249 return null; 269 return null;
250 270
251 default: 271 default:
252 return null; 272 return null;
253 } 273 }
254 } 274 }
255 275
256 /// Rewrite an expression that was originally processed in a non-boolean
257 /// context.
258 Expression putInBooleanContext(Expression e) {
259 if (e is Not && e.operand is Not) {
260 return (e.operand as Not).operand;
261 } else {
262 return e;
263 }
264 }
265
266 /// Forces a boolean conversion of the given expression. 276 /// Forces a boolean conversion of the given expression.
267 Expression toBoolean(Expression e) { 277 Expression toBoolean(Expression e) {
268 if (isBooleanValued(e)) 278 if (isBooleanValued(e))
269 return e; 279 return e;
270 else 280 else
271 return new Not(new Not(e)); 281 return new Not(new Not(e));
272 } 282 }
273 283
274 /// Creates an equivalent boolean expression. The expression must occur in a 284 /// Creates an equivalent boolean expression. The expression must occur in a
275 /// context where its result is immediately subject to boolean conversion. 285 /// context where its result is immediately subject to boolean conversion.
276 /// If [polarity] if false, the negated condition will be created instead. 286 /// If [polarity] if false, the negated condition will be created instead.
277 /// If [liftNots] is true (default) then Not expressions will be lifted toward 287 /// If [liftNots] is true (default) then Not expressions will be lifted toward
278 /// the root the condition so they can be eliminated by the caller. 288 /// the root of the condition so they can be eliminated by the caller.
279 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) { 289 Expression makeCondition(Expression e, bool polarity, {bool liftNots:true}) {
280 if (e is Not) { 290 if (e is Not) {
281 // !!E ==> E 291 // !!E ==> E
282 return makeCondition(e.operand, !polarity, liftNots: liftNots); 292 return makeCondition(e.operand, !polarity, liftNots: liftNots);
283 } 293 }
284 if (e is LogicalOperator) { 294 if (e is LogicalOperator) {
285 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y 295 // If polarity=false, then apply the rewrite !(x && y) ==> !x || !y
286 e.left = makeCondition(e.left, polarity); 296 e.left = makeCondition(e.left, polarity);
287 e.right = makeCondition(e.right, polarity); 297 e.right = makeCondition(e.right, polarity);
288 if (!polarity) { 298 if (!polarity) {
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 403
394 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) { 404 Expression makeOr(Expression e1, Expression e2, {bool liftNots: true}) {
395 if (e1 is Not && e2 is Not && liftNots) { 405 if (e1 is Not && e2 is Not && liftNots) {
396 return new Not(new LogicalOperator.and(e1.operand, e2.operand)); 406 return new Not(new LogicalOperator.and(e1.operand, e2.operand));
397 } else { 407 } else {
398 return new LogicalOperator.or(e1, e2); 408 return new LogicalOperator.or(e1, e2);
399 } 409 }
400 } 410 }
401 } 411 }
402 412
OLDNEW
« no previous file with comments | « no previous file | tests/language/conditional_rewrite_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698