[llvm-branch-commits] [llvm] [SPIRV] Implement spv_resource_getbasepointer for Vulkan buffers (PR #195152)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Apr 30 11:53:47 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Steven Perron (s-perron)
<details>
<summary>Changes</summary>
The new intrinsic spv_resource_getbasepointer allows retrieving the base
pointer of a resource without an index. This is necessary for resources
like ConstantBuffer<T> where you can access the top level struct of type
T.
Assisted-by: Gemini
<!-- branch-stack-start -->
-------------------------
- main
- https://github.com/llvm/llvm-project/pull/195151
- users/s-perron/constantbuffer-spirv-getbasepointer :point_left:
<sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup>
<!-- branch-stack-end -->
---
Full diff: https://github.com/llvm/llvm-project/pull/195152.diff
3 Files Affected:
- (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+12-7)
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+20-10)
- (added) llvm/test/CodeGen/SPIRV/hlsl-resources/getbasepointer.ll (+54)
``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 180a0f45874e1..cfded9596c094 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -411,6 +411,7 @@ bool isConvergenceIntrinsic(const Instruction *I) {
bool expectIgnoredInIRTranslation(const Instruction *I) {
return match(I, m_AnyIntrinsic<Intrinsic::invariant_start,
Intrinsic::spv_resource_handlefrombinding,
+ Intrinsic::spv_resource_getbasepointer,
Intrinsic::spv_resource_getpointer>());
}
@@ -1022,7 +1023,8 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
// TODO: maybe improve performance by caching demangled names
auto *II = dyn_cast<IntrinsicInst>(I);
- if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
+ if (II && (II->getIntrinsicID() == Intrinsic::spv_resource_getbasepointer ||
+ II->getIntrinsicID() == Intrinsic::spv_resource_getpointer)) {
auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType());
if (HandleType->getTargetExtName() == "spirv.Image" ||
HandleType->getTargetExtName() == "spirv.SignedImage") {
@@ -1034,12 +1036,15 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
} else if (HandleType->getTargetExtName() == "spirv.VulkanBuffer") {
// This call is supposed to index into an array
Ty = HandleType->getTypeParameter(0);
- if (Ty->isArrayTy())
- Ty = Ty->getArrayElementType();
- else {
- assert(Ty && Ty->isStructTy());
- uint32_t Index = cast<ConstantInt>(II->getOperand(1))->getZExtValue();
- Ty = cast<StructType>(Ty)->getElementType(Index);
+ if (II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
+ if (Ty->isArrayTy())
+ Ty = Ty->getArrayElementType();
+ else {
+ assert(Ty && Ty->isStructTy());
+ uint32_t Index =
+ cast<ConstantInt>(II->getOperand(1))->getZExtValue();
+ Ty = cast<StructType>(Ty)->getElementType(Index);
+ }
}
Ty = reconstitutePeeledArrayType(Ty);
} else {
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index aee3a29c6e42b..5b0204d5a2e8b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -700,6 +700,7 @@ static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
case Intrinsic::spv_radians:
case Intrinsic::spv_reflect:
case Intrinsic::spv_refract:
+ case Intrinsic::spv_resource_getbasepointer:
case Intrinsic::spv_resource_getpointer:
case Intrinsic::spv_resource_handlefrombinding:
case Intrinsic::spv_resource_handlefromimplicitbinding:
@@ -1887,7 +1888,9 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
auto *PtrDef = getVRegDef(*MRI, Ptr);
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
if (IntPtrDef &&
- IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
+ (IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getbasepointer ||
+ IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer)) {
+
Register HandleReg = IntPtrDef->getOperand(2).getReg();
SPIRVTypeInst HandleType = GR.getSPIRVTypeForVReg(HandleReg);
if (HandleType->getOpcode() == SPIRV::OpTypeImage) {
@@ -1979,7 +1982,9 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
auto *PtrDef = getVRegDef(*MRI, Ptr);
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
if (IntPtrDef &&
- IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
+ (IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getbasepointer ||
+ IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer)) {
+
Register HandleReg = IntPtrDef->getOperand(2).getReg();
Register NewHandleReg =
MRI->createVirtualRegister(MRI->getRegClass(HandleReg));
@@ -5094,6 +5099,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
case Intrinsic::spv_resource_gather:
case Intrinsic::spv_resource_gather_cmp:
return selectGatherIntrinsic(ResVReg, ResType, I);
+ case Intrinsic::spv_resource_getbasepointer:
case Intrinsic::spv_resource_getpointer: {
return selectResourceGetPointer(ResVReg, ResType, I);
}
@@ -5890,16 +5896,20 @@ bool SPIRVInstructionSelector::selectResourceGetPointer(Register &ResVReg,
assert(ResType->getOpcode() == SPIRV::OpTypePointer);
MachineIRBuilder MIRBuilder(I);
- Register IndexReg = I.getOperand(3).getReg();
Register ZeroReg =
buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I);
- BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpAccessChain))
- .addDef(ResVReg)
- .addUse(GR.getSPIRVTypeID(ResType))
- .addUse(ResourcePtr)
- .addUse(ZeroReg)
- .addUse(IndexReg)
- .constrainAllUses(TII, TRI, RBI);
+ auto MIB =
+ BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpAccessChain))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType))
+ .addUse(ResourcePtr)
+ .addUse(ZeroReg);
+
+ if (I.getNumExplicitOperands() > 3) {
+ Register IndexReg = I.getOperand(3).getReg();
+ MIB.addUse(IndexReg);
+ }
+ MIB.constrainAllUses(TII, TRI, RBI);
return true;
}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/getbasepointer.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/getbasepointer.ll
new file mode 100644
index 0000000000000..aefeea0981871
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/getbasepointer.ll
@@ -0,0 +1,54 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %}
+
+target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
+
+ at .str.1 = private unnamed_addr constant [2 x i8] c"B\00", align 1
+ at .str.2 = private unnamed_addr constant [2 x i8] c"S\00", align 1
+
+; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0
+
+; CHECK-DAG: [[ArrayType:%.+]] = OpTypeRuntimeArray [[int]]
+; CHECK-DAG: [[BufferType:%.+]] = OpTypeStruct [[ArrayType]]
+; CHECK-DAG: [[BufferPtrType:%.+]] = OpTypePointer StorageBuffer [[BufferType]]
+; CHECK-DAG: [[BufferVar:%.+]] = OpVariable [[BufferPtrType]] StorageBuffer
+
+; CHECK-DAG: [[StructType:%.+]] = OpTypeStruct [[int]] [[int]]
+; CHECK-DAG: [[StructWrapper:%.+]] = OpTypeStruct [[StructType]]
+; CHECK-DAG: [[StructPtrType:%.+]] = OpTypePointer StorageBuffer [[StructWrapper]]
+; CHECK-DAG: [[StructVar:%.+]] = OpVariable [[StructPtrType]] StorageBuffer
+
+define i32 @main() local_unnamed_addr {
+entry:
+; CHECK-DAG: [[BufferHandle:%.+]] = OpCopyObject [[BufferPtrType]] [[BufferVar]]
+; CHECK-DAG: [[StructHandle:%.+]] = OpCopyObject [[StructPtrType]] [[StructVar]]
+
+ %BufferHandle = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.1)
+ %StructHandle = tail call target("spirv.VulkanBuffer", {i32, i32}, 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBufferStruct(i32 0, i32 2, i32 1, i32 0, ptr nonnull @.str.2)
+
+; CHECK: [[AC2:%.+]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]]
+; CHECK-NOT: [[AC2]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]] %
+ %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getbasepointer.p11.tspirv.VulkanBuffer(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %BufferHandle)
+ %load2 = load i32, ptr addrspace(11) %2
+
+; CHECK: [[AC3:%.+]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]] [[index:%[0-9]+]]
+ %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %BufferHandle, i32 42)
+ %load3 = load i32, ptr addrspace(11) %3
+
+; CHECK: [[AC4:%.+]] = OpAccessChain {{.*}} [[StructHandle]] [[zero]]
+; CHECK-NOT: [[AC4]] = OpAccessChain {{.*}} [[StructHandle]] [[zero]] %
+ %4 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getbasepointer.p11.tspirv.VulkanBufferStruct(target("spirv.VulkanBuffer", {i32, i32}, 12, 0) %StructHandle)
+ %load4 = load i32, ptr addrspace(11) %4
+
+ %res1 = add i32 %load2, %load3
+ %res2 = add i32 %res1, %load4
+ ret i32 %res2
+}
+
+declare target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer(i32, i32, i32, i32, ptr)
+declare target("spirv.VulkanBuffer", {i32, i32}, 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBufferStruct(i32, i32, i32, i32, ptr)
+
+declare ptr addrspace(11) @llvm.spv.resource.getbasepointer.p11.tspirv.VulkanBuffer(target("spirv.VulkanBuffer", [0 x i32], 12, 0))
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer(target("spirv.VulkanBuffer", [0 x i32], 12, 0), i32)
+declare ptr addrspace(11) @llvm.spv.resource.getbasepointer.p11.tspirv.VulkanBufferStruct(target("spirv.VulkanBuffer", {i32, i32}, 12, 0))
``````````
</details>
https://github.com/llvm/llvm-project/pull/195152
More information about the llvm-branch-commits
mailing list