Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MathExtras: rewrite some methods to never overflow #95556

Merged
merged 5 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions llvm/include/llvm/Support/MathExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ inline uint64_t PowerOf2Ceil(uint64_t A) {
/// alignTo(~0LL, 8) = 0
/// alignTo(321, 255) = 510
/// \endcode
///
/// May overflow.
artagnon marked this conversation as resolved.
Show resolved Hide resolved
inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
assert(Align != 0u && "Align can't be 0.");
return (Value + Align - 1) / Align * Align;
Expand Down Expand Up @@ -424,33 +426,37 @@ template <uint64_t Align> 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;
uint64_t Bias = (Numerator != 0);
artagnon marked this conversation as resolved.
Show resolved Hide resolved
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;
int64_t Bias = (Denominator > 0 ? 1 : -1);
artagnon marked this conversation as resolved.
Show resolved Hide resolved
bool SameSign = (Numerator > 0) == (Denominator > 0);
return SameSign ? ((Numerator + X) / Denominator) + 1
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;
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;
artagnon marked this conversation as resolved.
Show resolved Hide resolved
}

/// Returns the remainder of the Euclidean division of LHS by RHS. Result is
Expand All @@ -461,9 +467,11 @@ 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. Guranteed to
artagnon marked this conversation as resolved.
Show resolved Hide resolved
/// never overflow.
inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) {
return (Numerator + (Denominator / 2)) / Denominator;
return (Numerator / Denominator) +
(Denominator == 1 ? 0 : Numerator % Denominator >= Denominator / 2);
artagnon marked this conversation as resolved.
Show resolved Hide resolved
}

/// Returns the largest uint64_t less than or equal to \p Value and is
Expand Down
27 changes: 25 additions & 2 deletions llvm/unittests/Support/MathExtrasTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,15 +459,30 @@ 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(std::numeric_limits<uint32_t>::max(), 2),
2147483648u);
std::numeric_limits<uint32_t>::max() / 2 + 1);
EXPECT_EQ(divideNearest(std::numeric_limits<uint64_t>::max(), 2),
std::numeric_limits<uint64_t>::max() / 2 + 1);
EXPECT_EQ(divideNearest(std::numeric_limits<uint64_t>::max(), 1),
std::numeric_limits<uint64_t>::max());
}

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<uint32_t>::max(), 2), 2147483648u);
EXPECT_EQ(divideCeil(5, 4), 2u);
EXPECT_EQ(divideCeil(6, 4), 2u);
EXPECT_EQ(divideCeil(3, 1), 3u);
EXPECT_EQ(divideCeil(std::numeric_limits<uint32_t>::max(), 2),
std::numeric_limits<uint32_t>::max() / 2 + 1);
EXPECT_EQ(divideCeil(std::numeric_limits<uint64_t>::max(), 2),
std::numeric_limits<uint64_t>::max() / 2 + 1);
EXPECT_EQ(divideCeil(std::numeric_limits<uint64_t>::max(), 1),
std::numeric_limits<uint64_t>::max());

EXPECT_EQ(divideCeilSigned(14, 3), 5);
EXPECT_EQ(divideCeilSigned(15, 3), 5);
Expand All @@ -479,8 +494,12 @@ TEST(MathExtras, DivideCeil) {
EXPECT_EQ(divideCeilSigned(0, -3), 0);
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int32_t>::max(), 2),
std::numeric_limits<int32_t>::max() / 2 + 1);
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::max(), 2),
std::numeric_limits<int64_t>::max() / 2 + 1);
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int32_t>::max(), -2),
std::numeric_limits<int32_t>::min() / 2 + 1);
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::max(), -2),
std::numeric_limits<int64_t>::min() / 2 + 1);
}

TEST(MathExtras, DivideFloorSigned) {
Expand All @@ -494,8 +513,12 @@ TEST(MathExtras, DivideFloorSigned) {
EXPECT_EQ(divideFloorSigned(0, -3), 0);
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int32_t>::max(), 2),
std::numeric_limits<int32_t>::max() / 2);
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::max(), 2),
std::numeric_limits<int64_t>::max() / 2);
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int32_t>::max(), -2),
std::numeric_limits<int32_t>::min() / 2);
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::max(), -2),
std::numeric_limits<int64_t>::min() / 2);
}

TEST(MathExtras, Mod) {
Expand Down
Loading