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

Hung Kuan Tseng llvmlistbot at llvm.org
Fri Aug 14 06:05:39 PDT 2026


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

>From e53103d1871a4044af0e1d483313a402d28a957b 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/4] [mlir][index] Fold and lower ceildivs without negating an
 operand

`index.ceildivs` computes the ceiling as `-(-n / m)` when the operands
have different signs, in `calculateCeilDivS` and in both lowerings,
`ConvertIndexCeilDivS` for LLVM and `ConvertIndexCeilDivSPattern` for
SPIR-V. Negating `INT_MIN` wraps, so all three 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 lowerings have no such check;
`-convert-index-to-llvm=index-bitwidth=32` and
`-convert-index-to-spirv=use-64bit-index=false` both emit the wrapped
computation, which evaluates to `306783378`.

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 lowerings emit
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 ++++++++-----------
 .../Conversion/IndexToSPIRV/IndexToSPIRV.cpp  | 43 +++++++----------
 mlir/lib/Dialect/Index/IR/IndexOps.cpp        | 23 +++------
 .../Conversion/IndexToLLVM/index-to-llvm.mlir | 44 ++++++++++-------
 .../IndexToSPIRV/index-to-spirv.mlir          | 44 ++++++++++-------
 .../Dialect/Index/index-canonicalize.mlir     | 39 ++++++++++++---
 6 files changed, 132 insertions(+), 109 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/Conversion/IndexToSPIRV/IndexToSPIRV.cpp b/mlir/lib/Conversion/IndexToSPIRV/IndexToSPIRV.cpp
