[Mlir-commits] [mlir] [mlir][SPIR-V] Add ComplexToSPIRV lowering for complex.sin/cos (PR #231873)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Oct 14 04:37:24 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Arseniy Obolenskiy (aobolensk)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/231873.diff


2 Files Affected:

- (modified) mlir/lib/Conversion/ComplexToSPIRV/ComplexToSPIRV.cpp (+89-1) 
- (modified) mlir/test/Conversion/ComplexToSPIRV/complex-to-spirv.mlir (+100) 


``````````diff
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>
+
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/231873


More information about the Mlir-commits mailing list