[llvm] [SPIR-V] Add lowering for G_FSINCOS (PR #179053)

Dmitry Sidorov via llvm-commits llvm-commits at lists.llvm.org
Sat Jan 31 11:58:41 PST 2026


https://github.com/MrSidims updated https://github.com/llvm/llvm-project/pull/179053

>From 1f9fc9a662e4692d90b9ecdafd20f591e04e73ab Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Sat, 31 Jan 2026 01:15:53 +0100
Subject: [PATCH 1/2] [SPIRV] Add lowering for G_FSINCOS

Use either OpenCL::sincos for compute or sequence of HLSL::sin + HLSL::cos for shader.
---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 68 +++++++++++++++++++
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp  |  3 +
 .../SPIRV/llvm-intrinsics/sincos-glsl.ll      | 35 ++++++++++
 .../SPIRV/llvm-intrinsics/sincos-opencl.ll    | 39 +++++++++++
 4 files changed, 145 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-glsl.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-opencl.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 765163fd0180b..bbbf2f4430b27 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -340,6 +340,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
                            MachineInstr &I) const;
   bool selectFrexp(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
+  bool selectSincos(Register ResVReg, const SPIRVType *ResType,
+                    MachineInstr &I) const;
   bool selectDerivativeInst(Register ResVReg, const SPIRVType *ResType,
                             MachineInstr &I, const unsigned DPdOpCode) const;
   // Utilities
@@ -982,6 +984,8 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
     return selectExtInst(ResVReg, ResType, I, CL::exp2, GL::Exp2);
   case TargetOpcode::G_FMODF:
     return selectModf(ResVReg, ResType, I);
+  case TargetOpcode::G_FSINCOS:
+    return selectSincos(ResVReg, ResType, I);
 
   case TargetOpcode::G_FLOG:
     return selectExtInst(ResVReg, ResType, I, CL::log, GL::Log);
@@ -1404,6 +1408,70 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg,
   return false;
 }
 
+bool SPIRVInstructionSelector::selectSincos(Register ResVReg,
+                                            const SPIRVType *ResType,
+                                            MachineInstr &I) const {
+  Register CosResVReg = I.getOperand(1).getReg();
+  unsigned SrcIdx = I.getNumExplicitDefs();
+  Register ResTypeReg = GR.getSPIRVTypeID(ResType);
+
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
+    // OpenCL.std sincos(x, cosval*) -> returns sin(x), writes cos(x) to ptr.
+    MachineIRBuilder MIRBuilder(I);
+    const SPIRVType *PointerType = GR.getOrCreateSPIRVPointerType(
+        ResType, MIRBuilder, SPIRV::StorageClass::Function);
+    Register PointerVReg =
+        createVirtualRegister(PointerType, &GR, MRI, MRI->getMF());
+
+    auto It = getOpVariableMBBIt(I);
+    auto MIB = BuildMI(*It->getParent(), It, It->getDebugLoc(),
+                       TII.get(SPIRV::OpVariable))
+                   .addDef(PointerVReg)
+                   .addUse(GR.getSPIRVTypeID(PointerType))
+                   .addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function))
+                   .constrainAllUses(TII, TRI, RBI);
+
+    MIB = MIB &
+          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+              .addDef(ResVReg)
+              .addUse(ResTypeReg)
+              .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
+              .addImm(CL::sincos)
+              .add(I.getOperand(SrcIdx))
+              .addUse(PointerVReg)
+              .constrainAllUses(TII, TRI, RBI);
+
+    MIB = MIB &
+          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
+              .addDef(CosResVReg)
+              .addUse(ResTypeReg)
+              .addUse(PointerVReg)
+              .constrainAllUses(TII, TRI, RBI);
+    return MIB;
+  } else if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
+    // GLSL.std.450 has no combined sincos; emit separate Sin and Cos.
+    auto MIB =
+        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+            .addDef(ResVReg)
+            .addUse(ResTypeReg)
+            .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+            .addImm(GL::Sin)
+            .add(I.getOperand(SrcIdx))
+            .constrainAllUses(TII, TRI, RBI);
+
+    MIB = MIB &
+          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+              .addDef(CosResVReg)
+              .addUse(ResTypeReg)
+              .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+              .addImm(GL::Cos)
+              .add(I.getOperand(SrcIdx))
+              .constrainAllUses(TII, TRI, RBI);
+    return MIB;
+  }
+  return false;
+}
+
 bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
                                                 const SPIRVType *ResType,
                                                 MachineInstr &I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 03d846cb90b4c..dc5906cfa9ceb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -434,10 +434,12 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
   // tighten these requirements. Many of these math functions are only legal on
   // specific bitwidths, so they are not selectable for
   // allFloatScalarsAndVectors.
