Skip to content

Commit bfd95a0

Browse files
authored
MathExtras: rewrite some methods to never overflow (#95556)
Rewrite divideCeil, divideNearest, divideFloorSigned, and divideCeilSigned to never overflow.
1 parent 23c1b48 commit bfd95a0

File tree

2 files changed

+58
-14
lines changed

2 files changed

+58
-14
lines changed

llvm/include/llvm/Support/MathExtras.h

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ inline uint64_t PowerOf2Ceil(uint64_t A) {
385385
/// alignTo(~0LL, 8) = 0
386386
/// alignTo(321, 255) = 510
387387
/// \endcode
388+
///
389+
/// May overflow.
388390
inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
389391
assert(Align != 0u && "Align can't be 0.");
390392
return (Value + Align - 1) / Align * Align;
@@ -424,33 +426,38 @@ template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) {
424426
return (Value + Align - 1) / Align * Align;
425427
}
426428

427-
/// Returns the integer ceil(Numerator / Denominator). Unsigned integer version.
429+
/// Returns the integer ceil(Numerator / Denominator). Unsigned version.
430+
/// Guaranteed to never overflow.
428431
inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
429-
return alignTo(Numerator, Denominator) / Denominator;
432+
assert(Denominator && "Division by zero");
433+
uint64_t Bias = (Numerator != 0);
434+
return (Numerator - Bias) / Denominator + Bias;
430435
}
431436

432-
/// Returns the integer ceil(Numerator / Denominator). Signed integer version.
437+
/// Returns the integer ceil(Numerator / Denominator). Signed version.
438+
/// Guaranteed to never overflow.
433439
inline int64_t divideCeilSigned(int64_t Numerator, int64_t Denominator) {
434440
assert(Denominator && "Division by zero");
435441
if (!Numerator)
436442
return 0;
437443
// C's integer division rounds towards 0.
438-
int64_t X = (Denominator > 0) ? -1 : 1;
439-
bool SameSign = (Numerator > 0) == (Denominator > 0);
440-
return SameSign ? ((Numerator + X) / Denominator) + 1
444+
int64_t Bias = (Denominator >= 0 ? 1 : -1);
445+
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
446+
return SameSign ? (Numerator - Bias) / Denominator + 1
441447
: Numerator / Denominator;
442448
}
443449

444-
/// Returns the integer floor(Numerator / Denominator). Signed integer version.
450+
/// Returns the integer floor(Numerator / Denominator). Signed version.
451+
/// Guaranteed to never overflow.
445452
inline int64_t divideFloorSigned(int64_t Numerator, int64_t Denominator) {
446453
assert(Denominator && "Division by zero");
447454
if (!Numerator)
448455
return 0;
449456
// C's integer division rounds towards 0.
450-
int64_t X = (Denominator > 0) ? -1 : 1;
451-
bool SameSign = (Numerator > 0) == (Denominator > 0);
457+
int64_t Bias = Denominator >= 0 ? -1 : 1;
458+
bool SameSign = (Numerator >= 0) == (Denominator >= 0);
452459
return SameSign ? Numerator / Denominator
453-
: -((-Numerator + X) / Denominator) - 1;
460+
: (Numerator - Bias) / Denominator - 1;
454461
}
455462

456463
/// Returns the remainder of the Euclidean division of LHS by RHS. Result is
@@ -461,9 +468,12 @@ inline int64_t mod(int64_t Numerator, int64_t Denominator) {
461468
return Mod < 0 ? Mod + Denominator : Mod;
462469
}
463470

464-
/// Returns the integer nearest(Numerator / Denominator).
471+
/// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to
472+
/// never overflow.
465473
inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) {
466-
return (Numerator + (Denominator / 2)) / Denominator;
474+
assert(Denominator && "Division by zero");
475+
uint64_t Mod = Numerator % Denominator;
476+
return (Numerator / Denominator) + (Mod > (Denominator - 1) / 2);
467477
}
468478

469479
/// Returns the largest uint64_t less than or equal to \p Value and is