index 92526da965157..99368d3cc184d 100644
--- a/mlir/lib/Conversion/IndexToSPIRV/IndexToSPIRV.cpp
+++ b/mlir/lib/Conversion/IndexToSPIRV/IndexToSPIRV.cpp
@@ -100,9 +100,9 @@ struct ConvertIndexConstantOpPattern final : OpConversionPattern<ConstantOp> {
 // ConvertIndexCeilDivS
 //===----------------------------------------------------------------------===//
 
-/// Convert `ceildivs(n, m)` into `x = m > 0 ? -1 : 1` and then
-/// `n*m > 0 ? (n+x)/m + 1 : -(-n/m)`. Formula taken from the equivalent
-/// conversion in IndexToLLVM.
+/// Convert `ceildivs(n, m)` into `z = n / m` and then
+/// `z*m != n && (n < 0) == (m < 0) ? z + 1 : z`. Formula taken from the
+/// equivalent conversion in IndexToLLVM.
 struct ConvertIndexCeilDivSPattern final : OpConversionPattern<CeilDivSOp> {
   using Base::Base;
 
@@ -119,30 +119,23 @@ struct ConvertIndexCeilDivSPattern final : OpConversionPattern<CeilDivSOp> {
                                            IntegerAttr::get(nType, 0));
     Value posOne = spirv::ConstantOp::create(rewriter, loc, nType,
                                              IntegerAttr::get(nType, 1));
-    Value negOne = spirv::ConstantOp::create(rewriter, loc, nType,
-                                             IntegerAttr::get(nType, -1));
 
-    // Compute `x`.
-    Value mPos = spirv::SGreaterThanOp::create(rewriter, loc, m, zero);
-    Value x = spirv::SelectOp::create(rewriter, loc, mPos, negOne, posOne);
+    // Compute the truncated quotient. Signed division rounds towards zero, so
+    // it already rounds up whenever the exact quotient is negative.
+    Value quotient = spirv::SDivOp::create(rewriter, loc, n, m);
+    Value quotientPlusOne =
+        spirv::IAddOp::create(rewriter, loc, quotient, posOne);
 
-    // Compute the positive result.
-    Value nPlusX = spirv::IAddOp::create(rewriter, loc, n, x);
-    Value nPlusXDivM = spirv::SDivOp::create(rewriter, loc, nPlusX, m);
-    Value posRes = spirv::IAddOp::create(rewriter, loc, nPlusXDivM, posOne);
-
-    // Compute the negative result.
-    Value negN = spirv::ISubOp::create(rewriter, loc, zero, n);
-    Value negNDivM = spirv::SDivOp::create(rewriter, loc, negN, m);
-    Value negRes = spirv::ISubOp::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 = spirv::SGreaterThanOp::create(rewriter, loc, n, zero);
-    Value sameSign = spirv::LogicalEqualOp::create(rewriter, loc, nPos, mPos);
-    Value nNonZero = spirv::INotEqualOp::create(rewriter, loc, n, zero);
-    Value cmp = spirv::LogicalAndOp::create(rewriter, loc, sameSign, nNonZero);
-    rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cmp, posRes, negRes);
+    // 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 = spirv::IMulOp::create(rewriter, loc, quotient, m);
+    Value inexact = spirv::INotEqualOp::create(rewriter, loc, n, product);
+    Value nNeg = spirv::SLessThanOp::create(rewriter, loc, n, zero);
+    Value mNeg = spirv::SLessThanOp::create(rewriter, loc, m, zero);
+    Value sameSign = spirv::LogicalEqualOp::create(rewriter, loc, nNeg, mNeg);
+    Value cmp = spirv::LogicalAndOp::create(rewriter, loc, inexact, sameSign);
+    rewriter.replaceOpWithNewOp<spirv::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/Conversion/IndexToSPIRV/index-to-spirv.mlir b/mlir/test/Conversion/IndexToSPIRV/index-to-spirv.mlir
index 2d00adff2e8ba..d9b6508a6233c 100644
--- a/mlir/test/Conversion/IndexToSPIRV/index-to-spirv.mlir
+++ b/mlir/test/Conversion/IndexToSPIRV/index-to-spirv.mlir
@@ -71,25 +71,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:.*]] = spirv.Constant 0
-  // CHECK: %[[POS_ONE:.*]] = spirv.Constant 1
-  // CHECK: %[[NEG_ONE:.*]] = spirv.Constant -1
-
-  // CHECK: %[[M_POS:.*]] = spirv.SGreaterThan %[[M]], %[[ZERO]]
-  // CHECK: %[[X:.*]] = spirv.Select %[[M_POS]], %[[NEG_ONE]], %[[POS_ONE]]
-
-  // CHECK: %[[N_PLUS_X:.*]] = spirv.IAdd %[[N]], %[[X]]
-  // CHECK: %[[N_PLUS_X_DIV_M:.*]] = spirv.SDiv %[[N_PLUS_X]], %[[M]]
-  // CHECK: %[[POS_RES:.*]] = spirv.IAdd %[[N_PLUS_X_DIV_M]], %[[POS_ONE]]
+  // CHECK: %[[ONE:.*]] = spirv.Constant 1
 
-  // CHECK: %[[NEG_N:.*]] = spirv.ISub %[[ZERO]], %[[N]]
-  // CHECK: %[[NEG_N_DIV_M:.*]] = spirv.SDiv %[[NEG_N]], %[[M]]
-  // CHECK: %[[NEG_RES:.*]] = spirv.ISub %[[ZERO]], %[[NEG_N_DIV_M]]
+  // CHECK: %[[QUOTIENT:.*]] = spirv.SDiv %[[N]], %[[M]]
+  // CHECK: %[[QUOTIENT_PLUS_ONE:.*]] = spirv.IAdd %[[QUOTIENT]], %[[ONE]]
 
