Skip to content

Commit efcdbd0

Browse files
artagnonaaryanshukla
authored andcommitted
MathExtras: add overflow query for signed-div (llvm#97901)
5221634 (Do not trigger UB during AffineExpr parsing) noticed that divideCeilSigned and divideFloorSigned would overflow when Numerator = INT_MIN, and Denominator = -1. This observation has already been made by DynamicAPInt, and it has code to check this. To avoid checks in multiple callers, centralize this query in MathExtras, and change divideCeilSigned/divideFloorSigned to assert on overflow.
1 parent d103f66 commit efcdbd0

File tree

4 files changed

+30
-23
lines changed

4 files changed

+30
-23
lines changed

llvm/include/llvm/ADT/DynamicAPInt.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,6 @@ LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt dynamicAPIntFromInt64(int64_t X) {
241241
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt mod(const DynamicAPInt &LHS,
242242
const DynamicAPInt &RHS);
243243

244-
namespace detail {
245-
// Division overflows only when trying to negate the minimal signed value.
246-
LLVM_ATTRIBUTE_ALWAYS_INLINE bool divWouldOverflow(int64_t X, int64_t Y) {
247-
return X == std::numeric_limits<int64_t>::min() && Y == -1;
248-
}
249-
} // namespace detail
250-
251244
/// We define the operations here in the header to facilitate inlining.
252245

253246
/// ---------------------------------------------------------------------------
@@ -348,7 +341,7 @@ LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt
348341
DynamicAPInt::operator/(const DynamicAPInt &O) const {
349342
if (LLVM_LIKELY(isSmall() && O.isSmall())) {
350343
// Division overflows only occur when negating the minimal possible value.
351-
if (LLVM_UNLIKELY(detail::divWouldOverflow(getSmall(), O.getSmall())))
344+
if (LLVM_UNLIKELY(divideSignedWouldOverflow(getSmall(), O.getSmall())))
352345
return -*this;
353346
return DynamicAPInt(getSmall() / O.getSmall());
354347
}
@@ -363,7 +356,8 @@ LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt abs(const DynamicAPInt &X) {
363356
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt ceilDiv(const DynamicAPInt &LHS,
364357
const DynamicAPInt &RHS) {
365358
if (LLVM_LIKELY(LHS.isSmall() && RHS.isSmall())) {
366-
if (LLVM_UNLIKELY(detail::divWouldOverflow(LHS.getSmall(), RHS.getSmall())))
359+
if (LLVM_UNLIKELY(
360+
divideSignedWouldOverflow(LHS.getSmall(), RHS.getSmall())))
367361
return -LHS;
368362
return DynamicAPInt(divideCeilSigned(LHS.getSmall(), RHS.getSmall()));
369363
}
@@ -373,7 +367,8 @@ LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt ceilDiv(const DynamicAPInt &LHS,
373367
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt floorDiv(const DynamicAPInt &LHS,
374368
const DynamicAPInt &RHS) {
375369
if (LLVM_LIKELY(LHS.isSmall() && RHS.isSmall())) {
376-
if (LLVM_UNLIKELY(detail::divWouldOverflow(LHS.getSmall(), RHS.getSmall())))
370+
if (LLVM_UNLIKELY(
371+
divideSignedWouldOverflow(LHS.getSmall(), RHS.getSmall())))
377372
return -LHS;
378373
return DynamicAPInt(divideFloorSigned(LHS.getSmall(), RHS.getSmall()));
379374
}
@@ -483,7 +478,7 @@ LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt &
483478
DynamicAPInt::operator/=(const DynamicAPInt &O) {
484479
if (LLVM_LIKELY(isSmall() && O.isSmall())) {
485480
// Division overflows only occur when negating the minimal possible value.
486-
if (LLVM_UNLIKELY(detail::divWouldOverflow(getSmall(), O.getSmall())))
481+
if (LLVM_UNLIKELY(divideSignedWouldOverflow(getSmall(), O.getSmall())))
487482
return *this = -*this;
488483
getSmall() /= O.getSmall();
489484
return *this;

llvm/include/llvm/Support/MathExtras.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,20 @@ constexpr uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
413413
return (Numerator - Bias) / Denominator + Bias;
414414
}
415415

416+
// Check whether divideCeilSigned or divideFloorSigned would overflow. This
417+
// happens only when Numerator = INT_MIN and Denominator = -1.
418+
template <typename U, typename V>
419+
constexpr bool divideSignedWouldOverflow(U Numerator, V Denominator) {
420+
return Numerator == std::numeric_limits<U>::min() && Denominator == -1;
421+
}
422+
416423
/// Returns the integer ceil(Numerator / Denominator). Signed version.
417-
/// Guaranteed to never overflow, unless Numerator is INT64_MIN and Denominator
418-
/// is -1.
424+
/// Overflow is explicitly forbidden with an assert.
419425
template <typename U, typename V, typename T = common_sint<U, V>>
420426
constexpr T divideCeilSigned(U Numerator, V Denominator) {
421427
assert(Denominator && "Division by zero");
428+
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
429+
"Divide would overflow");
422430
if (!Numerator)
423431
return 0;
424432
// C's integer division rounds towards 0.
@@ -429,11 +437,12 @@ constexpr T divideCeilSigned(U Numerator, V Denominator) {
429437
}
430438

431439
/// Returns the integer floor(Numerator / Denominator). Signed version.
432-
/// Guaranteed to never overflow, unless Numerator is INT64_MIN and Denominator
433-
/// is -1.
440+
/// Overflow is explicitly forbidden with an assert.
434441
template <typename U, typename V, typename T = common_sint<U, V>>
435442
constexpr T divideFloorSigned(U Numerator, V Denominator) {
436443
assert(Denominator && "Division by zero");
444+
assert(!divideSignedWouldOverflow(Numerator, Denominator) &&
445+
"Divide would overflow");
437446
if (!Numerator)
438447
return 0;
439448
// C's integer division rounds towards 0.

llvm/unittests/Support/MathExtrasTest.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,12 @@ TEST(MathExtras, DivideCeil) {
523523
std::numeric_limits<int64_t>::min() / 2 + 1);
524524
EXPECT_EQ(divideCeilSigned(std::numeric_limits<int64_t>::min(), 1),
525525
std::numeric_limits<int64_t>::min());
526+
527+
// Overflow.
528+
EXPECT_TRUE(
529+
divideSignedWouldOverflow(std::numeric_limits<int8_t>::min(), -1));
530+
EXPECT_TRUE(
531+
divideSignedWouldOverflow(std::numeric_limits<int64_t>::min(), -1));
526532
}
527533

528534
TEST(MathExtras, DivideFloorSigned) {
@@ -544,6 +550,8 @@ TEST(MathExtras, DivideFloorSigned) {
544550
std::numeric_limits<int64_t>::min() / 2);
545551
EXPECT_EQ(divideFloorSigned(std::numeric_limits<int64_t>::min(), 1),
546552
std::numeric_limits<int64_t>::min());
553+
554+
// Same overflow condition, divideSignedWouldOverflow, applies.
547555
}
548556

549557
TEST(MathExtras, Mod) {

mlir/lib/IR/AffineExpr.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ using namespace mlir::detail;
2626

2727
using llvm::divideCeilSigned;
2828
using llvm::divideFloorSigned;
29+
using llvm::divideSignedWouldOverflow;
2930
using llvm::mod;
3031

3132
MLIRContext *AffineExpr::getContext() const { return expr->context; }
@@ -859,11 +860,8 @@ static AffineExpr simplifyFloorDiv(AffineExpr lhs, AffineExpr rhs) {
859860
return nullptr;
860861

861862
if (lhsConst) {
862-
// divideFloorSigned can only overflow in this case:
863-
if (lhsConst.getValue() == std::numeric_limits<int64_t>::min() &&
864-
rhsConst.getValue() == -1) {
863+
if (divideSignedWouldOverflow(lhsConst.getValue(), rhsConst.getValue()))
865864
return nullptr;
866-
}
867865
return getAffineConstantExpr(
868866
divideFloorSigned(lhsConst.getValue(), rhsConst.getValue()),
869867
lhs.getContext());
@@ -921,11 +919,8 @@ static AffineExpr simplifyCeilDiv(AffineExpr lhs, AffineExpr rhs) {
921919
return nullptr;
922920

923921
if (lhsConst) {
924-
// divideCeilSigned can only overflow in this case:
925-
if (lhsConst.getValue() == std::numeric_limits<int64_t>::min() &&
926-
rhsConst.getValue() == -1) {
922+
if (divideSignedWouldOverflow(lhsConst.getValue(), rhsConst.getValue()))
927923
return nullptr;
928-
}
929924
return getAffineConstantExpr(
930925
divideCeilSigned(lhsConst.getValue(), rhsConst.getValue()),
931926
lhs.getContext());

0 commit comments

Comments
 (0)