Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 dart2js.cps_ir.bounds_checker; | 5 library dart2js.cps_ir.bounds_checker; |
| 6 | 6 |
| 7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
| 8 import 'optimizers.dart' show Pass; | 8 import 'optimizers.dart' show Pass; |
| 9 import 'octagon.dart'; | 9 import 'octagon.dart'; |
| 10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 void makeGreaterThanOrEqual(SignedVariable v1, SignedVariable v2) { | 161 void makeGreaterThanOrEqual(SignedVariable v1, SignedVariable v2) { |
| 162 // v1 >= v2 <==> v2 - v1 <= 0 | 162 // v1 >= v2 <==> v2 - v1 <= 0 |
| 163 applyConstraint(v2, v1.negated, 0); | 163 applyConstraint(v2, v1.negated, 0); |
| 164 } | 164 } |
| 165 | 165 |
| 166 void makeGreaterThan(SignedVariable v1, SignedVariable v2) { | 166 void makeGreaterThan(SignedVariable v1, SignedVariable v2) { |
| 167 // v1 > v2 <==> v2 - v1 <= -1 | 167 // v1 > v2 <==> v2 - v1 <= -1 |
| 168 applyConstraint(v2, v1.negated, -1); | 168 applyConstraint(v2, v1.negated, -1); |
| 169 } | 169 } |
| 170 | 170 |
| 171 void makeLessThanOrEqualToConstant(SignedVariable v1, int k) { | |
| 172 // v1 + v1 <= 2k | |
| 173 applyConstraint(v1, v1, 2 * k); | |
| 174 } | |
| 175 | |
| 176 void makeGreaterThanOrEqualToConstant(SignedVariable v1, int k) { | |
| 177 // -v1 - v1 <= -2k | |
| 178 applyConstraint(v1.negated, v1.negated, -2 * k); | |
| 179 } | |
| 180 | |
| 171 void makeConstant(SignedVariable v1, int k) { | 181 void makeConstant(SignedVariable v1, int k) { |
| 172 // We model this using the constraints: | 182 // We model this using the constraints: |
| 173 // v1 + v1 <= 2k | 183 // v1 + v1 <= 2k |
| 174 // -v1 - v1 <= -2k | 184 // -v1 - v1 <= -2k |
| 175 applyConstraint(v1, v1, 2 * k); | 185 applyConstraint(v1, v1, 2 * k); |
| 176 applyConstraint(v1.negated, v1.negated, -2 * k); | 186 applyConstraint(v1.negated, v1.negated, -2 * k); |
| 177 } | 187 } |
| 178 | 188 |
| 179 /// Make `v1 = v2 + k`. | 189 /// Make `v1 = v2 + k`. |
| 180 void makeExactSum(SignedVariable v1, SignedVariable v2, int k) { | 190 void makeExactSum(SignedVariable v1, SignedVariable v2, int k) { |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 322 // TODO(asgerf): It might be faster to inline the constant in the | 332 // TODO(asgerf): It might be faster to inline the constant in the |
| 323 // constraints that reference it. | 333 // constraints that reference it. |
| 324 if (node.value.isInt) { | 334 if (node.value.isInt) { |
| 325 IntConstantValue constant = node.value; | 335 IntConstantValue constant = node.value; |
| 326 makeConstant(getValue(node), constant.primitiveValue); | 336 makeConstant(getValue(node), constant.primitiveValue); |
| 327 } | 337 } |
| 328 } | 338 } |
| 329 | 339 |
| 330 @override | 340 @override |
| 331 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { | 341 void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| 332 if (node.operator != BuiltinOperator.NumAdd && | 342 if (!isInt(node)) return; |
| 333 node.operator != BuiltinOperator.NumSubtract) { | 343 if (node.arguments.length == 1) { |
| 334 return; | 344 applyUnaryOperator(node); |
| 335 } | 345 } else if (node.arguments.length == 2) { |
| 336 if (!isInt(node.arguments[0].definition) || | 346 applyBinaryOperator(node); |
| 337 !isInt(node.arguments[1].definition)) { | |
| 338 return; | |
| 339 } | |
| 340 if (!isInt(node)) { | |
| 341 // TODO(asgerf): The result of this operation should always be an integer, | |
| 342 // but currently type propagation does not always prove this. | |
| 343 return; | |
| 344 } | |
| 345 // We have `v1 = v2 +/- v3`, but the octagon cannot represent constraints | |
| 346 // involving more than two variables. Check if one operand is a constant. | |
| 347 int getConstantArgument(int n) { | |
| 348 Primitive prim = node.arguments[n].definition; | |
| 349 if (prim is Constant && prim.value.isInt) { | |
| 350 IntConstantValue constant = prim.value; | |
| 351 return constant.primitiveValue; | |
| 352 } | |
| 353 return null; | |
| 354 } | |
| 355 int constant = getConstantArgument(0); | |
| 356 int operandIndex = 1; | |
| 357 if (constant == null) { | |
| 358 constant = getConstantArgument(1); | |
| 359 operandIndex = 0; | |
| 360 } | |
| 361 if (constant == null) { | |
| 362 // Neither argument was a constant. | |
| 363 // Classical octagon-based analyzers would compute upper and lower bounds | |
| 364 // for the two operands and add constraints for the result based on | |
| 365 // those. For performance reasons we omit that. | |
| 366 // TODO(asgerf): It seems expensive, but we should evaluate it. | |
| 367 return; | |
| 368 } | |
| 369 SignedVariable v1 = getValue(node); | |
| 370 SignedVariable v2 = getValue(node.arguments[operandIndex].definition); | |
| 371 | |
| 372 if (node.operator == BuiltinOperator.NumAdd) { | |
| 373 // v1 = v2 + const | |
| 374 makeFloatingPointSum(v1, v2, constant); | |
| 375 } else if (operandIndex == 0) { | |
| 376 // v1 = v2 - const | |
| 377 makeFloatingPointSum(v1, v2, -constant); | |
| 378 } else { | |
| 379 // v1 = const - v2 <==> v1 = (-v2) + const | |
| 380 makeFloatingPointSum(v1, v2.negated, constant); | |
| 381 } | 347 } |
| 382 } | 348 } |
| 383 | 349 |
| 350 void applyBinaryOperator(ApplyBuiltinOperator node) { | |
| 351 Primitive left = node.arguments[0].definition; | |
| 352 Primitive right = node.arguments[1].definition; | |
| 353 if (!isInt(left) || !isInt(right)) { | |
| 354 return; | |
| 355 } | |
| 356 SignedVariable leftVar = getValue(left); | |
| 357 SignedVariable rightVar = getValue(right); | |
| 358 SignedVariable result = getValue(node); | |
| 359 switch (node.operator) { | |
| 360 case BuiltinOperator.NumAdd: | |
| 361 int leftConst = getIntConstant(left); | |
| 362 if (leftConst != null) { | |
| 363 makeFloatingPointSum(result, rightVar, leftConst); | |
| 364 } | |
| 365 int rightConst = getIntConstant(right); | |
| 366 if (rightConst != null) { | |
| 367 makeFloatingPointSum(result, leftVar, rightConst); | |
| 368 } | |
| 369 // Attempt to compute the sign of the result. | |
| 370 // TODO(asgerf): Compute upper/lower bounds instead of using 0. | |
| 371 if (testConstraint(leftVar, rightVar, 0)) { | |
| 372 makeLessThanOrEqualToConstant(result, 0); | |
| 373 } | |
| 374 if (testConstraint(leftVar.negated, rightVar.negated, 0)) { | |
| 375 makeGreaterThanOrEqualToConstant(result, 0); | |
| 376 } | |
| 377 // Classical octagon-based analyzers would compute upper and lower | |
| 378 // bounds for the two operands and add constraints for the result based | |
| 379 // on those. For performance reasons we only compute the sign | |
| 380 // TODO(asgerf): It seems expensive, but we should evaluate it. | |
| 381 break; | |
| 382 | |
| 383 case BuiltinOperator.NumSubtract: | |
| 384 int leftConst = getIntConstant(left); | |
| 385 if (leftConst != null) { | |
| 386 // result = leftConst - right = (-right) + leftConst | |
| 387 makeFloatingPointSum(result, rightVar.negated, leftConst); | |
| 388 } | |
| 389 int rightConst = getIntConstant(right); | |
| 390 if (rightConst != null) { | |
| 391 // result = left - rightConst = left + (-rightConst) | |
| 392 makeFloatingPointSum(result, leftVar, -rightConst); | |
| 393 } | |
| 394 // Attempt to compute the sign of the result. | |
| 395 if (isDefinitelyGreaterThanOrEqualTo(leftVar, rightVar)) { | |
| 396 makeGreaterThanOrEqualToConstant(result, 0); | |
| 397 } | |
| 398 if (isDefinitelyLessThanOrEqualTo(leftVar, rightVar)) { | |
| 399 makeLessThanOrEqualToConstant(result, 0); | |
| 400 } | |
| 401 break; | |
| 402 | |
| 403 case BuiltinOperator.NumTruncatingDivideToSigned32: | |
| 404 if (isDefinitelyGreaterThanOrEqualToConstant(leftVar, 0)) { | |
| 405 // If we divide by a positive number, the result is closer to zero. | |
| 406 // If we divide by a negative number, the result is negative, and | |
| 407 // thus less than the original (non-negative) number. | |
| 408 // TODO(asgerf): The divisor is currently always positive, because | |
| 409 // type propagation checks that, but we could do better. | |
| 410 makeLessThanOrEqual(result, leftVar); | |
| 411 } | |
| 412 break; | |
| 413 | |
| 414 case BuiltinOperator.NumShr: | |
| 415 if (isDefinitelyGreaterThanOrEqualToConstant(leftVar, 0)) { | |
| 416 makeLessThanOrEqual(result, leftVar); | |
| 417 } | |
|
sra1
2015/11/27 20:28:21
Also: x >> k <= MAX_UINT32 >> k
Base64(De|En)c
asgerf
2015/11/30 13:46:03
Done.
| |
| 418 break; | |
| 419 | |
| 420 case BuiltinOperator.NumRemainder: | |
| 421 // TODO(asgerf): This check overlaps with checks performed in a type | |
| 422 // propagation transformation, and we can do it more precisely here. | |
| 423 // Should we do the rewrite here? | |
|
sra1
2015/11/27 20:28:21
We would want to do the check on InvokeMethod to s
asgerf
2015/11/30 13:46:03
My point was we could consider doing the rewriting
| |
| 424 if (isDefinitelyGreaterThanOrEqualToConstant(leftVar, 0) && | |
|
sra1
2015/11/27 20:28:21
We do this test a lot. How many steps does it take
asgerf
2015/11/30 13:46:03
The bounds checker is a bit wasteful like this, bu
asgerf
2015/12/01 16:19:35
I made unary checks a bit faster with a special ca
| |
| 425 isDefinitelyGreaterThanOrEqualToConstant(rightVar, 1)) { | |
| 426 makeLessThanOrEqual(result, leftVar); | |
| 427 makeLessThan(result, rightVar); | |
| 428 } | |
| 429 break; | |
| 430 | |
| 431 case BuiltinOperator.NumAnd: | |
| 432 // We use the faster UInt32 check instead of constraint based checks | |
| 433 // here, because the common case is that one operand is a constant. | |
| 434 if (isUInt32(left)) { | |
| 435 makeLessThanOrEqual(result, leftVar); | |
| 436 } | |
| 437 if (isUInt32(right)) { | |
| 438 makeLessThanOrEqual(result, rightVar); | |
| 439 } | |
| 440 break; | |
| 441 | |
| 442 default: | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 void applyUnaryOperator(ApplyBuiltinOperator node) { | |
| 447 Primitive argument = node.arguments[0].definition; | |
| 448 if (!isInt(argument)) return; | |
| 449 if (node.operator == BuiltinOperator.NumNegate) { | |
| 450 valueOf[node] = getValue(argument).negated; | |
| 451 } | |
|
sra1
2015/11/27 20:28:21
~x == MAX_UINT32 - x, if x is uint32.
Not sure
asgerf
2015/11/30 13:46:03
Seems really unlikely to make a difference.
Conne
| |
| 452 } | |
| 453 | |
| 454 int getIntConstant(Primitive prim) { | |
| 455 if (prim is Constant && prim.value.isInt) { | |
| 456 IntConstantValue constant = prim.value; | |
| 457 return constant.primitiveValue; | |
| 458 } | |
| 459 return null; | |
| 460 } | |
| 461 | |
| 384 @override | 462 @override |
| 385 void visitGetLength(GetLength node) { | 463 void visitGetLength(GetLength node) { |
| 386 valueOf[node] = getLength(node.object.definition, currentEffectNumber); | 464 valueOf[node] = getLength(node.object.definition, currentEffectNumber); |
| 387 } | 465 } |
| 388 | 466 |
| 389 void analyzeLoopEntry(InvokeContinuation node) { | 467 void analyzeLoopEntry(InvokeContinuation node) { |
| 390 foundLoop = true; | 468 foundLoop = true; |
| 391 Continuation cont = node.continuation.definition; | 469 Continuation cont = node.continuation.definition; |
| 392 if (isStrongLoopPass) { | 470 if (isStrongLoopPass) { |
| 393 for (int i = 0; i < node.arguments.length; ++i) { | 471 for (int i = 0; i < node.arguments.length; ++i) { |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 589 } | 667 } |
| 590 return node.body; | 668 return node.body; |
| 591 } | 669 } |
| 592 } | 670 } |
| 593 | 671 |
| 594 /// Lattice representing the known (weak) monotonicity of a loop variable. | 672 /// Lattice representing the known (weak) monotonicity of a loop variable. |
| 595 /// | 673 /// |
| 596 /// The lattice bottom is represented by `null` and represents the case where | 674 /// The lattice bottom is represented by `null` and represents the case where |
| 597 /// the loop variable never changes value during the loop. | 675 /// the loop variable never changes value during the loop. |
| 598 enum Monotonicity { NotMonotone, Increasing, Decreasing, } | 676 enum Monotonicity { NotMonotone, Increasing, Decreasing, } |
| OLD | NEW |