llvm/unittests/Support/MathExtrasTest.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,15 +459,37 @@ TEST(MathExtras, DivideNearest) {
459459
EXPECT_EQ(divideNearest(14, 3), 5u);
460460
EXPECT_EQ(divideNearest(15, 3), 5u);
461461
EXPECT_EQ(divideNearest(0, 3), 0u);
462+
EXPECT_EQ(divideNearest(5, 4), 1u);
463+
EXPECT_EQ(divideNearest(6, 4), 2u);
464+
EXPECT_EQ(divideNearest(3, 1), 3u);
465+
EXPECT_EQ(divideNearest(3, 6), 1u);
466+
EXPECT_EQ(divideNearest(3, 7), 0u);
462467
EXPECT_EQ(divideNearest(std::numeric_limits<uint32_t>::max(), 2),
463-
2147483648u);
468+
std::numeric_limits<uint32_t>::max() / 2 + 1);
469+
EXPECT_EQ(divideNearest(std::numeric_limits<uint64_t>::max(), 2),
470+
std::numeric_limits<uint64_t>::max() / 2 + 1);
471+
EXPECT_EQ(divideNearest(std::numeric_limits<uint64_t>::max(), 1),
472+
std::numeric_limits<uint64_t>::max());
473+
EXPECT_EQ(divideNearest(std::numeric_limits<uint64_t>::max() - 1,
474+
std::numeric_limits<uint64_t>::max()),
475+
1u);
464476
}
465477

466478
TEST(MathExtras, DivideCeil) {
467479
EXPECT_EQ(divideCeil(14, 3), 5u);
468480
EXPECT_EQ(divideCeil(15, 3), 5u);
469481
EXPECT_EQ(divideCeil(0, 3), 0u);
470-
EXPECT_EQ(divideCeil(std::numeric_limits<uint32_t>::max(), 2), 2147483648u);
482+
EXPECT_EQ(divideCeil(5, 4), 2u);
483+
EXPECT_EQ(divideCeil(6, 4), 2u);
484+
EXPECT_EQ(divideCeil(3, 1), 3u);
485+
EXPECT_EQ(divideCeil(3, 6), 1u);
486+
EXPECT_EQ(divideCeil(3, 7), 1u);
487+
EXPECT_EQ(divideCeil(std::numeric_limits<uint32_t>::max(), 2),
488+
std::numeric_limits<uint32_t>::max() / 2 + 1);
489+
EXPECT_EQ(divideCeil(std::numeric_limits<uint64_t>::max(), 2),
490+
std::numeric_limits<uint64_t>::max() / 2 + 1);
491+
EXPECT_EQ(divideCeil(std::numeric_limits<uint64_t>::max(), 1),
492+
std::numeric_limits<uint64_t>::max());
471493

472494
EXPECT_EQ(divideCeilSigned(14, 3), 5);
473495
EXPECT_EQ(divideCeilSigned(15, 3), 5);
@@ -479,8 +501,14 @@ TEST(MathExtras, DivideCeil) {
479501
EXPECT_EQ(divideCeilSigned(0, -3), 0);
480502
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int32_t>::max(), 2),
481503
std::numeric_limits<int32_t>::max() / 2 + 1);
504+
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::max(), 2),
505+
std::numeric_limits<int64_t>::max() / 2 + 1);
482506
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int32_t>::max(), -2),
483507
std::numeric_limits<int32_t>::min() / 2 + 1);
508+
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::max(), -2),
509+
std::numeric_limits<int64_t>::min() / 2 + 1);
510+
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::min(), 1),
511+
std::numeric_limits<int64_t>::min());
484512
}
485513

486514
TEST(MathExtras, DivideFloorSigned) {
@@ -494,8 +522,14 @@ TEST(MathExtras, DivideFloorSigned) {
494522
EXPECT_EQ(divideFloorSigned(0, -3), 0);
495523
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int32_t>::max(), 2),
496524
std::numeric_limits<int32_t>::max() / 2);
525+
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::max(), 2),
526+
std::numeric_limits<int64_t>::max() / 2);
497527
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int32_t>::max(), -2),
498528
std::numeric_limits<int32_t>::min() / 2);
529+
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::max(), -2),
530+
std::numeric_limits<int64_t>::min() / 2);
531+
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::min(), 1),
532+
std::numeric_limits<int64_t>::min());
499533
}
500534

501535
TEST(MathExtras, Mod) {

0 commit comments

Comments
 (0)