[Mlir-commits] [mlir] [mlir] Compute ceildivs consistently for INT_MIN operands (PR #215696)

Hung Kuan Tseng llvmlistbot at llvm.org
Wed Aug 12 07:46:39 PDT 2026


https://github.com/Tim096 updated https://github.com/llvm/llvm-project/pull/215696

>From 3fb0db2cdf38f3df7f7a73af43f9e8021f2645d8 Mon Sep 17 00:00:00 2001
From: Hung-Kuan Tseng <tseng.tim096 at gmail.com>
Date: Wed, 12 Aug 2026 06:26:36 +0800
Subject: [PATCH 1/3] [mlir][index] Fold and lower ceildivs without negating an
 operand

`index.ceildivs` computes the ceiling as `-(-n / m)` when the operands
have different signs, both in `calculateCeilDivS` and in
`ConvertIndexCeilDivS`. Negating `INT_MIN` wraps, so both compute a
positive result for `ceildivs(INT_MIN, [positive number])`.

The folder never returns that value, because `foldBinaryOpChecked`
requires the 32-bit and the 64-bit result to agree and they do not: for
`index.ceildivs(-2147483648, 7)` the 32-bit computation gives
`306783378` and the 64-bit one `-306783378`, so it does not fold at all.
The lowering has no such check, and
`-convert-index-to-llvm=index-bitwidth=32` emits the wrapped
computation.

Compute the ceiling without negating either operand instead. sdiv
truncates towards zero, which already rounds up whenever the exact
quotient is negative, so the only correction needed is +1 when the
operands share a sign and the division is inexact. The folder gets that
from `APIntOps::RoundingSDiv` with `Rounding::UP`; the lowering emits
the same shape, which is what ExpandOps.cpp already uses to expand
`arith.ceildivsi`.

`ceildivs(INT_MIN, -1)` no longer folds. Its exact result, 2^31, is not
representable on 32-bit; the old computation passed the consistency
check there only because the correction wrapped back to `INT_MIN`, and
the lowering emits `sdiv INT_MIN, -1`, which is poison.
`arith.ceildivsi` does not fold this case either.

That case is rejected with the bit test `sdiv_ov` itself performs rather
than by calling `sdiv_ov` and discarding its quotient, because
`RoundingSDiv` already divides internally and the fold has no reason to
divide twice.
---
 .../Conversion/IndexToLLVM/IndexToLLVM.cpp    | 48 ++++++++-----------
 mlir/lib/Dialect/Index/IR/IndexOps.cpp        | 23 +++------
 .../Conversion/IndexToLLVM/index-to-llvm.mlir | 44 ++++++++++-------
 .../Dialect/Index/index-canonicalize.mlir     | 39 ++++++++++++---
 4 files changed, 87 insertions(+), 67 deletions(-)

diff --git a/mlir/lib/Conversion/IndexToLLVM/IndexToLLVM.cpp b/mlir/lib/Conversion/IndexToLLVM/IndexToLLVM.cpp
index ba7d2b6fb83aa..00c9644a6fe4f 100644
--- a/mlir/lib/Conversion/IndexToLLVM/IndexToLLVM.cpp
+++ b/mlir/lib/Conversion/IndexToLLVM/IndexToLLVM.cpp
@@ -25,8 +25,8 @@ namespace {
 // ConvertIndexCeilDivS
 //===----------------------------------------------------------------------===//
 
-/// Convert `ceildivs(n, m)` into `x = m > 0 ? -1 : 1` and then
-/// `n*m > 0 ? (n+x)/m + 1 : -(-n/m)`.
+/// Convert `ceildivs(n, m)` into `z = n / m` and then
+/// `z*m != n && (n < 0) == (m < 0) ? z + 1 : z`.
 struct ConvertIndexCeilDivS : mlir::ConvertOpToLLVMPattern<CeilDivSOp> {
   using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern;
 
@@ -38,33 +38,27 @@ struct ConvertIndexCeilDivS : mlir::ConvertOpToLLVMPattern<CeilDivSOp> {
     Value m = adaptor.getRhs();
     Value zero = LLVM::ConstantOp::create(rewriter, loc, n.getType(), 0);
     Value posOne = LLVM::ConstantOp::create(rewriter, loc, n.getType(), 1);
-    Value negOne = LLVM::ConstantOp::create(rewriter, loc, n.getType(), -1);
-
-    // Compute `x`.
-    Value mPos =
-        LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::sgt, m, zero);
-    Value x = LLVM::SelectOp::create(rewriter, loc, mPos, negOne, posOne);
 
-    // Compute the positive result.
-    Value nPlusX = LLVM::AddOp::create(rewriter, loc, n, x);
-    Value nPlusXDivM = LLVM::SDivOp::create(rewriter, loc, nPlusX, m);
-    Value posRes = LLVM::AddOp::create(rewriter, loc, nPlusXDivM, posOne);
-
-    // Compute the negative result.
-    Value negN = LLVM::SubOp::create(rewriter, loc, zero, n);
-    Value negNDivM = LLVM::SDivOp::create(rewriter, loc, negN, m);
-    Value negRes = LLVM::SubOp::create(rewriter, loc, zero, negNDivM);
-
-    // Pick the positive result if `n` and `m` have the same sign and `n` is
-    // non-zero, i.e. `(n > 0) == (m > 0) && n != 0`.
-    Value nPos =
-        LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::sgt, n, zero);
+    // Compute the truncated quotient. sdiv rounds towards zero, so it already
+    // rounds up whenever the exact quotient is negative.
+    Value quotient = LLVM::SDivOp::create(rewriter, loc, n, m);
+    Value quotientPlusOne =
+        LLVM::AddOp::create(rewriter, loc, quotient, posOne);
+
+    // Correct the quotient by one if the division is inexact and the exact
+    // quotient is positive, i.e. if `n` and `m` have the same sign.
+    Value product = LLVM::MulOp::create(rewriter, loc, quotient, m);
+    Value inexact = LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::ne,
+                                         n, product);
+    Value nNeg =
+        LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::slt, n, zero);
+    Value mNeg =
+        LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::slt, m, zero);
     Value sameSign = LLVM::ICmpOp::create(rewriter, loc,
-                                          LLVM::ICmpPredicate::eq, nPos, mPos);
-    Value nNonZero =
-        LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::ne, n, zero);
-    Value cmp = LLVM::AndOp::create(rewriter, loc, sameSign, nNonZero);
-    rewriter.replaceOpWithNewOp<LLVM::SelectOp>(op, cmp, posRes, negRes);
+                                          LLVM::ICmpPredicate::eq, nNeg, mNeg);
+    Value cmp = LLVM::AndOp::create(rewriter, loc, inexact, sameSign);
+    rewriter.replaceOpWithNewOp<LLVM::SelectOp>(op, cmp, quotientPlusOne,
+                                                quotient);
     return success();
   }
 };