+  // clang-format off
   getActionDefinitionsBuilder({G_STRICT_FSQRT,
                                G_FPOW,
                                G_FEXP,
                                G_FMODF,
+                               G_FSINCOS,
                                G_FEXP2,
                                G_FLOG,
                                G_FLOG2,
@@ -466,6 +468,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
                                G_FMAXIMUM,
                                G_INTRINSIC_ROUNDEVEN})
       .legalFor(allFloatScalarsAndVectors);
+  // clang-format on
 
   getActionDefinitionsBuilder(G_FCOPYSIGN)
       .legalForCartesianProduct(allFloatScalarsAndVectors,
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-glsl.ll
new file mode 100644
index 0000000000000..968bcc1ea6e2c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-glsl.ll
@@ -0,0 +1,35 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#Vec2FloatTy:]] = OpTypeVector %[[#FloatTy]] 2
+
+; CHECK: %[[#XParam:]] = OpFunctionParameter %[[#FloatTy]]
+; CHECK: %[[#SinRes:]] = OpExtInst %[[#FloatTy]] %[[#ExtInstId]] Sin %[[#XParam]]
+; CHECK: %[[#CosRes:]] = OpExtInst %[[#FloatTy]] %[[#ExtInstId]] Cos %[[#XParam]]
+; CHECK: %[[#Sum:]] = OpFAdd %[[#FloatTy]] %[[#SinRes]] %[[#CosRes]]
+; CHECK: OpReturnValue %[[#Sum]]
+define float @test_sincos_scalar(float %x) {
+  %result = call { float, float } @llvm.sincos.f32(float %x)
+  %sin = extractvalue { float, float } %result, 0
+  %cos = extractvalue { float, float } %result, 1
+  %sum = fadd float %sin, %cos
+  ret float %sum
+}
+
+; CHECK: %[[#XvParam:]] = OpFunctionParameter %[[#Vec2FloatTy]]
+; CHECK: %[[#SinResv:]] = OpExtInst %[[#Vec2FloatTy]] %[[#ExtInstId]] Sin %[[#XvParam]]
+; CHECK: %[[#CosResv:]] = OpExtInst %[[#Vec2FloatTy]] %[[#ExtInstId]] Cos %[[#XvParam]]
+; CHECK: %[[#Sumv:]] = OpFAdd %[[#Vec2FloatTy]] %[[#SinResv]] %[[#CosResv]]
+; CHECK: OpReturnValue %[[#Sumv]]
+define <2 x float> @test_sincos_vec2(<2 x float> %x) {
+  %result = call { <2 x float>, <2 x float> } @llvm.sincos.v2f32(<2 x float> %x)
+  %sin = extractvalue { <2 x float>, <2 x float> } %result, 0
+  %cos = extractvalue { <2 x float>, <2 x float> } %result, 1
+  %sum = fadd <2 x float> %sin, %cos
+  ret <2 x float> %sum
+}
+
+declare { float, float } @llvm.sincos.f32(float)
+declare { <2 x float>, <2 x float> } @llvm.sincos.v2f32(<2 x float>)
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-opencl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-opencl.ll
new file mode 100644
index 0000000000000..32e783af3b3cb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos-opencl.ll
@@ -0,0 +1,39 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "OpenCL.std"
+; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#FnPtrTy:]] = OpTypePointer Function %[[#FloatTy]]
+; CHECK-DAG: %[[#Vec2FloatTy:]] = OpTypeVector %[[#FloatTy]] 2
+; CHECK-DAG: %[[#FnPtrVec2Ty:]] = OpTypePointer Function %[[#Vec2FloatTy]]
+
+; CHECK: %[[#XParam:]] = OpFunctionParameter %[[#FloatTy]]
+; CHECK: %[[#Var:]] = OpVariable %[[#FnPtrTy]] Function
+; CHECK: %[[#SinRes:]] = OpExtInst %[[#FloatTy]] %[[#ExtInstId]] sincos %[[#XParam]] %[[#Var]]
+; CHECK: %[[#CosRes:]] = OpLoad %[[#FloatTy]] %[[#Var]]
+; CHECK: %[[#Sum:]] = OpFAdd %[[#FloatTy]] %[[#SinRes]] %[[#CosRes]]
+; CHECK: OpReturnValue %[[#Sum]]
+define float @test_sincos_scalar(float %x) {
+  %result = call { float, float } @llvm.sincos.f32(float %x)
+  %sin = extractvalue { float, float } %result, 0
+  %cos = extractvalue { float, float } %result, 1
+  %sum = fadd float %sin, %cos
+  ret float %sum
+}
+
+; CHECK: %[[#XvParam:]] = OpFunctionParameter %[[#Vec2FloatTy]]
+; CHECK: %[[#Varv:]] = OpVariable %[[#FnPtrVec2Ty]] Function
+; CHECK: %[[#SinResv:]] = OpExtInst %[[#Vec2FloatTy]] %[[#ExtInstId]] sincos %[[#XvParam]] %[[#Varv]]
+; CHECK: %[[#CosResv:]] = OpLoad %[[#Vec2FloatTy]] %[[#Varv]]
+; CHECK: %[[#Sumv:]] = OpFAdd %[[#Vec2FloatTy]] %[[#SinResv]] %[[#CosResv]]
+; CHECK: OpReturnValue %[[#Sumv]]
+define <2 x float> @test_sincos_vec2(<2 x float> %x) {
+  %result = call { <2 x float>, <2 x float> } @llvm.sincos.v2f32(<2 x float> %x)
+  %sin = extractvalue { <2 x float>, <2 x float> } %result, 0
+  %cos = extractvalue { <2 x float>, <2 x float> } %result, 1
+  %sum = fadd <2 x float> %sin, %cos
+  ret <2 x float> %sum
+}
+
+declare { float, float } @llvm.sincos.f32(float)
+declare { <2 x float>, <2 x float> } @llvm.sincos.v2f32(<2 x float>)

>From 40317ed4a91c43051014d1d5937884865ac5c7da Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Sat, 31 Jan 2026 20:58:24 +0100
Subject: [PATCH 2/2] format

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp   | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index bbbf2f4430b27..fd8a7d60f78e6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1459,14 +1459,15 @@ bool SPIRVInstructionSelector::selectSincos(Register ResVReg,
             .add(I.getOperand(SrcIdx))
             .constrainAllUses(TII, TRI, RBI);
 
-    MIB = MIB &
-          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-              .addDef(CosResVReg)
-              .addUse(ResTypeReg)
-              .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
-              .addImm(GL::Cos)
-              .add(I.getOperand(SrcIdx))
-              .constrainAllUses(TII, TRI, RBI);
+    MIB =
+        MIB &
+        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+            .addDef(CosResVReg)
+            .addUse(ResTypeReg)
+            .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+            .addImm(GL::Cos)
+            .add(I.getOperand(SrcIdx))
+            .constrainAllUses(TII, TRI, RBI);
     return MIB;
   }
   return false;



More information about the llvm-commits mailing list