[llvm-branch-commits] [llvm] [SPIRV] Implement the int_spv_resource_calculate_lod* IntrinsicsSPIRV (PR #188337)
Steven Perron via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Mar 25 11:52:25 PDT 2026
https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/188337
>From 9015e85b98b91de53a02812f3b516081f9263840 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 10 Mar 2026 15:30:02 -0400
Subject: [PATCH 1/2] [SPIRV] Implement the int_spv_resource_calculate_lod*
IntrinsicsSPIRV
Implements intrinsics used to get the level-of-detail given a texture,
sampler, and a coordinate. It will be used to implement the
corresponding HLSL methods.
---
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 10 +++
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 70 +++++++++++++++++++
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 14 +++-
.../hlsl-resources/CalculateLevelOfDetail.ll | 39 +++++++++++
4 files changed, 131 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index da91ffe31fd72..67bf16b45dd55 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -270,6 +270,16 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
llvm_float_ty, llvm_any_ty],
[IntrReadMem]>;
+ def int_spv_resource_calculate_lod
+ : DefaultAttrsIntrinsic<[llvm_any_ty],
+ [llvm_any_ty, llvm_any_ty, llvm_any_ty],
+ [IntrReadMem]>;
+
+ def int_spv_resource_calculate_lod_unclamped
+ : DefaultAttrsIntrinsic<[llvm_any_ty],
+ [llvm_any_ty, llvm_any_ty, llvm_any_ty],
+ [IntrReadMem]>;
+
def int_spv_resource_gather
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 31421e21ecde6..c448e6d07805a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -379,6 +379,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
MachineInstr &I) const;
bool selectSampleBasicIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
MachineInstr &I) const;
+ bool selectCalculateLodIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
+ MachineInstr &I) const;
bool selectSampleBiasIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
MachineInstr &I) const;
bool selectSampleGradIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
@@ -4504,6 +4506,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
case Intrinsic::spv_resource_load_level: {
return selectLoadLevelIntrinsic(ResVReg, ResType, I);
}
+ case Intrinsic::spv_resource_calculate_lod:
+ case Intrinsic::spv_resource_calculate_lod_unclamped:
+ return selectCalculateLodIntrinsic(ResVReg, ResType, I);
case Intrinsic::spv_resource_sample:
case Intrinsic::spv_resource_sample_clamp:
return selectSampleBasicIntrinsic(ResVReg, ResType, I);
@@ -4815,6 +4820,71 @@ bool SPIRVInstructionSelector::generateSampleImage(
return true;
}
+bool SPIRVInstructionSelector::selectCalculateLodIntrinsic(
+ Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
+ Register ImageReg = I.getOperand(2).getReg();
+ Register SamplerReg = I.getOperand(3).getReg();
+ Register CoordinateReg = I.getOperand(4).getReg();
+
+ auto *ImageDef = dyn_cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
+ if (!ImageDef)
+ return false;
+ Register NewImageReg = MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
+ if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
+ *ImageDef, I)) {
+ return false;
+ }
+
+ auto *SamplerDef = dyn_cast<GIntrinsic>(getVRegDef(*MRI, SamplerReg));
+ if (!SamplerDef)
+ return false;
+ Register NewSamplerReg =
+ MRI->createVirtualRegister(MRI->getRegClass(SamplerReg));
+ if (!loadHandleBeforePosition(
+ NewSamplerReg, GR.getSPIRVTypeForVReg(SamplerReg), *SamplerDef, I)) {
+ return false;
+ }
+
+ MachineIRBuilder MIRBuilder(I);
+ SPIRVTypeInst SampledImageType = GR.getOrCreateOpTypeSampledImage(
+ GR.getSPIRVTypeForVReg(ImageReg), MIRBuilder);
+ Register SampledImageReg =
+ MRI->createVirtualRegister(GR.getRegClass(SampledImageType));
+
+ BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpSampledImage))
+ .addDef(SampledImageReg)
+ .addUse(GR.getSPIRVTypeID(SampledImageType))
+ .addUse(NewImageReg)
+ .addUse(NewSamplerReg)
+ .constrainAllUses(TII, TRI, RBI);
+
+ SPIRVTypeInst Vec2Ty = GR.getOrCreateSPIRVVectorType(ResType, 2, I, TII);
+ Register QueryResultReg = MRI->createVirtualRegister(GR.getRegClass(Vec2Ty));
+
+ BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpImageQueryLod))
+ .addDef(QueryResultReg)
+ .addUse(GR.getSPIRVTypeID(Vec2Ty))
+ .addUse(SampledImageReg)
+ .addUse(CoordinateReg)
+ .constrainAllUses(TII, TRI, RBI);
+
+ unsigned ExtractedIndex =
+ cast<GIntrinsic>(I).getIntrinsicID() ==
+ Intrinsic::spv_resource_calculate_lod_unclamped
+ ? 1
+ : 0;
+
+ MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(),
+ TII.get(SPIRV::OpCompositeExtract))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType))
+ .addUse(QueryResultReg)
+ .addImm(ExtractedIndex);
+
+ MIB.constrainAllUses(TII, TRI, RBI);
+ return true;
+}
+
bool SPIRVInstructionSelector::selectSampleBasicIntrinsic(
Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
Register ImageReg = I.getOperand(2).getReg();
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 6bd2eb552a936..ad2b79b6fe63c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -947,8 +947,8 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
Capability::StorageBufferArrayDynamicIndexing,
Capability::StorageImageArrayDynamicIndexing,
Capability::DerivativeControl, Capability::MinLod,
- Capability::ImageGatherExtended, Capability::Addresses,
- Capability::VulkanMemoryModelKHR});
+ Capability::ImageQuery, Capability::ImageGatherExtended,
+ Capability::Addresses, Capability::VulkanMemoryModelKHR});
// Became core in Vulkan 1.2
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 5))) {
@@ -1719,6 +1719,16 @@ void addInstrRequirements(const MachineInstr &MI,
case SPIRV::OpGroupNonUniformQuadSwap:
Reqs.addCapability(SPIRV::Capability::GroupNonUniformQuad);
break;
+ case SPIRV::OpImageQueryLod:
+ Reqs.addCapability(SPIRV::Capability::ImageQuery);
+ break;
+ case SPIRV::OpImageQuerySize:
+ case SPIRV::OpImageQuerySizeLod:
+ case SPIRV::OpImageQueryLevels:
+ case SPIRV::OpImageQuerySamples:
+ if (ST.isShader())
+ Reqs.addCapability(SPIRV::Capability::ImageQuery);
+ break;
case SPIRV::OpImageQueryFormat: {
Register ResultReg = MI.getOperand(0).getReg();
const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
new file mode 100644
index 0000000000000..fa7d6982f6b24
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
@@ -0,0 +1,39 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+
+define void @main() {
+entry:
+ %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+ %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res_vec:[0-9]+]] = OpImageQueryLod %[[v2float]] %[[si]] %[[coord0]]
+; CHECK: %[[res0:[0-9]+]] = OpCompositeExtract %[[float]] %[[res_vec]] 0
+ %res0 = call float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res_vec2:[0-9]+]] = OpImageQueryLod %[[v2float]] %[[si2]] %[[coord0]]
+; CHECK: %[[res1:[0-9]+]] = OpCompositeExtract %[[float]] %[[res_vec2]] 1
+ %res1 = call float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>)
+
+ ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+declare float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
>From e4dded4fb5a3c5f61fd540e77a039e234507b4ee Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Wed, 25 Mar 2026 14:52:11 -0400
Subject: [PATCH 2/2] Fix up test.
---
.../CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
index fa7d6982f6b24..0cc824bfd9891 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/CalculateLevelOfDetail.ll
@@ -11,7 +11,7 @@
@.str = private unnamed_addr constant [4 x i8] c"img\00", align 1
@.str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
-define void @main() {
+define void @main() #0 {
entry:
%img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
%sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
@@ -33,7 +33,4 @@ entry:
ret void
}
-declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
-declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
-declare float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
-declare float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+attributes #0 = { "hlsl.shader"="pixel" }
More information about the llvm-branch-commits
mailing list