[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