| 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 } |
| 418 int shiftAmount = getIntConstant(right); |
| 419 if (shiftAmount != null) { |
| 420 // TODO(asgerf): Compute upper bound on [leftVar] and use that |
| 421 // instead of MAX_UINT32. |
| 422 makeLessThanOrEqualToConstant(result, MAX_UINT32 >> shiftAmount); |
| 423 } |
| 424 break; |
| 425 |
| 426 case BuiltinOperator.NumRemainder: |
| 427 // TODO(asgerf): This check overlaps with checks performed in a type |
| 428 // propagation transformation, and we can do it more precisely here. |
| 429 // Should we do the rewrite here? |
| 430 if (isDefinitelyGreaterThanOrEqualToConstant(leftVar, 0) && |
| 431 isDefinitelyGreaterThanOrEqualToConstant(rightVar, 1)) { |
| 432 makeLessThanOrEqual(result, leftVar); |
| 433 makeLessThan(result, rightVar); |
| 434 } |
| 435 break; |
| 436 |
| 437 case BuiltinOperator.NumAnd: |
| 438 // We use the faster UInt32 check instead of constraint based checks |
| 439 // here, because the common case is that one operand is a constant. |
| 440 if (isUInt32(left)) { |
| 441 makeLessThanOrEqual(result, leftVar); |
| 442 } |
| 443 if (isUInt32(right)) { |
| 444 makeLessThanOrEqual(result, rightVar); |
| 445 } |
| 446 break; |
| 447 |
| 448 default: |
| 449 } |
| 450 } |
| 451 |
| 452 void applyUnaryOperator(ApplyBuiltinOperator node) { |
| 453 Primitive argument = node.arguments[0].definition; |
| 454 if (!isInt(argument)) return; |
| 455 if (node.operator == BuiltinOperator.NumNegate) { |
| 456 valueOf[node] = getValue(argument).negated; |
| 457 } |
| 458 } |
| 459 |
| 460 int getIntConstant(Primitive prim) { |
| 461 if (prim is Constant && prim.value.isInt) { |
| 462 IntConstantValue constant = prim.value; |
| 463 return constant.primitiveValue; |
| 464 } |
| 465 return null; |
| 466 } |
| 467 |
| 384 @override | 468 @override |
| 385 void visitGetLength(GetLength node) { | 469 void visitGetLength(GetLength node) { |
| 386 valueOf[node] = getLength(node.object.definition, currentEffectNumber); | 470 valueOf[node] = getLength(node.object.definition, currentEffectNumber); |
| 387 } | 471 } |
| 388 | 472 |
| 389 void analyzeLoopEntry(InvokeContinuation node) { | 473 void analyzeLoopEntry(InvokeContinuation node) { |
| 390 foundLoop = true; | 474 foundLoop = true; |
| 391 Continuation cont = node.continuation.definition; | 475 Continuation cont = node.continuation.definition; |
| 392 if (isStrongLoopPass) { | 476 if (isStrongLoopPass) { |
| 393 for (int i = 0; i < node.arguments.length; ++i) { | 477 for (int i = 0; i < node.arguments.length; ++i) { |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 } | 673 } |
| 590 return node.body; | 674 return node.body; |
| 591 } | 675 } |
| 592 } | 676 } |
| 593 | 677 |
| 594 /// Lattice representing the known (weak) monotonicity of a loop variable. | 678 /// Lattice representing the known (weak) monotonicity of a loop variable. |
| 595 /// | 679 /// |
| 596 /// The lattice bottom is represented by `null` and represents the case where | 680 /// The lattice bottom is represented by `null` and represents the case where |
| 597 /// the loop variable never changes value during the loop. | 681 /// the loop variable never changes value during the loop. |
| 598 enum Monotonicity { NotMonotone, Increasing, Decreasing, } | 682 enum Monotonicity { NotMonotone, Increasing, Decreasing, } |
| OLD | NEW |