| OLD | NEW |
| (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 } | |
| OLD | NEW |