[llvm] [SPIR-V] Add lowering for G_FSINCOS (PR #179053)
Dmitry Sidorov via llvm-commits
llvm-commits at lists.llvm.org
Sat Jan 31 09:03:20 PST 2026
https://github.com/MrSidims created https://github.com/llvm/llvm-project/pull/179053
Use either OpenCL::sincos for compute or sequence of HLSL::sin + HLSL::cos for shader.
>From 3f46b300a62f686068c5f634e5382678aabd4549 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] [SPIRV] Add lowering for G_FSINCOS
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 69 +++++++++++
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 1 +
.../CodeGen/SPIRV/llvm-intrinsics/sincos.ll | 117 ++++++++++++++++++
3 files changed, 187 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 765163fd0180b..a9bdfc9e4ee18 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,71 @@ bool SPIRVInstructionSelector::selectFrexp(Register ResVReg,
return false;
}
+bool SPIRVInstructionSelector::selectSincos(Register ResVReg,
+ const SPIRVType *ResType,
+ MachineInstr &I) const {
+ // G_FSINCOS has 2 defs (sin, cos) and 1 source operand.
+ // Operand 0: sin result (ResVReg), Operand 1: cos result, Operand 2: source.
+ Register CosResVReg = I.getOperand(1).getReg();
+ unsigned SrcIdx = I.getNumExplicitDefs();
+
+ 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(GR.getSPIRVTypeID(ResType))
+ .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(GR.getSPIRVTypeID(ResType))
+ .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(GR.getSPIRVTypeID(ResType))
+ .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(GR.getSPIRVTypeID(ResType))
+ .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..5ab77b3f5ec3c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -438,6 +438,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
G_FPOW,
G_FEXP,
G_FMODF,
+ G_FSINCOS,
G_FEXP2,
G_FLOG,
G_FLOG2,
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos.ll
new file mode 100644
index 0000000000000..2b1540d1dd725
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/sincos.ll
@@ -0,0 +1,117 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-OCL
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s --check-prefix=CHECK-GLSL
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-OCL-DAG: %[[#extinst_id:]] = OpExtInstImport "OpenCL.std"
+; CHECK-OCL-DAG: %[[#float_ty:]] = OpTypeFloat 32
+; CHECK-OCL-DAG: %[[#fn_ptr_ty:]] = OpTypePointer Function %[[#float_ty]]
+; CHECK-OCL-DAG: %[[#vec2_float_ty:]] = OpTypeVector %[[#float_ty]] 2
+; CHECK-OCL-DAG: %[[#fn_ptr_vec2_ty:]] = OpTypePointer Function %[[#vec2_float_ty]]
+
+; CHECK-GLSL-DAG: %[[#extinst_id:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-GLSL-DAG: %[[#float_ty:]] = OpTypeFloat 32
+; CHECK-GLSL-DAG: %[[#vec2_float_ty:]] = OpTypeVector %[[#float_ty]] 2
+
+;; Scalar f32 sincos
+
+; CHECK-OCL: %[[#x_param:]] = OpFunctionParameter %[[#float_ty]]
+; CHECK-OCL: %[[#var:]] = OpVariable %[[#fn_ptr_ty]] Function
+; CHECK-OCL: %[[#sin_res:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] sincos %[[#x_param]] %[[#var]]
+; CHECK-OCL: %[[#cos_res:]] = OpLoad %[[#float_ty]] %[[#var]]
+; CHECK-OCL: OpReturnValue %[[#sin_res]]
+define float @test_sincos_scalar_sin(float %x) {
+ %result = call { float, float } @llvm.sincos.f32(float %x)
+ %sin = extractvalue { float, float } %result, 0
+ %cos = extractvalue { float, float } %result, 1
+ ret float %sin
+}
+
+; CHECK-OCL: %[[#x2_param:]] = OpFunctionParameter %[[#float_ty]]
+; CHECK-OCL: %[[#var2:]] = OpVariable %[[#fn_ptr_ty]] Function
+; CHECK-OCL: %[[#sin_res2:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] sincos %[[#x2_param]] %[[#var2]]
+; CHECK-OCL: %[[#cos_res2:]] = OpLoad %[[#float_ty]] %[[#var2]]
+; CHECK-OCL: OpReturnValue %[[#cos_res2]]
+define float @test_sincos_scalar_cos(float %x) {
+ %result = call { float, float } @llvm.sincos.f32(float %x)
+ %sin = extractvalue { float, float } %result, 0
+ %cos = extractvalue { float, float } %result, 1
+ ret float %cos
+}
+
+;; Vector <2 x f32> sincos
+
+; CHECK-OCL: %[[#xv_param:]] = OpFunctionParameter %[[#vec2_float_ty]]
+; CHECK-OCL: %[[#varv:]] = OpVariable %[[#fn_ptr_vec2_ty]] Function
+; CHECK-OCL: %[[#sin_resv:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] sincos %[[#xv_param]] %[[#varv]]
+; CHECK-OCL: %[[#cos_resv:]] = OpLoad %[[#vec2_float_ty]] %[[#varv]]
+; CHECK-OCL: OpReturnValue %[[#sin_resv]]
+define <2 x float> @test_sincos_vec2_sin(<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
+ ret <2 x float> %sin
+}
+
+; CHECK-OCL: %[[#xv2_param:]] = OpFunctionParameter %[[#vec2_float_ty]]
+; CHECK-OCL: %[[#varv2:]] = OpVariable %[[#fn_ptr_vec2_ty]] Function
+; CHECK-OCL: %[[#sin_resv2:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] sincos %[[#xv2_param]] %[[#varv2]]
+; CHECK-OCL: %[[#cos_resv2:]] = OpLoad %[[#vec2_float_ty]] %[[#varv2]]
+; CHECK-OCL: OpReturnValue %[[#cos_resv2]]
+define <2 x float> @test_sincos_vec2_cos(<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
+ ret <2 x float> %cos
+}
+
+;; GLSL path: separate Sin and Cos
+
+; CHECK-GLSL: %[[#xg_param:]] = OpFunctionParameter %[[#float_ty]]
+; CHECK-GLSL: %[[#sin_resg:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] Sin %[[#xg_param]]
+; CHECK-GLSL: %[[#cos_resg:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] Cos %[[#xg_param]]
+; CHECK-GLSL: OpReturnValue %[[#sin_resg]]
+define float @test_sincos_glsl_sin(float %x) {
+ %result = call { float, float } @llvm.sincos.f32(float %x)
+ %sin = extractvalue { float, float } %result, 0
+ %cos = extractvalue { float, float } %result, 1
+ ret float %sin
+}
+
+; CHECK-GLSL: %[[#xg2_param:]] = OpFunctionParameter %[[#float_ty]]
+; CHECK-GLSL: %[[#sin_resg2:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] Sin %[[#xg2_param]]
+; CHECK-GLSL: %[[#cos_resg2:]] = OpExtInst %[[#float_ty]] %[[#extinst_id]] Cos %[[#xg2_param]]
+; CHECK-GLSL: OpReturnValue %[[#cos_resg2]]
+define float @test_sincos_glsl_cos(float %x) {
+ %result = call { float, float } @llvm.sincos.f32(float %x)
+ %sin = extractvalue { float, float } %result, 0
+ %cos = extractvalue { float, float } %result, 1
+ ret float %cos
+}
+
+;; GLSL path: vector <2 x f32>
+
+; CHECK-GLSL: %[[#xgv_param:]] = OpFunctionParameter %[[#vec2_float_ty]]
+; CHECK-GLSL: %[[#sin_resgv:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] Sin %[[#xgv_param]]
+; CHECK-GLSL: %[[#cos_resgv:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] Cos %[[#xgv_param]]
+; CHECK-GLSL: OpReturnValue %[[#sin_resgv]]
+define <2 x float> @test_sincos_glsl_vec2_sin(<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
+ ret <2 x float> %sin
+}
+
+; CHECK-GLSL: %[[#xgv2_param:]] = OpFunctionParameter %[[#vec2_float_ty]]
+; CHECK-GLSL: %[[#sin_resgv2:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] Sin %[[#xgv2_param]]
+; CHECK-GLSL: %[[#cos_resgv2:]] = OpExtInst %[[#vec2_float_ty]] %[[#extinst_id]] Cos %[[#xgv2_param]]
+; CHECK-GLSL: OpReturnValue %[[#cos_resgv2]]
+define <2 x float> @test_sincos_glsl_vec2_cos(<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
+ ret <2 x float> %cos
+}
+
+declare { float, float } @llvm.sincos.f32(float)
+declare { <2 x float>, <2 x float> } @llvm.sincos.v2f32(<2 x float>)
More information about the llvm-commits
mailing list