[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