[Mlir-commits] [mlir] [mlir][SPIR-V] Add ComplexToSPIRV lowering for complex.sin/cos (PR #231873)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Wed Oct 14 21:08:55 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/231873
>From 031f9e88b772b53df75148a09fb7b42096924519 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 5 Aug 2026 11:48:46 +0200
Subject: [PATCH 1/2] [mlir][SPIR-V] Add ComplexToSPIRV lowering for
complex.sin/cos
Lowers complex.sin/cos via the shared TrigonometricOpPattern base,
following the standard real/imaginary component identities in terms
of sin, cos, and exp of the scalar components.
---
.../ComplexToSPIRV/ComplexToSPIRV.cpp | 90 +++++++++++++++-
.../ComplexToSPIRV/complex-to-spirv.mlir | 100 ++++++++++++++++++
2 files changed, 189 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp b/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
index e57d6d922e5ba..4195ade8287b1 100644
--- a/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
+++ b/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
@@ -369,6 +369,89 @@ struct SignOpPattern final : OpConversionPattern<complex::SignOp> {
return success();
}
};
+
+/// Common building blocks for complex.sin/cos, following the identities
+/// sin(x + iy) = sin(x) * (0.5*exp(y) + 0.5/exp(y))
+/// + i * cos(x) * (0.5*exp(y) - 0.5/exp(y))
+/// cos(x + iy) = cos(x) * (0.5/exp(y) + 0.5*exp(y))
+/// + i * sin(x) * (0.5/exp(y) - 0.5*exp(y))
+template <typename ComplexOp, typename SinOp, typename CosOp, typename ExpOp>
+struct TrigonometricOpPattern : OpConversionPattern<ComplexOp> {
+ using OpConversionPattern<ComplexOp>::OpConversionPattern;
+ using OpAdaptor = typename ComplexOp::Adaptor;
+
+ LogicalResult
+ matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ Type spirvType =
+ this->getTypeConverter()->convertType(op.getResult().getType());
+ if (!spirvType)
+ return rewriter.notifyMatchFailure(op, "unable to convert result type");
+
+ Location loc = op.getLoc();
+ Value complexVal = adaptor.getComplex();
+
+ Value re =
+ spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
+ Value im =
+ spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
+
+ Value half = createFPConstant(rewriter, loc, re.getType(), 0.5);
+ Value exp = ExpOp::create(rewriter, loc, im);
+ Value scaledExp = spirv::FMulOp::create(rewriter, loc, half, exp);
+ Value reciprocalExp = spirv::FDivOp::create(rewriter, loc, half, exp);
+ Value sin = SinOp::create(rewriter, loc, re);
+ Value cos = CosOp::create(rewriter, loc, re);
+
+ auto [resultRe, resultIm] =
+ combine(rewriter, loc, scaledExp, reciprocalExp, sin, cos);
+
+ rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
+ op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
+ return success();
+ }
+
+ virtual std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
+ Location loc, Value scaledExp,
+ Value reciprocalExp, Value sin,
+ Value cos) const = 0;
+};
+
+template <typename SinOp, typename CosOp, typename ExpOp>
+struct SinOpPattern final
+ : TrigonometricOpPattern<complex::SinOp, SinOp, CosOp, ExpOp> {
+ using TrigonometricOpPattern<complex::SinOp, SinOp, CosOp,
+ ExpOp>::TrigonometricOpPattern;
+
+ std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
+ Location loc, Value scaledExp,
+ Value reciprocalExp, Value sin,
+ Value cos) const override {
+ Value sum = spirv::FAddOp::create(rewriter, loc, scaledExp, reciprocalExp);
+ Value resultRe = spirv::FMulOp::create(rewriter, loc, sum, sin);
+ Value diff = spirv::FSubOp::create(rewriter, loc, scaledExp, reciprocalExp);
+ Value resultIm = spirv::FMulOp::create(rewriter, loc, diff, cos);
+ return {resultRe, resultIm};
+ }
+};
+
+template <typename SinOp, typename CosOp, typename ExpOp>
+struct CosOpPattern final
+ : TrigonometricOpPattern<complex::CosOp, SinOp, CosOp, ExpOp> {
+ using TrigonometricOpPattern<complex::CosOp, SinOp, CosOp,
+ ExpOp>::TrigonometricOpPattern;
+
+ std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
+ Location loc, Value scaledExp,
+ Value reciprocalExp, Value sin,
+ Value cos) const override {
+ Value sum = spirv::FAddOp::create(rewriter, loc, reciprocalExp, scaledExp);
+ Value resultRe = spirv::FMulOp::create(rewriter, loc, sum, cos);
+ Value diff = spirv::FSubOp::create(rewriter, loc, reciprocalExp, scaledExp);
+ Value resultIm = spirv::FMulOp::create(rewriter, loc, diff, sin);
+ return {resultRe, resultIm};
+ }
+};
} // namespace
//===----------------------------------------------------------------------===//
@@ -395,5 +478,10 @@ void mlir::populateComplexToSPIRVPatterns(
SignOpPattern<spirv::GLSqrtOp, spirv::GLFAbsOp, spirv::GLFMaxOp,
spirv::GLFMinOp>,
SignOpPattern<spirv::CLSqrtOp, spirv::CLFAbsOp, spirv::CLFMaxOp,
- spirv::CLFMinOp>>(typeConverter, context);
+ spirv::CLFMinOp>,
+ SinOpPattern<spirv::GLSinOp, spirv::GLCosOp, spirv::GLExpOp>,
+ SinOpPattern<spirv::CLSinOp, spirv::CLCosOp, spirv::CLExpOp>,
+ CosOpPattern<spirv::GLSinOp, spirv::GLCosOp, spirv::GLExpOp>,
+ CosOpPattern<spirv::CLSinOp, spirv::CLCosOp, spirv::CLExpOp>>(
+ typeConverter, context);
}
diff --git a/mlir/test/Conversion/ComplexToSPIRV/complex-to-spirv.mlir b/mlir/test/Conversion/ComplexToSPIRV/complex-to-spirv.mlir
index 1b39062ab985a..5d9bc983ecb13 100644
--- a/mlir/test/Conversion/ComplexToSPIRV/complex-to-spirv.mlir
+++ b/mlir/test/Conversion/ComplexToSPIRV/complex-to-spirv.mlir
@@ -300,3 +300,103 @@ func.func @complex_sign(%arg: complex<f32>) -> complex<f32> {
// CHECK: %[[SELRE:.+]] = spirv.Select %[[ISZERO]], %[[RE]], %[[SIGNRE]] : i1, f32
// CHECK: %[[SELIM:.+]] = spirv.Select %[[ISZERO]], %[[IM]], %[[SIGNIM]] : i1, f32
// CHECK: %[[RESULT:.+]] = spirv.CompositeConstruct %[[SELRE]], %[[SELIM]] : (f32, f32) -> vector<2xf32>
+
+// -----
+
+func.func @complex_sin(%arg: complex<f32>) -> complex<f32> {
+ %sin = complex.sin %arg : complex<f32>
+ return %sin : complex<f32>
+}
+
+// CHECK-LABEL: func.func @complex_sin
+// CHECK-SAME: %[[ARG:.+]]: complex<f32>
+// CHECK: %[[V:.+]] = builtin.unrealized_conversion_cast %[[ARG]] : complex<f32> to vector<2xf32>
+// CHECK: %[[RE:.+]] = spirv.CompositeExtract %[[V]][0 : i32] : vector<2xf32>
+// CHECK: %[[IM:.+]] = spirv.CompositeExtract %[[V]][1 : i32] : vector<2xf32>
+// CHECK: %[[HALF:.+]] = spirv.Constant 5.000000e-01 : f32
+// CHECK: %[[EXP:.+]] = spirv.GL.Exp %[[IM]] : f32
+// CHECK: %[[SCALED:.+]] = spirv.FMul %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[RECIP:.+]] = spirv.FDiv %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[SIN:.+]] = spirv.GL.Sin %[[RE]] : f32
+// CHECK: %[[COS:.+]] = spirv.GL.Cos %[[RE]] : f32
+// CHECK: %[[SUM:.+]] = spirv.FAdd %[[SCALED]], %[[RECIP]] : f32
+// CHECK: %[[RESRE:.+]] = spirv.FMul %[[SUM]], %[[SIN]] : f32
+// CHECK: %[[DIFF:.+]] = spirv.FSub %[[SCALED]], %[[RECIP]] : f32
+// CHECK: %[[RESIM:.+]] = spirv.FMul %[[DIFF]], %[[COS]] : f32
+// CHECK: spirv.CompositeConstruct %[[RESRE]], %[[RESIM]] : (f32, f32) -> vector<2xf32>
+
+// -----
+
+func.func @complex_cos(%arg: complex<f32>) -> complex<f32> {
+ %cos = complex.cos %arg : complex<f32>
+ return %cos : complex<f32>
+}
+
+// CHECK-LABEL: func.func @complex_cos
+// CHECK-SAME: %[[ARG:.+]]: complex<f32>
+// CHECK: %[[V:.+]] = builtin.unrealized_conversion_cast %[[ARG]] : complex<f32> to vector<2xf32>
+// CHECK: %[[RE:.+]] = spirv.CompositeExtract %[[V]][0 : i32] : vector<2xf32>
+// CHECK: %[[IM:.+]] = spirv.CompositeExtract %[[V]][1 : i32] : vector<2xf32>
+// CHECK: %[[HALF:.+]] = spirv.Constant 5.000000e-01 : f32
+// CHECK: %[[EXP:.+]] = spirv.GL.Exp %[[IM]] : f32
+// CHECK: %[[SCALED:.+]] = spirv.FMul %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[RECIP:.+]] = spirv.FDiv %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[SIN:.+]] = spirv.GL.Sin %[[RE]] : f32
+// CHECK: %[[COS:.+]] = spirv.GL.Cos %[[RE]] : f32
+// CHECK: %[[SUM:.+]] = spirv.FAdd %[[RECIP]], %[[SCALED]] : f32
+// CHECK: %[[RESRE:.+]] = spirv.FMul %[[SUM]], %[[COS]] : f32
+// CHECK: %[[DIFF:.+]] = spirv.FSub %[[RECIP]], %[[SCALED]] : f32
+// CHECK: %[[RESIM:.+]] = spirv.FMul %[[DIFF]], %[[SIN]] : f32
+// CHECK: spirv.CompositeConstruct %[[RESRE]], %[[RESIM]] : (f32, f32) -> vector<2xf32>
+
+// -----
+
+module attributes {
+ spirv.target_env = #spirv.target_env<#spirv.vce<v1.0, [Kernel], []>, #spirv.resource_limits<>>
+} {
+
+func.func @complex_sin_opencl(%arg: complex<f32>) -> complex<f32> {
+ %sin = complex.sin %arg : complex<f32>
+ return %sin : complex<f32>
+}
+
+// CHECK-LABEL: func.func @complex_sin_opencl
+// CHECK-SAME: %[[ARG:.+]]: complex<f32>
+// CHECK: %[[V:.+]] = builtin.unrealized_conversion_cast %[[ARG]] : complex<f32> to vector<2xf32>
+// CHECK: %[[RE:.+]] = spirv.CompositeExtract %[[V]][0 : i32] : vector<2xf32>
+// CHECK: %[[IM:.+]] = spirv.CompositeExtract %[[V]][1 : i32] : vector<2xf32>
+// CHECK: %[[HALF:.+]] = spirv.Constant 5.000000e-01 : f32
+// CHECK: %[[EXP:.+]] = spirv.CL.exp %[[IM]] : f32
+// CHECK: %[[SCALED:.+]] = spirv.FMul %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[RECIP:.+]] = spirv.FDiv %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[SIN:.+]] = spirv.CL.sin %[[RE]] : f32
+// CHECK: %[[COS:.+]] = spirv.CL.cos %[[RE]] : f32
+// CHECK: %[[SUM:.+]] = spirv.FAdd %[[SCALED]], %[[RECIP]] : f32
+// CHECK: %[[RESRE:.+]] = spirv.FMul %[[SUM]], %[[SIN]] : f32
+// CHECK: %[[DIFF:.+]] = spirv.FSub %[[SCALED]], %[[RECIP]] : f32
+// CHECK: %[[RESIM:.+]] = spirv.FMul %[[DIFF]], %[[COS]] : f32
+// CHECK: spirv.CompositeConstruct %[[RESRE]], %[[RESIM]] : (f32, f32) -> vector<2xf32>
+
+func.func @complex_cos_opencl(%arg: complex<f32>) -> complex<f32> {
+ %cos = complex.cos %arg : complex<f32>
+ return %cos : complex<f32>
+}
+
+// CHECK-LABEL: func.func @complex_cos_opencl
+// CHECK-SAME: %[[ARG:.+]]: complex<f32>
+// CHECK: %[[V:.+]] = builtin.unrealized_conversion_cast %[[ARG]] : complex<f32> to vector<2xf32>
+// CHECK: %[[RE:.+]] = spirv.CompositeExtract %[[V]][0 : i32] : vector<2xf32>
+// CHECK: %[[IM:.+]] = spirv.CompositeExtract %[[V]][1 : i32] : vector<2xf32>
+// CHECK: %[[HALF:.+]] = spirv.Constant 5.000000e-01 : f32
+// CHECK: %[[EXP:.+]] = spirv.CL.exp %[[IM]] : f32
+// CHECK: %[[SCALED:.+]] = spirv.FMul %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[RECIP:.+]] = spirv.FDiv %[[HALF]], %[[EXP]] : f32
+// CHECK: %[[SIN:.+]] = spirv.CL.sin %[[RE]] : f32
+// CHECK: %[[COS:.+]] = spirv.CL.cos %[[RE]] : f32
+// CHECK: %[[SUM:.+]] = spirv.FAdd %[[RECIP]], %[[SCALED]] : f32
+// CHECK: %[[RESRE:.+]] = spirv.FMul %[[SUM]], %[[COS]] : f32
+// CHECK: %[[DIFF:.+]] = spirv.FSub %[[RECIP]], %[[SCALED]] : f32
+// CHECK: %[[RESIM:.+]] = spirv.FMul %[[DIFF]], %[[SIN]] : f32
+// CHECK: spirv.CompositeConstruct %[[RESRE]], %[[RESIM]] : (f32, f32) -> vector<2xf32>
+
+}
>From 9bf1ed6f5bb4b120902c56b8b2201f91fefd4cb5 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 15 Oct 2026 06:08:43 +0200
Subject: [PATCH 2/2] address comments
---
.../ComplexToSPIRV/ComplexToSPIRV.cpp | 75 ++++++-------------
1 file changed, 24 insertions(+), 51 deletions(-)
diff --git a/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp b/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
index 4195ade8287b1..ade634f8c5a47 100644
--- a/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
+++ b/mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp
@@ -370,13 +370,17 @@ struct SignOpPattern final : OpConversionPattern<complex::SignOp> {
}
};
-/// Common building blocks for complex.sin/cos, following the identities
+enum class SinCos { Sin, Cos };
+
+/// Lowers complex.sin/cos following the identities
/// sin(x + iy) = sin(x) * (0.5*exp(y) + 0.5/exp(y))
/// + i * cos(x) * (0.5*exp(y) - 0.5/exp(y))
/// cos(x + iy) = cos(x) * (0.5/exp(y) + 0.5*exp(y))
/// + i * sin(x) * (0.5/exp(y) - 0.5*exp(y))
-template <typename ComplexOp, typename SinOp, typename CosOp, typename ExpOp>
-struct TrigonometricOpPattern : OpConversionPattern<ComplexOp> {
+template <SinCos Kind, typename SinOp, typename CosOp, typename ExpOp,
+ typename ComplexOp = std::conditional_t<
+ Kind == SinCos::Sin, complex::SinOp, complex::CosOp>>
+struct TrigonometricOpPattern final : OpConversionPattern<ComplexOp> {
using OpConversionPattern<ComplexOp>::OpConversionPattern;
using OpAdaptor = typename ComplexOp::Adaptor;
@@ -403,54 +407,20 @@ struct TrigonometricOpPattern : OpConversionPattern<ComplexOp> {
Value sin = SinOp::create(rewriter, loc, re);
Value cos = CosOp::create(rewriter, loc, re);
- auto [resultRe, resultIm] =
- combine(rewriter, loc, scaledExp, reciprocalExp, sin, cos);
+ bool isSin = Kind == SinCos::Sin;
+ Value lhs = isSin ? scaledExp : reciprocalExp;
+ Value rhs = isSin ? reciprocalExp : scaledExp;
+ Value sum = spirv::FAddOp::create(rewriter, loc, lhs, rhs);
+ Value resultRe =
+ spirv::FMulOp::create(rewriter, loc, sum, isSin ? sin : cos);
+ Value diff = spirv::FSubOp::create(rewriter, loc, lhs, rhs);
+ Value resultIm =
+ spirv::FMulOp::create(rewriter, loc, diff, isSin ? cos : sin);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
-
- virtual std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
- Location loc, Value scaledExp,
- Value reciprocalExp, Value sin,
- Value cos) const = 0;
-};
-
-template <typename SinOp, typename CosOp, typename ExpOp>
-struct SinOpPattern final
- : TrigonometricOpPattern<complex::SinOp, SinOp, CosOp, ExpOp> {
- using TrigonometricOpPattern<complex::SinOp, SinOp, CosOp,
- ExpOp>::TrigonometricOpPattern;
-
- std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
- Location loc, Value scaledExp,
- Value reciprocalExp, Value sin,
- Value cos) const override {
- Value sum = spirv::FAddOp::create(rewriter, loc, scaledExp, reciprocalExp);
- Value resultRe = spirv::FMulOp::create(rewriter, loc, sum, sin);
- Value diff = spirv::FSubOp::create(rewriter, loc, scaledExp, reciprocalExp);
- Value resultIm = spirv::FMulOp::create(rewriter, loc, diff, cos);
- return {resultRe, resultIm};
- }
-};
-
-template <typename SinOp, typename CosOp, typename ExpOp>
-struct CosOpPattern final
- : TrigonometricOpPattern<complex::CosOp, SinOp, CosOp, ExpOp> {
- using TrigonometricOpPattern<complex::CosOp, SinOp, CosOp,
- ExpOp>::TrigonometricOpPattern;
-
- std::pair<Value, Value> combine(ConversionPatternRewriter &rewriter,
- Location loc, Value scaledExp,
- Value reciprocalExp, Value sin,
- Value cos) const override {
- Value sum = spirv::FAddOp::create(rewriter, loc, reciprocalExp, scaledExp);
- Value resultRe = spirv::FMulOp::create(rewriter, loc, sum, cos);
- Value diff = spirv::FSubOp::create(rewriter, loc, reciprocalExp, scaledExp);
- Value resultIm = spirv::FMulOp::create(rewriter, loc, diff, sin);
- return {resultRe, resultIm};
- }
};
} // namespace
@@ -479,9 +449,12 @@ void mlir::populateComplexToSPIRVPatterns(
spirv::GLFMinOp>,
SignOpPattern<spirv::CLSqrtOp, spirv::CLFAbsOp, spirv::CLFMaxOp,
spirv::CLFMinOp>,
- SinOpPattern<spirv::GLSinOp, spirv::GLCosOp, spirv::GLExpOp>,
- SinOpPattern<spirv::CLSinOp, spirv::CLCosOp, spirv::CLExpOp>,
- CosOpPattern<spirv::GLSinOp, spirv::GLCosOp, spirv::GLExpOp>,
- CosOpPattern<spirv::CLSinOp, spirv::CLCosOp, spirv::CLExpOp>>(
- typeConverter, context);
+ TrigonometricOpPattern<SinCos::Sin, spirv::GLSinOp, spirv::GLCosOp,
+ spirv::GLExpOp>,
+ TrigonometricOpPattern<SinCos::Sin, spirv::CLSinOp, spirv::CLCosOp,
+ spirv::CLExpOp>,
+ TrigonometricOpPattern<SinCos::Cos, spirv::GLSinOp, spirv::GLCosOp,
+ spirv::GLExpOp>,
+ TrigonometricOpPattern<SinCos::Cos, spirv::CLSinOp, spirv::CLCosOp,
+ spirv::CLExpOp>>(typeConverter, context);
}
More information about the Mlir-commits
mailing list