[Mlir-commits] [mlir] [mlir][SPIR-V] Add ArithToSPIRV patterns for ceildivui/ceildivsi/floordivsi (PR #212926)

Arseniy Obolenskiy llvmlistbot at llvm.org
Thu Jul 30 02:25:20 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/212926

>From 99d2e85062bcea4d9a03cd204983609dac9f0ac5 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 30 Jul 2026 06:34:07 +0200
Subject: [PATCH 1/2] [mlir][SPIR-V] Add ArithToSPIRV patterns for
 ceildivui/ceildivsi/floordivsi

---
 .../Conversion/ArithToSPIRV/ArithToSPIRV.cpp  | 125 ++++++++++++++++++
 .../arith-to-spirv-unsupported.mlir           |   9 ++
 .../ArithToSPIRV/arith-to-spirv.mlir          |  55 ++++++++
 3 files changed, 189 insertions(+)

diff --git a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
index 614ac2b43b5f2..e2e4ae36fc676 100644
--- a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
+++ b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
@@ -496,6 +496,128 @@ struct RemSIOpCLPattern final : public OpConversionPattern<arith::RemSIOp> {
   }
 };
 
+//===----------------------------------------------------------------------===//
+// CeilDivUIOp
+//===----------------------------------------------------------------------===//
+
+/// Converts arith.ceildivui to SPIR-V ops. Convert `ceildivui(n, m)` into
+/// `n == 0 ? 0 : (n-1)/m + 1`. Formula taken from the equivalent conversion
+/// in IndexToSPIRV.
+struct CeilDivUIOpPattern final : OpConversionPattern<arith::CeilDivUIOp> {
+  using Base::Base;
+
+  LogicalResult
+  matchAndRewrite(arith::CeilDivUIOp op, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    Type dstType = getTypeConverter()->convertType(op.getType());
+    if (!dstType)
+      return getTypeConversionFailure(rewriter, op);
+
+    if (!getElementTypeOrSelf(op.getType()).isIndex() &&
+        dstType != op.getType())
+      return op.emitError("bitwidth emulation is not implemented yet on "
+                          "unsigned op pattern version");
+
+    Location loc = op.getLoc();
+    Value n = adaptor.getLhs();
+    Value m = adaptor.getRhs();
+
+    Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
+    Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
+
+    Value minusOne = spirv::ISubOp::create(rewriter, loc, n, one);
+    Value quotient = spirv::UDivOp::create(rewriter, loc, minusOne, m);
+    Value plusOne = spirv::IAddOp::create(rewriter, loc, quotient, one);
+
+    Value cmp = spirv::IEqualOp::create(rewriter, loc, n, zero);
+    rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cmp, zero, plusOne);
+    return success();
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// CeilDivSIOp
+//===----------------------------------------------------------------------===//
+
+/// Converts arith.ceildivsi to SPIR-V ops. Convert `ceildivsi(a, b)` into
+/// `q = a/b; (q*b != a && (a<0) == (b<0)) ? q+1 : q`. Formula taken from the
+/// equivalent conversion in ExpandOps.
+struct CeilDivSIOpPattern final : OpConversionPattern<arith::CeilDivSIOp> {
+  using Base::Base;
+
+  LogicalResult
+  matchAndRewrite(arith::CeilDivSIOp op, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    Type dstType = getTypeConverter()->convertType(op.getType());
+    if (!dstType)
+      return getTypeConversionFailure(rewriter, op);
+
+    Location loc = op.getLoc();
+    Value a = adaptor.getLhs();
+    Value b = adaptor.getRhs();
+
+    Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
+    Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
+
+    Value quotient = spirv::SDivOp::create(rewriter, loc, a, b);
+    Value product = spirv::IMulOp::create(rewriter, loc, quotient, b);
+    Value notEqual = spirv::INotEqualOp::create(rewriter, loc, a, product);
+
+    Value aNeg = spirv::SLessThanOp::create(rewriter, loc, a, zero);
+    Value bNeg = spirv::SLessThanOp::create(rewriter, loc, b, zero);
+    Value sameSign = spirv::LogicalEqualOp::create(rewriter, loc, aNeg, bNeg);
+    Value cond = spirv::LogicalAndOp::create(rewriter, loc, notEqual, sameSign);
+
+    Value quotientPlusOne = spirv::IAddOp::create(rewriter, loc, quotient, one);
+    rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cond, quotientPlusOne,
+                                                 quotient);
+    return success();
+  }
+};
+
+//===----------------------------------------------------------------------===//
+// FloorDivSIOp
+//===----------------------------------------------------------------------===//
+
+/// Converts arith.floordivsi to SPIR-V ops. Convert `floordivsi(a, b)` into
+/// `q = a/b; (q*b != a && (a<0) != (b<0)) ? q-1 : q`. Formula taken from the
+/// equivalent conversion in ExpandOps.
+struct FloorDivSIOpPattern final : OpConversionPattern<arith::FloorDivSIOp> {
+  using Base::Base;
+
+  LogicalResult
+  matchAndRewrite(arith::FloorDivSIOp op, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const override {
+    Type dstType = getTypeConverter()->convertType(op.getType());
+    if (!dstType)
+      return getTypeConversionFailure(rewriter, op);
+
+    Location loc = op.getLoc();
+    Value a = adaptor.getLhs();
+    Value b = adaptor.getRhs();
+
+    Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
+    Value minusOne = getScalarOrVectorConstInt(
+        dstType, static_cast<uint64_t>(-1), rewriter, loc);
+
+    Value quotient = spirv::SDivOp::create(rewriter, loc, a, b);
+    Value product = spirv::IMulOp::create(rewriter, loc, quotient, b);
+    Value notEqual = spirv::INotEqualOp::create(rewriter, loc, a, product);
+
+    Value aNeg = spirv::SLessThanOp::create(rewriter, loc, a, zero);
+    Value bNeg = spirv::SLessThanOp::create(rewriter, loc, b, zero);
+    Value diffSign =
+        spirv::LogicalNotEqualOp::create(rewriter, loc, aNeg, bNeg);
+    Value cond = spirv::LogicalAndOp::create(rewriter, loc, notEqual, diffSign);
+
+    Value quotientMinusOne =
+        spirv::IAddOp::create(rewriter, loc, quotient, minusOne);
+    rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cond, quotientMinusOne,
+                                                 quotient);
+    return success();
+  }
+};
+
 //===----------------------------------------------------------------------===//
 // BitwiseOp
 //===----------------------------------------------------------------------===//
