[Mlir-commits] [mlir] [mlir][Index] Fix INT_MIN overflow in CeilDivSOp constant folding (PR #223127)
Cheng-Chieh Liu
llvmlistbot at llvm.org
Sat Sep 12 02:41:10 PDT 2026
https://github.com/jay87406 updated https://github.com/llvm/llvm-project/pull/223127
>From 9b99b2db7e6b4f3c0e28a42256b04445fe1435b5 Mon Sep 17 00:00:00 2001
From: jay87406 <jay87406 at gmail.com>
Date: Sat, 12 Sep 2026 00:52:40 -0500
Subject: [PATCH 1/2] [mlir][Index] Fix INT_MIN overflow in CeilDivSOp constant
folding
---
mlir/lib/Dialect/Index/IR/IndexOps.cpp | 29 ++++++++++---------
.../Dialect/Index/index-canonicalize.mlir | 22 ++++++++++++--
2 files changed, 36 insertions(+), 15 deletions(-)
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
>From 01adda2572f656b393d5d45507c7934ef086c846 Mon Sep 17 00:00:00 2001
From: Cheng-Chieh Liu <jay87406 at gmail.com>
Date: Sat, 12 Sep 2026 04:32:18 -0500
Subject: [PATCH 2/2] mlir: index: update ceildivs folder comment to use valid
INT_MIN example
---
mlir/lib/Dialect/Index/IR/IndexOps.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Dialect/Index/IR/IndexOps.cpp b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
index 92f68bade1ee8..3b969a6e90085 100644
--- a/mlir/lib/Dialect/Index/IR/IndexOps.cpp
+++ b/mlir/lib/Dialect/Index/IR/IndexOps.cpp
@@ -246,8 +246,11 @@ OpFoldResult DivUOp::fold(FoldAdaptor adaptor) {
//===----------------------------------------------------------------------===//
/// 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`).
+/// is negated. For example, `ceildivs(INT_MIN, 2)` is representable and must
+/// fold to a negative result. Negating `INT_MIN` directly via `-(-n)` (as done
+/// previously) silently overflows back to `INT_MIN`, which incorrectly
+/// yielded a sign-flipped positive value. This mirrors
+/// `arith::CeilDivSIOp::fold`.
static std::optional<APInt> calculateCeilDivS(const APInt &n, const APInt &m) {
// Don't fold division by zero.
if (m.isZero())
@@ -258,7 +261,7 @@ static std::optional<APInt> calculateCeilDivS(const APInt &n, const APInt &m) {
bool overflow = false;
APInt quotient = n.sdiv_ov(m, overflow);
- if (overflow) // MININT / -1, not representable.
+ if (overflow) // The exact quotient is not representable (e.g. INT_MIN / -1).
return std::nullopt;
// sdiv already rounds towards the ceiling for a negative quotient; a
// positive, inexact quotient needs a +1 correction.
More information about the Mlir-commits
mailing list