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

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Oct 29 08:50:01 PDT 2026


Author: Arseniy Obolenskiy
Date: 2026-10-29T16:49:54+01:00
New Revision: 6d289cc87877f12e2b83260a117a38b8fd19e8a3

URL: https://github.com/llvm/llvm-project/commit/6d289cc87877f12e2b83260a117a38b8fd19e8a3
DIFF: https://github.com/llvm/llvm-project/commit/6d289cc87877f12e2b83260a117a38b8fd19e8a3.diff

LOG: [mlir][SPIR-V] Add ArithToSPIRV patterns for ceildivui/ceildivsi/floordivsi (#212926)

Added: 
    

Modified: 
    mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
    mlir/test/Conversion/ArithToSPIRV/arith-to-spirv-unsupported.mlir
    mlir/test/Conversion/ArithToSPIRV/arith-to-spirv.mlir
    mlir/test/Conversion/ConvertToSPIRV/arith.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
index 6e3bcea50d5650..bd73f2fd76cae1 100644
--- a/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
+++ b/mlir/lib/Conversion/ArithToSPIRV/ArithToSPIRV.cpp
@@ -499,6 +499,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 
diff Sign =
+        spirv::LogicalNotEqualOp::create(rewriter, loc, aNeg, bNeg);
+    Value cond = spirv::LogicalAndOp::create(rewriter, loc, notEqual, 
diff Sign);
+
+    Value quotientMinusOne =
+        spirv::IAddOp::create(rewriter, loc, quotient, minusOne);
+    rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, cond, quotientMinusOne,
+                                                 quotient);
+    return success();
+  }
+};
+
 //===----------------------------------------------------------------------===//
 // BitwiseOp
 //===----------------------------------------------------------------------===//
@@ -588,6 +710,9 @@ struct XOrIOpBooleanPattern final : public OpConversionPattern<arith::XOrIOp> {
 ///   muli(a, b)  = a & b  (1*1=1, else 0 = LogicalAnd)
 ///   divui(a, b) = a & b  (a/1=a, a/0=UB; truth table matches AND)
 ///   divsi(a, b) = a & b  (same as divui on i1)
+///   ceildivui(a, b)  = a & b  (same as divui on i1)
+///   ceildivsi(a, b)  = a & b  (same as divsi on i1)
+///   floordivsi(a, b) = a & b  (same as divsi on i1)
 ///   maxsi(a, b) = a & b  (signed i1: 1 represents -1, so max is 0 unless both
 ///                        are 1)
 ///   maxui(a, b) = a | b  (unsigned max on i1: 1 when either operand is 1)
@@ -1485,6 +1610,12 @@ 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>,
+    BoolIOpPattern<arith::CeilDivUIOp, spirv::LogicalAndOp>,  // same as divui on i1
+    CeilDivUIOpPattern,
+    BoolIOpPattern<arith::CeilDivSIOp, spirv::LogicalAndOp>,  // same as divsi on i1
+    CeilDivSIOpPattern,
+    BoolIOpPattern<arith::FloorDivSIOp, spirv::LogicalAndOp>, // same as divsi on i1
+    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 9ecebb502630cb..08098e4552e07b 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 c79ab69133ac3c..31159eaa2f17f7 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
@@ -380,6 +435,26 @@ func.func @bool_shift_div_rem_scalar(%arg0 : i1, %arg1 : i1) {
   // CHECK: %[[NOTB:.+]] = spirv.LogicalNot %arg1
   // CHECK: spirv.LogicalAnd %arg0, %[[NOTB]]
   %6 = arith.remsi %arg0, %arg1 : i1
+  // ceildivui(a,b) = a & b  (same as divui)
+  // CHECK: spirv.LogicalAnd %arg0, %arg1
+  %7 = arith.ceildivui %arg0, %arg1 : i1
+  // ceildivsi(a,b) = a & b  (same as divsi)
+  // CHECK: spirv.LogicalAnd %arg0, %arg1
+  %8 = arith.ceildivsi %arg0, %arg1 : i1
+  // floordivsi(a,b) = a & b  (same as divsi)
+  // CHECK: spirv.LogicalAnd %arg0, %arg1
+  %9 = arith.floordivsi %arg0, %arg1 : i1
+  return
+}
+
+// CHECK-LABEL: @bool_div_vector
+func.func @bool_div_vector(%arg0 : vector<4xi1>, %arg1 : vector<4xi1>) {
+  // CHECK: spirv.LogicalAnd %arg0, %arg1 : vector<4xi1>
+  %0 = arith.ceildivui %arg0, %arg1 : vector<4xi1>
+  // CHECK: spirv.LogicalAnd %arg0, %arg1 : vector<4xi1>
+  %1 = arith.ceildivsi %arg0, %arg1 : vector<4xi1>
+  // CHECK: spirv.LogicalAnd %arg0, %arg1 : vector<4xi1>
+  %2 = arith.floordivsi %arg0, %arg1 : vector<4xi1>
   return
 }
 

diff  --git a/mlir/test/Conversion/ConvertToSPIRV/arith.mlir b/mlir/test/Conversion/ConvertToSPIRV/arith.mlir
index fa204c4df8ace5..ec62be555c2d04 100644
--- a/mlir/test/Conversion/ConvertToSPIRV/arith.mlir
+++ b/mlir/test/Conversion/ConvertToSPIRV/arith.mlir
@@ -37,10 +37,10 @@ func.func @int32_scalar_srem(%lhs: i32, %rhs: i32) {
 // CHECK-LABEL: @scalar_ceildivui
 // CHECK-SAME: (%[[LHS:.+]]: i32, %[[RHS:.+]]: i32)
 func.func @scalar_ceildivui(%lhs: i32, %rhs: i32) -> i32 {
-  // CHECK:     %[[ISZERO:.+]] = spirv.IEqual %[[LHS]], %{{.+}} : 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:     spirv.ReturnValue %[[R]]
   %0 = arith.ceildivui %lhs, %rhs : i32


        


More information about the Mlir-commits mailing list