From f5f1faefd4d8ecbcc652a10ee3f5224f96bfb50e Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Mon, 3 Feb 2025 16:09:00 -0500 Subject: [PATCH 1/2] [SPIRV] Handle vector load/store for RWBuffer The type inference for `spv_resource_getpointer` must look at the uses of the intrinsic. The type of the handle will be a scalar type, but it can still be read or written as a vector of that type. Fixes https://github.com/llvm/llvm-project/issues/124551. --- llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 11 ++- .../SPIRV/hlsl-resources/BufferLoadStore.ll | 86 ++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 702206b8e0dc5..e0e49f2ab1fce 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -748,7 +748,16 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { auto *ImageType = cast(II->getOperand(0)->getType()); assert(ImageType->getTargetExtName() == "spirv.Image"); - Ty = ImageType->getTypeParameter(0); + for (auto U : II->users()) { + if (auto *LD = dyn_cast(U)) { + Ty = LD->getType(); + } else if (auto *ST = dyn_cast(U)) { + Ty = ST->getAccessType(); + } else { + llvm_unreachable("Unexpected user. The only expect users of a " + "resource pointer to an image are loads and stores"); + } + } } else if (Function *CalledF = CI->getCalledFunction()) { std::string DemangledName = getOclOrSpirvBuiltinDemangledName(CalledF->getName()); diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferLoadStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferLoadStore.ll index 25dcc90cb61cd..d810ef9ccecc4 100644 --- a/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferLoadStore.ll +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferLoadStore.ll @@ -2,6 +2,7 @@ ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-library %s -o - -filetype=obj | spirv-val %} ; CHECK-DAG: [[float:%[0-9]+]] = OpTypeFloat 32 +; CHECK-DAG: [[v2float:%[0-9]+]] = OpTypeVector [[float]] 2 ; CHECK-DAG: [[v4float:%[0-9]+]] = OpTypeVector [[float]] 4 ; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0 ; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0 @@ -10,10 +11,11 @@ ; CHECK-DAG: [[twenty_three:%[0-9]+]] = OpConstant [[int]] 23 ; CHECK-DAG: [[ImageType:%[0-9]+]] = OpTypeImage [[float]] Buffer 2 0 0 2 Rgba32f ; CHECK-DAG: [[ImagePtr:%[0-9]+]] = OpTypePointer UniformConstant [[ImageType]] -; CHECK: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant +; CHECK-DAG: [[Var:%[0-9]+]] = OpVariable [[ImagePtr]] UniformConstant ; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) -define void @main() local_unnamed_addr #0 { +; CHECK: OpFunction +define void @main_scalar() local_unnamed_addr #0 { entry: ; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] %s_h.i = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false) @@ -50,6 +52,86 @@ bb_both: ret void } +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector2() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4float]] [[H]] [[one]] +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[float]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[float]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2float]] [[E0]] [[E1]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 1) + %1 = load <2 x float>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[V]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 0) + store <2 x float> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4float]] [[H]] [[twenty_three]] +; CHECK: [[E0:%[0-9]+]] = OpCompositeExtract [[float]] [[R]] 0 +; CHECK: [[E1:%[0-9]+]] = OpCompositeExtract [[float]] [[R]] 1 +; CHECK: [[V:%[0-9]+]] = OpCompositeConstruct [[v2float]] [[E0]] [[E1]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 23) + %4 = load <2 x float>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[V]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 20) + store <2 x float> %4, ptr %5, align 4 + ret void +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +; CHECK: OpFunction +define void @main_vector4() local_unnamed_addr #0 { +entry: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] + %s_h.i = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false) + +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4float]] [[H]] [[one]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 1) + %1 = load <4 x float>, ptr %0, align 4 +; CHECK: OpBranch [[bb_store:%[0-9]+]] + br label %bb_store + +; CHECK: [[bb_store]] = OpLabel +bb_store: + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[zero]] [[R]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 0) + store <4 x float> %1, ptr %2, align 4 +; CHECK: OpBranch [[bb_both:%[0-9]+]] + br label %bb_both + +; CHECK: [[bb_both]] = OpLabel +bb_both: +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: [[R:%[0-9]+]] = OpImageRead [[v4float]] [[H]] [[twenty_three]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 23) + %4 = load <4 x float>, ptr %3, align 4 + +; CHECK: [[H:%[0-9]+]] = OpLoad [[ImageType]] [[Var]] +; CHECK: OpImageWrite [[H]] [[twenty]] [[R]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %s_h.i, i32 20) + store <4 x float> %4, ptr %5, align 4 + ret void +} + ; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) declare ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32) #1 From 5d62a03a99dedfa23c5acb76c33ed5ec119c2385 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Tue, 4 Feb 2025 10:41:14 -0500 Subject: [PATCH 2/2] Remove loop --- llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index e0e49f2ab1fce..17f5c69ae0948 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -748,15 +748,10 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { auto *ImageType = cast(II->getOperand(0)->getType()); assert(ImageType->getTargetExtName() == "spirv.Image"); - for (auto U : II->users()) { - if (auto *LD = dyn_cast(U)) { - Ty = LD->getType(); - } else if (auto *ST = dyn_cast(U)) { - Ty = ST->getAccessType(); - } else { - llvm_unreachable("Unexpected user. The only expect users of a " - "resource pointer to an image are loads and stores"); - } + if (II->hasOneUse()) { + auto *U = *II->users().begin(); + Ty = cast(U)->getAccessType(); + assert(Ty && "Unable to get type for resource pointer."); } } else if (Function *CalledF = CI->getCalledFunction()) { std::string DemangledName =