[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