diff --git a/mlir/lib/Dialect/Index/IR/IndexOps.cpp b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
index 2b1baa8b44643..d522c970db62b 100644
--- a/mlir/lib/Dialect/Index/IR/IndexOps.cpp
+++ b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
@@ -245,27 +245,18 @@ OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
 // CeilDivSOp
 //===----------------------------------------------------------------------===//
 
-/// Compute `ceildivs(n, m)` as `x = m > 0 ? -1 : 1` and then
-/// `n*m > 0 ? (n+x)/m + 1 : -(-n/m)`.
+/// Compute `ceildivs(n, m)` by rounding the quotient of `n / m` towards
+/// positive infinity.
 static std::optional<APInt> calculateCeilDivS(const APInt &n, const APInt &m) {
   // Don't fold division by zero.
   if (m.isZero())
     return std::nullopt;
-  // Short-circuit the zero case.
-  if (n.isZero())
-    return n;
+  // Don't fold `INT_MIN / -1`, the one quotient that is not representable.
+  // Neither operand is negated, so every other `INT_MIN` dividend is fine.
+  if (n.isMinSignedValue() && m.isAllOnes())
+    return std::nullopt;
 
-  bool mGtZ = m.sgt(0);
-  if (n.sgt(0) != mGtZ) {
-    // If the operands have different signs, compute the negative result. Signed
-    // division overflow is not possible, since if `m == -1`, `n` can be at most
-    // `INT_MAX`, and `-INT_MAX != INT_MIN` in two's complement.
-    return -(-n).sdiv(m);
-  }
-  // Otherwise, compute the positive result. Signed division overflow is not
-  // possible since if `m == -1`, `x` will be `1`.
-  int64_t x = mGtZ ? -1 : 1;
-  return (n + x).sdiv(m) + 1;
+  return llvm::APIntOps::RoundingSDiv(n, m, APInt::Rounding::UP);
 }
 
 OpFoldResult CeilDivSOp::fold(FoldAdaptor adaptor) {
diff --git a/mlir/test/Conversion/IndexToLLVM/index-to-llvm.mlir b/mlir/test/Conversion/IndexToLLVM/index-to-llvm.mlir
index 007929ed677fa..15797bfaa411d 100644
--- a/mlir/test/Conversion/IndexToLLVM/index-to-llvm.mlir
+++ b/mlir/test/Conversion/IndexToLLVM/index-to-llvm.mlir
@@ -54,25 +54,18 @@ func.func @ceildivs(%n: index, %m: index) -> index {
   // CHECK-DAG: %[[N:.*]] = builtin.unrealized_conversion_cast %[[NI]]
   // CHECK-DAG: %[[M:.*]] = builtin.unrealized_conversion_cast %[[MI]]
   // CHECK: %[[ZERO:.*]] = llvm.mlir.constant(0 :
-  // CHECK: %[[POS_ONE:.*]] = llvm.mlir.constant(1 :
-  // CHECK: %[[NEG_ONE:.*]] = llvm.mlir.constant(-1 :
-
-  // CHECK: %[[M_POS:.*]] = llvm.icmp "sgt" %[[M]], %[[ZERO]]
-  // CHECK: %[[X:.*]] = llvm.select %[[M_POS]], %[[NEG_ONE]], %[[POS_ONE]]
-
-  // CHECK: %[[N_PLUS_X:.*]] = llvm.add %[[N]], %[[X]]
-  // CHECK: %[[N_PLUS_X_DIV_M:.*]] = llvm.sdiv %[[N_PLUS_X]], %[[M]]
-  // CHECK: %[[POS_RES:.*]] = llvm.add %[[N_PLUS_X_DIV_M]], %[[POS_ONE]]
+  // CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 :
 
-  // CHECK: %[[NEG_N:.*]] = llvm.sub %[[ZERO]], %[[N]]
-  // CHECK: %[[NEG_N_DIV_M:.*]] = llvm.sdiv %[[NEG_N]], %[[M]]
-  // CHECK: %[[NEG_RES:.*]] = llvm.sub %[[ZERO]], %[[NEG_N_DIV_M]]
+  // CHECK: %[[QUOTIENT:.*]] = llvm.sdiv %[[N]], %[[M]]
+  // CHECK: %[[QUOTIENT_PLUS_ONE:.*]] = llvm.add %[[QUOTIENT]], %[[ONE]]
 
-  // CHECK: %[[N_POS:.*]] = llvm.icmp "sgt" %[[N]], %[[ZERO]]
-  // CHECK: %[[SAME_SIGN:.*]] = llvm.icmp "eq" %[[N_POS]], %[[M_POS]]
-  // CHECK: %[[N_NON_ZERO:.*]] = llvm.icmp "ne" %[[N]], %[[ZERO]]
-  // CHECK: %[[CMP:.*]] = llvm.and %[[SAME_SIGN]], %[[N_NON_ZERO]]
-  // CHECK: %[[RESULT:.*]] = llvm.select %[[CMP]], %[[POS_RES]], %[[NEG_RES]]
+  // CHECK: %[[PRODUCT:.*]] = llvm.mul %[[QUOTIENT]], %[[M]]
+  // CHECK: %[[INEXACT:.*]] = llvm.icmp "ne" %[[N]], %[[PRODUCT]]
+  // CHECK: %[[N_NEG:.*]] = llvm.icmp "slt" %[[N]], %[[ZERO]]
+  // CHECK: %[[M_NEG:.*]] = llvm.icmp "slt" %[[M]], %[[ZERO]]
+  // CHECK: %[[SAME_SIGN:.*]] = llvm.icmp "eq" %[[N_NEG]], %[[M_NEG]]
+  // CHECK: %[[CMP:.*]] = llvm.and %[[INEXACT]], %[[SAME_SIGN]]
+  // CHECK: %[[RESULT:.*]] = llvm.select %[[CMP]], %[[QUOTIENT_PLUS_ONE]], %[[QUOTIENT]]
   %result = index.ceildivs %n, %m
 
   // CHECK: %[[RESULTI:.*]] = builtin.unrealized_conversion_cast %[[RESULT]]
@@ -80,6 +73,23 @@ func.func @ceildivs(%n: index, %m: index) -> index {
   return %result : index
 }
 
+// INDEX32-LABEL: @ceildivs_intmin_dividend
+// INDEX64-LABEL: @ceildivs_intmin_dividend
+func.func @ceildivs_intmin_dividend() -> index {
+  %n = index.constant -2147483648
+  %m = index.constant 7
+
+  // The conversion folds an op before it looks for a pattern, and the fold
+  // needs the 32-bit and the 64-bit result to agree, which they now do. They
+  // did not while the dividend was negated, so the sequence above ran instead
+  // and computed 306783378 on 32-bit.
+  // INDEX32: llvm.mlir.constant(-306783378 : i32) : i32
+  // INDEX64: llvm.mlir.constant(-306783378 : i64) : i64
+  %result = index.ceildivs %n, %m
+
+  return %result : index
+}
+
 // CHECK-LABEL: @ceildivu
 // CHECK-SAME: %[[NI:.*]]: index, %[[MI:.*]]: index
 func.func @ceildivu(%n: index, %m: index) -> index {
diff --git a/mlir/test/Dialect/Index/index-canonicalize.mlir b/mlir/test/Dialect/Index/index-canonicalize.mlir
index 45da6ea57d796..25b74350ca70a 100644
--- a/mlir/test/Dialect/Index/index-canonicalize.mlir
+++ b/mlir/test/Dialect/Index/index-canonicalize.mlir
@@ -150,32 +150,57 @@ func.func @ceildivs() -> (index, index, index) {
 }
 
 // CHECK-LABEL: @ceildivs_neg
-func.func @ceildivs_neg() -> index {
+func.func @ceildivs_neg() -> (index, index) {
   %c5 = index.constant -5
   %c2 = index.constant 2
-  // CHECK: %[[A:.*]] = index.constant -2
+  %cn2 = index.constant -2
+
+  // CHECK-DAG: %[[A:.*]] = index.constant -2
   %0 = index.ceildivs %c5, %c2
-  // CHECK: return %[[A]]
-  return %0 : index
+
+  // Both operands are negative, so the exact quotient is positive and the
+  // truncated one has to be corrected.
+  // CHECK-DAG: %[[B:.*]] = index.constant 3
+  %1 = index.ceildivs %c5, %cn2
+
+  // CHECK: return %[[A]], %[[B]]
+  return %0, %1 : index, index
 }
 
 // CHECK-LABEL: @ceildivs_edge
 func.func @ceildivs_edge() -> (index, index) {
+  // CHECK-DAG: %[[B:.*]] = index.constant -2147483647
+  // CHECK-DAG: %[[NEG_ONE:.*]] = index.constant -1
+  // CHECK-DAG: %[[INT_MIN:.*]] = index.constant -2147483648
   %cn1 = index.constant -1
   %cIntMin = index.constant -2147483648
   %cIntMax = index.constant 2147483647
 
-  // The result is 0 on 32-bit.
-  // CHECK-DAG: %[[A:.*]] = index.constant 2147483648
+  // The result, 2147483648, is not representable on 32-bit, so this does not
+  // fold on either bitwidth.
+  // CHECK: %[[A:.*]] = index.ceildivs %[[INT_MIN]], %[[NEG_ONE]]
   %0 = index.ceildivs %cIntMin, %cn1
 
-  // CHECK-DAG: %[[B:.*]] = index.constant -2147483647
   %1 = index.ceildivs %cIntMax, %cn1
 
   // CHECK: return %[[A]], %[[B]]
   return %0, %1 : index, index
 }
 
+// CHECK-LABEL: @ceildivs_intmin_dividend
+func.func @ceildivs_intmin_dividend() -> index {
+  %c7 = index.constant 7
+  %cIntMin = index.constant -2147483648
+
+  // Negating the dividend wrapped on 32-bit and gave a positive result there,
+  // which disagreed with the 64-bit one, so this used not to fold at all.
+  // CHECK: %[[A:.*]] = index.constant -306783378
+  %0 = index.ceildivs %cIntMin, %c7
+
+  // CHECK: return %[[A]]
+  return %0 : index
+}
+
 // CHECK-LABEL: @ceildivu
 func.func @ceildivu() -> index {
   %0 = index.constant 0x200000001

>From 04c6ba64bfaa4f61adafb08a4c7df46645afed65 Mon Sep 17 00:00:00 2001
From: Hung-Kuan Tseng <tseng.tim096 at gmail.com>
Date: Wed, 12 Aug 2026 00:51:34 +0800
Subject: [PATCH 2/3] [mlir] Drop the ceildivsi INT_MIN workarounds in range
 inference

`inferCeilDivS` negates the quotient of `MININT / [positive number]`,
which is not signed ceiling division. It was added so that the analysis
agreed with what the op actually computed: the expansion at the time was
`-(-a / b)`, and negating `MININT` is a noop, so that expression returned
a positive value (#116284, fixing #115293). A second workaround unions in
the range of `[MININT + 1, smax]` whenever the dividend range starts at
`MININT`, to cover the discontinuity the first one introduces (#121062).

#133774 replaced that expansion with the mathematical ceiling in April
2025, and the workarounds were left behind. With the previous commit,
nothing else in tree reports a positive value there; on main, for
`ceildivsi(INT64_MIN, 1189465982)`:

  -arith-expand -canonicalize  folds it to -7754212542
  -int-range-optimizations     proves it equal to 7754212542

Drop both workarounds. What is left has the same shape as
`inferFloorDivS`: `inferDivSRange` only evaluates at the range endpoints,
which bounds the result because ceildivsi is monotonic in each operand
once the divisor cannot change sign -- and `inferDivSRange` already gives
up unless it cannot. With the negation gone there is no discontinuity for
the second workaround to patch up, which the existing
`@ceil_divsi_full_range` test pins: it still does not fold.
---
 .../Interfaces/Utils/InferIntRangeCommon.cpp  | 19 +-------------
 .../Dialect/Arith/int-range-interface.mlir    | 25 ++++++++++++++++---
 2 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp b/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
index c9f49fda726e7..f3a9db75bb406 100644
--- a/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
+++ b/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
@@ -376,26 +376,9 @@ mlir::intrange::inferCeilDivS(ArrayRef<ConstantIntRanges> argRanges) {
           result.sadd_ov(APInt(result.getBitWidth(), 1), overflowed);
       return overflowed ? std::optional<APInt>() : corrected;
     }
-    // Special case where the usual implementation of ceilDiv causes
-    // INT_MIN / [positive number] to be positive. This doesn't match the
-    // definition of signed ceiling division mathematically, but it prevents
-    // inconsistent constant-folding results. This arises because (-int_min) is
-    // still negative, so -(-int_min / b) is -(int_min / b), which is
-    // positive See #115293.
-    if (lhs.isMinSignedValue() && rhs.sgt(1)) {
-      return -result;
-    }
     return result;
   };
-  ConstantIntRanges result = inferDivSRange(lhs, rhs, ceilDivSIFix);
-  if (lhs.smin().isMinSignedValue() && lhs.smax().sgt(lhs.smin())) {
-    // If lhs range includes INT_MIN and lhs is not a single value, we can
-    // suddenly wrap to positive val, skipping entire negative range, add
-    // [INT_MIN + 1, smax()] range to the result to handle this.
-    auto newLhs = ConstantIntRanges::fromSigned(lhs.smin() + 1, lhs.smax());
-    result = result.rangeUnion(inferDivSRange(newLhs, rhs, ceilDivSIFix));
-  }
-  return result;
+  return inferDivSRange(lhs, rhs, ceilDivSIFix);
 }
 
 ConstantIntRanges
diff --git a/mlir/test/Dialect/Arith/int-range-interface.mlir b/mlir/test/Dialect/Arith/int-range-interface.mlir
index dd8240299ef7e..cff3f5d3a632c 100644
--- a/mlir/test/Dialect/Arith/int-range-interface.mlir
+++ b/mlir/test/Dialect/Arith/int-range-interface.mlir
@@ -272,19 +272,38 @@ func.func @ceil_divsi_full_range(%6: index) -> index {
   return %55 : index
 }
 
-// CHECK-LABEL: func @ceil_divsi_intmin_bug_115293
+// ceildivsi(INT_MIN, x > 1) is negative, as the mathematical definition of
+// signed ceiling division requires. See #115293, where it was inferred to be
+// positive to match a folder that gave up on INT_MIN operands.
+// CHECK-LABEL: func @ceil_divsi_intmin
 // CHECK: %[[ret:.*]] = arith.constant true
 // CHECK: return %[[ret]]
-func.func @ceil_divsi_intmin_bug_115293() -> i1 {
+func.func @ceil_divsi_intmin() -> i1 {
     %intMin_i64 = test.with_bounds { smin = -9223372036854775808 : si64, smax = -9223372036854775808 : si64, umin = 9223372036854775808 : ui64, umax = 9223372036854775808 : ui64 } : i64
     %denom_i64 = test.with_bounds { smin = 1189465982 : si64, smax = 1189465982 : si64, umin = 1189465982 : ui64, umax = 1189465982 : ui64 } : i64
-    %res_i64 = test.with_bounds { smin = 7754212542 : si64, smax = 7754212542 : si64, umin = 7754212542 : ui64, umax = 7754212542 : ui64 }  : i64
+    %res_i64 = test.with_bounds { smin = -7754212542 : si64, smax = -7754212542 : si64, umin = 18446744065955339074 : ui64, umax = 18446744065955339074 : ui64 }  : i64
 
     %0 = arith.ceildivsi %intMin_i64, %denom_i64 : i64
     %1 = arith.cmpi eq, %0, %res_i64 : i64
     func.return %1 : i1
 }
 
+// A dividend range that starts at INT_MIN needs no special case either:
+// ceildivsi is monotonic in its dividend, so the endpoints bound it. Here every
+// quotient is negative, which #115293 could not conclude.
+// CHECK-LABEL: func @ceil_divsi_intmin_dividend_range
+// CHECK: %[[ret:.*]] = arith.constant true
+// CHECK: return %[[ret]]
+func.func @ceil_divsi_intmin_dividend_range() -> i1 {
+    %c0_i64 = arith.constant 0 : i64
+    %c64_i64 = arith.constant 64 : i64
+    %num_i64 = test.with_bounds { smin = -9223372036854775808 : si64, smax = -9223372036854775553 : si64, umin = 9223372036854775808 : ui64, umax = 9223372036854776063 : ui64 } : i64
+
+    %0 = arith.ceildivsi %num_i64, %c64_i64 : i64
+    %1 = arith.cmpi slt, %0, %c0_i64 : i64
+    func.return %1 : i1
+}
+
 // CHECK-LABEL: func @floor_divsi
 // CHECK: %[[true:.*]] = arith.constant true
 // CHECK: return %[[true]]

>From 7a0d9f0ea804f99d93896f728e7352c706af5089 Mon Sep 17 00:00:00 2001
From: Hung-Kuan Tseng <tseng.tim096 at gmail.com>
Date: Wed, 12 Aug 2026 06:29:41 +0800
Subject: [PATCH 3/3] [mlir][index] Add a 32-bit regression for ceildivs range
 inference

`index.ceildivs` infers its result range twice, once on the 64-bit
operands and once on their 32-bit truncations, and unions the two. A
dividend range that only starts at `INT_MIN` after that truncation
therefore reaches the workarounds that the previous commit removed, and
no `arith` test covers it.
---
 .../Dialect/Index/int-range-inference.mlir     | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/mlir/test/Dialect/Index/int-range-inference.mlir b/mlir/test/Dialect/Index/int-range-inference.mlir
index 951624d573a64..74c4b93a6e217 100644
--- a/mlir/test/Dialect/Index/int-range-inference.mlir
+++ b/mlir/test/Dialect/Index/int-range-inference.mlir
@@ -64,3 +64,21 @@ func.func @add_big(%arg0 : index) -> i1 {
   %3 = index.cmp uge(%1, %cmin)
   func.return %3 : i1
 }
+
+// The dividend range starts at INT_MIN once truncated to 32 bits. The 32-bit
+// inference used to negate the quotient there, which put a positive value in
+// the result range and left the comparison unresolved.
+// CHECK-LABEL: func @ceildivs_intmin_dividend
+// CHECK: %[[true:.*]] = index.bool.constant true
+// CHECK: return %[[true]]
+func.func @ceildivs_intmin_dividend(%arg0 : index) -> i1 {
+  %c0 = index.constant 0
+  %c7 = index.constant 7
+  %cmin = index.constant -2147483648
+  %cmax = index.constant -2147483393
+  %0 = index.maxs %arg0, %cmin
+  %1 = index.mins %0, %cmax
+  %2 = index.ceildivs %1, %c7
+  %3 = index.cmp slt(%2, %c0)
+  func.return %3 : i1
+}



More information about the Mlir-commits mailing list