-  // CHECK: %[[N_POS:.*]] = spirv.SGreaterThan %[[N]], %[[ZERO]]
-  // CHECK: %[[SAME_SIGN:.*]] = spirv.LogicalEqual %[[N_POS]], %[[M_POS]]
-  // CHECK: %[[N_NON_ZERO:.*]] = spirv.INotEqual %[[N]], %[[ZERO]]
-  // CHECK: %[[CMP:.*]] = spirv.LogicalAnd %[[SAME_SIGN]], %[[N_NON_ZERO]]
-  // CHECK: %[[RESULT:.*]] = spirv.Select %[[CMP]], %[[POS_RES]], %[[NEG_RES]]
+  // CHECK: %[[PRODUCT:.*]] = spirv.IMul %[[QUOTIENT]], %[[M]]
+  // CHECK: %[[INEXACT:.*]] = spirv.INotEqual %[[N]], %[[PRODUCT]]
+  // CHECK: %[[N_NEG:.*]] = spirv.SLessThan %[[N]], %[[ZERO]]
+  // CHECK: %[[M_NEG:.*]] = spirv.SLessThan %[[M]], %[[ZERO]]
+  // CHECK: %[[SAME_SIGN:.*]] = spirv.LogicalEqual %[[N_NEG]], %[[M_NEG]]
+  // CHECK: %[[CMP:.*]] = spirv.LogicalAnd %[[INEXACT]], %[[SAME_SIGN]]
+  // CHECK: %[[RESULT:.*]] = spirv.Select %[[CMP]], %[[QUOTIENT_PLUS_ONE]], %[[QUOTIENT]]
   %result = index.ceildivs %n, %m
 
   // %[[RESULTI:.*] = builtin.unrealized_conversion_cast %[[RESULT]]
@@ -97,6 +90,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: spirv.Constant -306783378 : i32
+  // INDEX64: spirv.Constant -306783378 : 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 34131c49288a7f4f55cccb0b177aa1559294f315 Mon Sep 17 00:00:00 2001
From: Hung-Kuan Tseng <tseng.tim096 at gmail.com>
Date: Thu, 13 Aug 2026 19:11:39 +0800
Subject: [PATCH 2/4] [mlir][affine] Expand ceildiv without negating the
 dividend

`expandAffineExpr` lowers affine `ceildiv` as
`a <= 0 ? -(-a / b) : (a - 1) / b + 1`. Negating `INT_MIN` wraps, so for
`INT64_MIN ceildiv 7` the lowered arithmetic evaluates to
`1317624576693539401`, while both the constant folder
(`divideCeilSigned`) and range inference (`intrange::inferCeilDivS`)
give the exact quotient, `-1317624576693539401`. Canonicalizing or
running range optimizations before the lowering therefore changes the
result.

Affine `ceildiv` requires a strictly positive divisor -- the expansion
already relied on that, and rejects a non-positive constant one -- so
the ceiling needs no negation and no sign test:

    a ceildiv b = let q = a / b in a > q * b ? q + 1 : q

`q * b` is `a` minus the remainder, so for a positive `b` the comparison
holds exactly when the division is inexact and the exact quotient is
positive, which is when truncation has to be corrected. This is the same
correction `index.ceildivs` uses, with the divisor's sign known.

`@lowered_affine_ceildiv` in Transforms/canonicalize.mlir is a copy of
what this lowering emits, so it changes with it.
---
 mlir/lib/Dialect/Affine/Utils/Utils.cpp       | 31 ++++++------
 .../AffineToStandard/lower-affine.mlir        | 49 ++++++++++++-------
 mlir/test/Transforms/canonicalize.mlir        | 32 +++++-------
 3 files changed, 58 insertions(+), 54 deletions(-)

diff --git a/mlir/lib/Dialect/Affine/Utils/Utils.cpp b/mlir/lib/Dialect/Affine/Utils/Utils.cpp
index 7043083298615..60de6744b4e67 100644
--- a/mlir/lib/Dialect/Affine/Utils/Utils.cpp
+++ b/mlir/lib/Dialect/Affine/Utils/Utils.cpp
@@ -152,10 +152,15 @@ class AffineApplyExpander
   /// single division operation as
   ///
   ///     a ceildiv b =