@@ -1476,6 +1598,9 @@ void mlir::arith::populateArithToSPIRVPatterns(
     spirv::ElementwiseOpPattern<arith::DivUIOp, spirv::UDivOp>,
     BoolIOpPattern<arith::DivSIOp, spirv::LogicalAndOp>,     // same as divui on i1
     spirv::ElementwiseOpPattern<arith::DivSIOp, spirv::SDivOp>,
+    CeilDivUIOpPattern,
+    CeilDivSIOpPattern,
+    FloorDivSIOpPattern,
     BoolIOpAndNotPattern<arith::RemUIOp>,  // remui(a,b) = a & ~b (see pattern comment)
     spirv::ElementwiseOpPattern<arith::RemUIOp, spirv::UModOp>,
     BoolIOpAndNotPattern<arith::RemSIOp>,  // remsi(a,b) = a & ~b (see pattern comment)
diff --git a/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv-unsupported.mlir b/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv-unsupported.mlir
index 9ecebb502630c..08098e4552e07 100644
--- a/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv-unsupported.mlir
+++ b/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv-unsupported.mlir
@@ -62,6 +62,15 @@ func.func @int_vector4_invalid(%arg0: vector<2xi16>, %arg1: vector<2xi16>) {
 
 // -----
 
+func.func @ceildivui_vector4_invalid(%arg0: vector<2xi16>, %arg1: vector<2xi16>) {
+  // expected-error @+2 {{failed to legalize operation 'arith.ceildivui'}}
+  // expected-error @+1 {{bitwidth emulation is not implemented yet on unsigned op}}
+  %0 = arith.ceildivui %arg0, %arg1: vector<2xi16>
+  return
+}
+
+// -----
+
 func.func @int_vector_invalid_bitwidth(%arg0: vector<2xi12>) {
   // expected-error @+1 {{failed to legalize operation 'arith.addi'}}
   %0 = arith.addi %arg0, %arg0: vector<2xi12>
diff --git a/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv.mlir b/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv.mlir
index 4cef06992727b..312c4c059591a 100644
--- a/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv.mlir
+++ b/mlir/test/Conversion/ArithToSPIRV/arith-to-spirv.mlir
@@ -40,6 +40,61 @@ func.func @int32_scalar_srem(%lhs: i32, %rhs: i32) {
   return
 }
 
+// CHECK-LABEL: @scalar_ceildivui
+// CHECK-SAME: (%[[LHS:.+]]: i32, %[[RHS:.+]]: i32)
+func.func @scalar_ceildivui(%lhs: i32, %rhs: i32) -> i32 {
+  // CHECK:     %[[MINUSONE:.+]] = spirv.ISub %[[LHS]], %{{.+}} : i32
+  // CHECK:     %[[Q:.+]] = spirv.UDiv %[[MINUSONE]], %[[RHS]] : i32
+  // CHECK:     %[[PLUSONE:.+]] = spirv.IAdd %[[Q]], %{{.+}} : i32
+  // CHECK:     %[[ISZERO:.+]] = spirv.IEqual %[[LHS]], %{{.+}} : i32
+  // CHECK:     %[[R:.+]] = spirv.Select %[[ISZERO]], %{{.+}}, %[[PLUSONE]] : i1, i32
+  // CHECK:     return %[[R]]
+  %0 = arith.ceildivui %lhs, %rhs : i32
+  return %0 : i32
+}
+
+// CHECK-LABEL: @scalar_ceildivsi
+// CHECK-SAME: (%[[LHS:.+]]: i32, %[[RHS:.+]]: i32)
+func.func @scalar_ceildivsi(%lhs: i32, %rhs: i32) -> i32 {
+  // CHECK:     %[[Q:.+]] = spirv.SDiv %[[LHS]], %[[RHS]] : i32
+  // CHECK:     %[[PROD:.+]] = spirv.IMul %[[Q]], %[[RHS]] : i32
+  // CHECK:     %[[NE:.+]] = spirv.INotEqual %[[LHS]], %[[PROD]] : i32
+  // CHECK:     %[[LNEG:.+]] = spirv.SLessThan %[[LHS]], %{{.+}} : i32
+  // CHECK:     %[[RNEG:.+]] = spirv.SLessThan %[[RHS]], %{{.+}} : i32
+  // CHECK:     %[[SAMESIGN:.+]] = spirv.LogicalEqual %[[LNEG]], %[[RNEG]] : i1
+  // CHECK:     %[[COND:.+]] = spirv.LogicalAnd %[[NE]], %[[SAMESIGN]] : i1
+  // CHECK:     %[[QP1:.+]] = spirv.IAdd %[[Q]], %{{.+}} : i32
+  // CHECK:     %[[R:.+]] = spirv.Select %[[COND]], %[[QP1]], %[[Q]] : i1, i32
+  // CHECK:     return %[[R]]
+  %0 = arith.ceildivsi %lhs, %rhs : i32
+  return %0 : i32
+}
+
+// CHECK-LABEL: @scalar_floordivsi
+// CHECK-SAME: (%[[LHS:.+]]: i32, %[[RHS:.+]]: i32)
+func.func @scalar_floordivsi(%lhs: i32, %rhs: i32) -> i32 {
+  // CHECK:     %[[Q:.+]] = spirv.SDiv %[[LHS]], %[[RHS]] : i32
+  // CHECK:     %[[PROD:.+]] = spirv.IMul %[[Q]], %[[RHS]] : i32
+  // CHECK:     %[[NE:.+]] = spirv.INotEqual %[[LHS]], %[[PROD]] : i32
+  // CHECK:     %[[LNEG:.+]] = spirv.SLessThan %[[LHS]], %{{.+}} : i32
+  // CHECK:     %[[RNEG:.+]] = spirv.SLessThan %[[RHS]], %{{.+}} : i32
+  // CHECK:     %[[DIFFSIGN:.+]] = spirv.LogicalNotEqual %[[LNEG]], %[[RNEG]] : i1
+  // CHECK:     %[[COND:.+]] = spirv.LogicalAnd %[[NE]], %[[DIFFSIGN]] : i1
+  // CHECK:     %[[QM1:.+]] = spirv.IAdd %[[Q]], %{{.+}} : i32
+  // CHECK:     %[[R:.+]] = spirv.Select %[[COND]], %[[QM1]], %[[Q]] : i1, i32
+  // CHECK:     return %[[R]]
+  %0 = arith.floordivsi %lhs, %rhs : i32
+  return %0 : i32
+}
+
+// CHECK-LABEL: @vector_ceildivsi
+func.func @vector_ceildivsi(%lhs: vector<4xi32>, %rhs: vector<4xi32>) -> vector<4xi32> {
+  // CHECK: spirv.SDiv %{{.*}}, %{{.*}} : vector<4xi32>
+  // CHECK: spirv.Select %{{.*}}, %{{.*}}, %{{.*}} : vector<4xi1>, vector<4xi32>
+  %0 = arith.ceildivsi %lhs, %rhs : vector<4xi32>
+  return %0 : vector<4xi32>
+}
+
 // CHECK-LABEL: @index_scalar
 func.func @index_scalar(%lhs: index, %rhs: index) {
   // CHECK: spirv.IAdd %{{.*}}, %{{.*}}: i32

>From 28e31d46da5eaa26e60813511860d6abf773c051 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 30 Jul 2026 10:46:51 +0200
Subject: [PATCH 2/2] fix

---
 mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
index e2e4ae36fc676..f01aa434cb853 100644
--- a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
+++ b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
@@ -525,11 +525,12 @@ struct CeilDivUIOpPattern final : OpConversionPattern<arith::CeilDivUIOp> {
     Value zero = spirv::ConstantOp::getZero(dstType, loc, rewriter);
     Value one = spirv::ConstantOp::getOne(dstType, loc, rewriter);
 
+    Value cmp = spirv::IEqualOp::create(rewriter, loc, n, zero);
+
     Value minusOne = spirv::ISubOp::create(rewriter, loc, n, one);
     Value quotient = spirv::UDivOp::create(rewriter, loc, minusOne, m);
     Value plusOne = spirv::IAddOp::create(rewriter, loc, quotient, one);
 
-    Value cmp = spirv::IEqualOp::create(rewriter, loc, n, zero);
     rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cmp, zero, plusOne);
     return success();
   }



More information about the Mlir-commits mailing list