[Mlir-commits] [mlir] [mlir][Index] Fix INT_MIN overflow in CeilDivSOp constant folding (PR #223127)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sat Sep 12 00:08:51 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-index

Author: Cheng-Chieh Liu (jay87406)

<details>
<summary>Changes</summary>

Fix a silent overflow in `calculateCeilDivS` caused by negating `INT_MIN` via `-(-n).sdiv(m)`, which wraps back and yields incorrect signs/magnitudes (e.g., `ceildivs(INT_MIN, 2)` folding to `4611686018427387904` instead of `-4611686018427387904`).

This replaces the negation logic with `sdiv_ov` and `sadd_ov`, matching `arith::CeilDivSIOp::fold`. It now safely folds `INT_MIN` when representable and returns `std::nullopt` on true overflows (like `INT_MIN / -1`).

Updated `ceildivs_edge` since the previous test for 32-bit truncated `INT32_MIN` only passed due to identical overflow behavior. Added `ceildivs_intmin64` to explicitly cover the fixed scenario.

Verified locally with `ninja mlir-opt && llvm-lit mlir/test/Dialect/Index/`.

---
Full diff: https://github.com/llvm/llvm-project/pull/223127.diff


2 Files Affected:

- (modified) mlir/lib/Dialect/Index/IR/IndexOps.cpp (+16-13) 
- (modified) mlir/test/Dialect/Index/index-canonicalize.mlir (+20-2) 


``````````diff
diff --git a/mlir/lib/Dialect/Index/IR/IndexOps.cpp b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
index 2b1baa8b44643..92f68bade1ee8 100644
--- a/mlir/lib/Dialect/Index/IR/IndexOps.cpp
+++ b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
@@ -245,8 +245,9 @@ 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)` via `sdiv_ov`/`sadd_ov` so that neither operand
+/// is negated, letting `MININT` fold correctly instead of silently
+/// overflowing (mirrors `arith::CeilDivSIOp::fold`).
 static std::optional<APInt> calculateCeilDivS(const APInt &n, const APInt &m) {
   // Don't fold division by zero.
   if (m.isZero())
@@ -255,17 +256,19 @@ static std::optional<APInt> calculateCeilDivS(const APInt &n, const APInt &m) {
   if (n.isZero())
     return n;
 
-  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;
+  bool overflow = false;
+  APInt quotient = n.sdiv_ov(m, overflow);
+  if (overflow) // MININT / -1, not representable.
+    return std::nullopt;
+  // sdiv already rounds towards the ceiling for a negative quotient; a
+  // positive, inexact quotient needs a +1 correction.
+  if (n.isNegative() != m.isNegative() || quotient * m == n)
+    return quotient;
+
+  bool addOverflow = false;
+  APInt result = quotient.sadd_ov(APInt(n.getBitWidth(), 1, /*isSigned=*/true),
+                                   addOverflow);
+  return addOverflow ? std::optional<APInt>() : result;
 }
 
 OpFoldResult CeilDivSOp::fold(FoldAdaptor adaptor) {
diff --git a/mlir/test/Dialect/Index/index-canonicalize.mlir b/mlir/test/Dialect/Index/index-canonicalize.mlir
index 45da6ea57d796..f161969584495 100644
--- a/mlir/test/Dialect/Index/index-canonicalize.mlir
+++ b/mlir/test/Dialect/Index/index-canonicalize.mlir
@@ -165,8 +165,8 @@ func.func @ceildivs_edge() -> (index, index) {
   %cIntMin = index.constant -2147483648
   %cIntMax = index.constant 2147483647
 
-  // The result is 0 on 32-bit.
-  // CHECK-DAG: %[[A:.*]] = index.constant 2147483648
+  // `INT_MIN / -1` overflows on a 32-bit index, so this must not fold.
+  // CHECK-DAG: %[[A:.*]] = index.ceildivs
   %0 = index.ceildivs %cIntMin, %cn1
 
   // CHECK-DAG: %[[B:.*]] = index.constant -2147483647
@@ -176,6 +176,24 @@ func.func @ceildivs_edge() -> (index, index) {
   return %0, %1 : index, index
 }
 
+// CHECK-LABEL: @ceildivs_intmin64
+func.func @ceildivs_intmin64() -> (index, index) {
+  %cIntMin = index.constant -9223372036854775808
+  %cn1 = index.constant -1
+  %c2 = index.constant 2
+
+  // `INT64_MIN / -1` is not representable, so this must not fold.
+  // CHECK-DAG: %[[A:.*]] = index.ceildivs
+  %0 = index.ceildivs %cIntMin, %cn1
+
+  // Must fold to the correct negative result, not a sign-flipped positive one.
+  // CHECK-DAG: %[[B:.*]] = index.constant -4611686018427387904
+  %1 = index.ceildivs %cIntMin, %c2
+
+  // CHECK: return %[[A]], %[[B]]
+  return %0, %1 : index, index
+}
+
 // CHECK-LABEL: @ceildivu
 func.func @ceildivu() -> index {
   %0 = index.constant 0x200000001

``````````

</details>


https://github.com/llvm/llvm-project/pull/223127


More information about the Mlir-commits mailing list