-  ///         let negative = a <= 0 in
-  ///         let absolute = negative ? -a : a - 1 in
-  ///         let quotient = absolute / b in
-  ///             negative ? -quotient : quotient + 1
+  ///         let quotient = a / b in
+  ///             a > quotient * b ? quotient + 1 : quotient
+  ///
+  /// Signed division rounds towards zero, so it already rounds up whenever the
+  /// exact quotient is negative. `quotient * b` is `a` minus the remainder, so
+  /// a positive divisor, which affine ceildiv requires, makes the comparison
+  /// hold exactly when the division is inexact and the exact quotient is
+  /// positive. Unlike the arith.ceildivsi expansion, no sign comparison is
+  /// needed.
   ///
   /// Note: not using arith.ceildivsi for the same reason as explained in the
   /// visitFloorDivExpr comment.
@@ -170,21 +175,15 @@ class AffineApplyExpander
     auto rhs = visit(expr.getRHS());
     assert(lhs && rhs && "unexpected affine expr lowering failure");
 
-    Value zeroCst = arith::ConstantIndexOp::create(builder, loc, 0);
     Value oneCst = arith::ConstantIndexOp::create(builder, loc, 1);
-    Value nonPositive = arith::CmpIOp::create(
-        builder, loc, arith::CmpIPredicate::sle, lhs, zeroCst);
-    Value negated = arith::SubIOp::create(builder, loc, zeroCst, lhs);
-    Value decremented = arith::SubIOp::create(builder, loc, lhs, oneCst);
-    Value dividend = arith::SelectOp::create(builder, loc, nonPositive, negated,
-                                             decremented);
-    Value quotient = arith::DivSIOp::create(builder, loc, dividend, rhs);
-    Value negatedQuotient =
-        arith::SubIOp::create(builder, loc, zeroCst, quotient);
+    Value quotient = arith::DivSIOp::create(builder, loc, lhs, rhs);
+    Value product = arith::MulIOp::create(builder, loc, quotient, rhs);
+    Value roundUp = arith::CmpIOp::create(
+        builder, loc, arith::CmpIPredicate::sgt, lhs, product);
     Value incrementedQuotient =
         arith::AddIOp::create(builder, loc, quotient, oneCst);
-    Value result = arith::SelectOp::create(
-        builder, loc, nonPositive, negatedQuotient, incrementedQuotient);
+    Value result = arith::SelectOp::create(builder, loc, roundUp,
+                                           incrementedQuotient, quotient);
     return result;
   }
 
diff --git a/mlir/test/Conversion/AffineToStandard/lower-affine.mlir b/mlir/test/Conversion/AffineToStandard/lower-affine.mlir
index 943b8675d183c..e2b13368f09a9 100644
--- a/mlir/test/Conversion/AffineToStandard/lower-affine.mlir
+++ b/mlir/test/Conversion/AffineToStandard/lower-affine.mlir
@@ -1,4 +1,5 @@
 // RUN: mlir-opt -lower-affine %s | FileCheck %s
