diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h index 05d87e176dec1..5bcefe4b6c361 100644 --- a/llvm/include/llvm/Support/MathExtras.h +++ b/llvm/include/llvm/Support/MathExtras.h @@ -385,6 +385,8 @@ inline uint64_t PowerOf2Ceil(uint64_t A) { /// alignTo(~0LL, 8) = 0 /// alignTo(321, 255) = 510 /// \endcode +/// +/// May overflow. inline uint64_t alignTo(uint64_t Value, uint64_t Align) { assert(Align != 0u && "Align can't be 0."); return (Value + Align - 1) / Align * Align; @@ -424,33 +426,38 @@ template constexpr inline uint64_t alignTo(uint64_t Value) { return (Value + Align - 1) / Align * Align; } -/// Returns the integer ceil(Numerator / Denominator). Unsigned integer version. +/// Returns the integer ceil(Numerator / Denominator). Unsigned version. +/// Guaranteed to never overflow. inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) { - return alignTo(Numerator, Denominator) / Denominator; + assert(Denominator && "Division by zero"); + uint64_t Bias = (Numerator != 0); + return (Numerator - Bias) / Denominator + Bias; } -/// Returns the integer ceil(Numerator / Denominator). Signed integer version. +/// Returns the integer ceil(Numerator / Denominator). Signed version. +/// Guaranteed to never overflow. inline int64_t divideCeilSigned(int64_t Numerator, int64_t Denominator) { assert(Denominator && "Division by zero"); if (!Numerator) return 0; // C's integer division rounds towards 0. - int64_t X = (Denominator > 0) ? -1 : 1; - bool SameSign = (Numerator > 0) == (Denominator > 0); - return SameSign ? ((Numerator + X) / Denominator) + 1 + int64_t Bias = (Denominator >= 0 ? 1 : -1); + bool SameSign = (Numerator >= 0) == (Denominator >= 0); + return SameSign ? (Numerator - Bias) / Denominator + 1 : Numerator / Denominator; } -/// Returns the integer floor(Numerator / Denominator). Signed integer version. +/// Returns the integer floor(Numerator / Denominator). Signed version. +/// Guaranteed to never overflow. inline int64_t divideFloorSigned(int64_t Numerator, int64_t Denominator) { assert(Denominator && "Division by zero"); if (!Numerator) return 0; // C's integer division rounds towards 0. - int64_t X = (Denominator > 0) ? -1 : 1; - bool SameSign = (Numerator > 0) == (Denominator > 0); + int64_t Bias = Denominator >= 0 ? -1 : 1; + bool SameSign = (Numerator >= 0) == (Denominator >= 0); return SameSign ? Numerator / Denominator - : -((-Numerator + X) / Denominator) - 1; + : (Numerator - Bias) / Denominator - 1; } /// 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) { return Mod < 0 ? Mod + Denominator : Mod; } -/// Returns the integer nearest(Numerator / Denominator). +/// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to +/// never overflow. inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) { - return (Numerator + (Denominator / 2)) / Denominator; + assert(Denominator && "Division by zero"); + uint64_t Mod = Numerator % Denominator; + return (Numerator / Denominator) + (Mod > (Denominator - 1) / 2); } /// Returns the largest uint64_t less than or equal to \p Value and is diff --git a/llvm/unittests/Support/MathExtrasTest.cpp b/llvm/unittests/Support/MathExtrasTest.cpp index bcccb963c96ae..bd09bab9be004 100644 --- a/llvm/unittests/Support/MathExtrasTest.cpp +++ b/llvm/unittests/Support/MathExtrasTest.cpp @@ -459,15 +459,37 @@ TEST(MathExtras, DivideNearest) { EXPECT_EQ(divideNearest(14, 3), 5u); EXPECT_EQ(divideNearest(15, 3), 5u); EXPECT_EQ(divideNearest(0, 3), 0u); + EXPECT_EQ(divideNearest(5, 4), 1u); + EXPECT_EQ(divideNearest(6, 4), 2u); + EXPECT_EQ(divideNearest(3, 1), 3u); + EXPECT_EQ(divideNearest(3, 6), 1u); + EXPECT_EQ(divideNearest(3, 7), 0u); EXPECT_EQ(divideNearest(std::numeric_limits::max(), 2), - 2147483648u); + std::numeric_limits::max() / 2 + 1); + EXPECT_EQ(divideNearest(std::numeric_limits::max(), 2), + std::numeric_limits::max() / 2 + 1); + EXPECT_EQ(divideNearest(std::numeric_limits::max(), 1), + std::numeric_limits::max()); + EXPECT_EQ(divideNearest(std::numeric_limits::max() - 1, + std::numeric_limits::max()), + 1u); } TEST(MathExtras, DivideCeil) { EXPECT_EQ(divideCeil(14, 3), 5u); EXPECT_EQ(divideCeil(15, 3), 5u); EXPECT_EQ(divideCeil(0, 3), 0u); - EXPECT_EQ(divideCeil(std::numeric_limits::max(), 2), 2147483648u); + EXPECT_EQ(divideCeil(5, 4), 2u); + EXPECT_EQ(divideCeil(6, 4), 2u); + EXPECT_EQ(divideCeil(3, 1), 3u); + EXPECT_EQ(divideCeil(3, 6), 1u); + EXPECT_EQ(divideCeil(3, 7), 1u); + EXPECT_EQ(divideCeil(std::numeric_limits::max(), 2), + std::numeric_limits::max() / 2 + 1); + EXPECT_EQ(divideCeil(std::numeric_limits::max(), 2), + std::numeric_limits::max() / 2 + 1); + EXPECT_EQ(divideCeil(std::numeric_limits::max(), 1), + std::numeric_limits::max()); EXPECT_EQ(divideCeilSigned(14, 3), 5); EXPECT_EQ(divideCeilSigned(15, 3), 5); @@ -479,8 +501,14 @@ TEST(MathExtras, DivideCeil) { EXPECT_EQ(divideCeilSigned(0, -3), 0); EXPECT_EQ(divideCeilSigned(std::numeric_limits::max(), 2), std::numeric_limits::max() / 2 + 1); + EXPECT_EQ(divideCeilSigned(std::numeric_limits::max(), 2), + std::numeric_limits::max() / 2 + 1); EXPECT_EQ(divideCeilSigned(std::numeric_limits::max(), -2), std::numeric_limits::min() / 2 + 1); + EXPECT_EQ(divideCeilSigned(std::numeric_limits::max(), -2), + std::numeric_limits::min() / 2 + 1); + EXPECT_EQ(divideCeilSigned(std::numeric_limits::min(), 1), + std::numeric_limits::min()); } TEST(MathExtras, DivideFloorSigned) { @@ -494,8 +522,14 @@ TEST(MathExtras, DivideFloorSigned) { EXPECT_EQ(divideFloorSigned(0, -3), 0); EXPECT_EQ(divideFloorSigned(std::numeric_limits::max(), 2), std::numeric_limits::max() / 2); + EXPECT_EQ(divideFloorSigned(std::numeric_limits::max(), 2), + std::numeric_limits::max() / 2); EXPECT_EQ(divideFloorSigned(std::numeric_limits::max(), -2), std::numeric_limits::min() / 2); + EXPECT_EQ(divideFloorSigned(std::numeric_limits::max(), -2), + std::numeric_limits::min() / 2); + EXPECT_EQ(divideFloorSigned(std::numeric_limits::min(), 1), + std::numeric_limits::min()); } TEST(MathExtras, Mod) {