[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