+// RUN: mlir-opt -lower-affine -canonicalize %s | FileCheck %s --check-prefix=FOLDED
 
 // CHECK-LABEL: func @empty() {
 func.func @empty() {
@@ -559,36 +560,48 @@ func.func @affine_apply_floordiv_dynamic_divisor(%arg0 : index, %arg1 : index) -
 // CHECK-LABEL: func @affine_apply_ceildiv
 func.func @affine_apply_ceildiv(%arg0 : index) -> (index) {
 // CHECK-NEXT:  %[[c42:.*]] = arith.constant 42 : index
-// CHECK-NEXT:  %[[c0:.*]] = arith.constant 0 : index
 // CHECK-NEXT:  %[[c1:.*]] = arith.constant 1 : index
-// CHECK-NEXT:  %[[v0:.*]] = arith.cmpi sle, %{{.*}}, %[[c0]] : index
-// CHECK-NEXT:  %[[v1:.*]] = arith.subi %[[c0]], %{{.*}} : index
-// CHECK-NEXT:  %[[v2:.*]] = arith.subi %{{.*}}, %[[c1]] : index
-// CHECK-NEXT:  %[[v3:.*]] = arith.select %[[v0]], %[[v1]], %[[v2]] : index
-// CHECK-NEXT:  %[[v4:.*]] = arith.divsi %[[v3]], %[[c42]] : index
-// CHECK-NEXT:  %[[v5:.*]] = arith.subi %[[c0]], %[[v4]] : index
-// CHECK-NEXT:  %[[v6:.*]] = arith.addi %[[v4]], %[[c1]] : index
-// CHECK-NEXT:  %[[v7:.*]] = arith.select %[[v0]], %[[v5]], %[[v6]] : index
+// CHECK-NEXT:  %[[v0:.*]] = arith.divsi %{{.*}}, %[[c42]] : index
+// CHECK-NEXT:  %[[v1:.*]] = arith.muli %[[v0]], %[[c42]] : index
+// CHECK-NEXT:  %[[v2:.*]] = arith.cmpi sgt, %{{.*}}, %[[v1]] : index
+// CHECK-NEXT:  %[[v3:.*]] = arith.addi %[[v0]], %[[c1]] : index
+// CHECK-NEXT:  %[[v4:.*]] = arith.select %[[v2]], %[[v3]], %[[v0]] : index
   %0 = affine.apply #map_ceildiv (%arg0)
   return %0 : index
 }
 #map_ceildiv_dynamic_divisor = affine_map<(i)[s] -> (i ceildiv s)>
 // CHECK-LABEL: func @affine_apply_ceildiv_dynamic_divisor
 func.func @affine_apply_ceildiv_dynamic_divisor(%arg0 : index, %arg1 : index) -> (index) {
-// CHECK-NEXT:  %[[c0:.*]] = arith.constant 0 : index
 // CHECK-NEXT:  %[[c1:.*]] = arith.constant 1 : index
-// CHECK-NEXT:  %[[v0:.*]] = arith.cmpi sle, %{{.*}}, %[[c0]] : index
-// CHECK-NEXT:  %[[v1:.*]] = arith.subi %[[c0]], %{{.*}} : index
-// CHECK-NEXT:  %[[v2:.*]] = arith.subi %{{.*}}, %[[c1]] : index
-// CHECK-NEXT:  %[[v3:.*]] = arith.select %[[v0]], %[[v1]], %[[v2]] : index
-// CHECK-NEXT:  %[[v4:.*]] = arith.divsi %[[v3]], %arg1 : index
-// CHECK-NEXT:  %[[v5:.*]] = arith.subi %[[c0]], %[[v4]] : index
-// CHECK-NEXT:  %[[v6:.*]] = arith.addi %[[v4]], %[[c1]] : index
-// CHECK-NEXT:  %[[v7:.*]] = arith.select %[[v0]], %[[v5]], %[[v6]] : index
+// CHECK-NEXT:  %[[v0:.*]] = arith.divsi %{{.*}}, %arg1 : index
+// CHECK-NEXT:  %[[v1:.*]] = arith.muli %[[v0]], %arg1 : index
+// CHECK-NEXT:  %[[v2:.*]] = arith.cmpi sgt, %{{.*}}, %[[v1]] : index
+// CHECK-NEXT:  %[[v3:.*]] = arith.addi %[[v0]], %[[c1]] : index
+// CHECK-NEXT:  %[[v4:.*]] = arith.select %[[v2]], %[[v3]], %[[v0]] : index
   %0 = affine.apply #map_ceildiv_dynamic_divisor (%arg0)[%arg1]
   return %0 : index
 }
 
+#map_ceildiv_seven = affine_map<(i) -> (i ceildiv 7)>
+// The lowered arithmetic has to agree with the constant folder
+// (`divideCeilSigned`) and with range inference (`intrange::inferCeilDivS`),
+// both of which give the exact, negative quotient for an INT_MIN dividend.
+// Negating the dividend, which the lowering used to do, made this positive
+// (`1317624576693539401`).
+//
+// The dividend is hidden behind an `arith.addi` so that the map is lowered
+// rather than folded; the affine folder never reaches the expansion.
+// FOLDED-LABEL: func @affine_apply_ceildiv_intmin_dividend
+func.func @affine_apply_ceildiv_intmin_dividend() -> (index) {
+  %c0 = arith.constant 0 : index
+  %cmin = arith.constant -9223372036854775808 : index
+  %dividend = arith.addi %cmin, %c0 : index
+// FOLDED:      %[[c:.*]] = arith.constant -1317624576693539401 : index
+// FOLDED-NEXT: return %[[c]]
+  %0 = affine.apply #map_ceildiv_seven (%dividend)
+  return %0 : index
+}
+
 // CHECK-LABEL: func @affine_load
 func.func @affine_load(%arg0 : index) {
   %0 = memref.alloc() : memref<10xf32>
diff --git a/mlir/test/Transforms/canonicalize.mlir b/mlir/test/Transforms/canonicalize.mlir
index 8e02c06a0a293..7e0352c87289c 100644
--- a/mlir/test/Transforms/canonicalize.mlir
+++ b/mlir/test/Transforms/canonicalize.mlir
@@ -655,32 +655,24 @@ func.func @lowered_affine_ceildiv() -> (index, index) {
 // CHECK-DAG:  %c-1 = arith.constant -1 : index
   %c-43 = arith.constant -43 : index
   %c42 = arith.constant 42 : index
-  %c0 = arith.constant 0 : index
   %c1 = arith.constant 1 : index
-  %0 = arith.cmpi sle, %c-43, %c0 : index
-  %1 = arith.subi %c0, %c-43 : index
-  %2 = arith.subi %c-43, %c1 : index
-  %3 = arith.select %0, %1, %2 : index
-  %4 = arith.divsi %3, %c42 : index
-  %5 = arith.subi %c0, %4 : index
-  %6 = arith.addi %4, %c1 : index
-  %7 = arith.select %0, %5, %6 : index
+  %0 = arith.divsi %c-43, %c42 : index
+  %1 = arith.muli %0, %c42 : index
+  %2 = arith.cmpi sgt, %c-43, %1 : index
+  %3 = arith.addi %0, %c1 : index
+  %4 = arith.select %2, %3, %0 : index
 // CHECK-DAG:  %c2 = arith.constant 2 : index
   %c43 = arith.constant 43 : index
   %c42_0 = arith.constant 42 : index
-  %c0_1 = arith.constant 0 : index
-  %c1_2 = arith.constant 1 : index
-  %8 = arith.cmpi sle, %c43, %c0_1 : index
-  %9 = arith.subi %c0_1, %c43 : index
-  %10 = arith.subi %c43, %c1_2 : index
-  %11 = arith.select %8, %9, %10 : index
-  %12 = arith.divsi %11, %c42_0 : index
-  %13 = arith.subi %c0_1, %12 : index
-  %14 = arith.addi %12, %c1_2 : index
-  %15 = arith.select %8, %13, %14 : index
+  %c1_0 = arith.constant 1 : index
+  %5 = arith.divsi %c43, %c42_0 : index
+  %6 = arith.muli %5, %c42_0 : index
+  %7 = arith.cmpi sgt, %c43, %6 : index
+  %8 = arith.addi %5, %c1_0 : index
+  %9 = arith.select %7, %8, %5 : index
 
   // CHECK-NEXT: return %c-1, %c2
-  return %7, %15 : index, index
+  return %4, %9 : index, index
 }
 
 // Checks that NOP casts are removed.

>From dbf7a5961bcae7b4cd89acac3e421ed03b8591d0 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 3/4] [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 358a393ac99cef258653e5e3d46fb57b29189c5e 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 4/4] [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