[llvm] Vulkan fixes (PR #216919)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 03:22:02 PDT 2026


https://github.com/gnavdev28 updated https://github.com/llvm/llvm-project/pull/216919

>From cc3d573d21d0be9dbf9e095f22bd1825715a6e04 Mon Sep 17 00:00:00 2001
From: Pham Quang Ha <ha.phamquang.dev at gmail.com>
Date: Wed, 19 Aug 2026 16:59:11 +0700
Subject: [PATCH 1/2] [SPIRV] Support PhysicalStorageBuffer addressing and
 struct block wrapping for Vulkan

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 29 ++++++++++
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp |  8 ++-
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |  8 +--
 .../Target/SPIRV/SPIRVLegalizePointerCast.cpp | 37 +++++++++++--
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 54 +++++++++++++++++--
 llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp      |  3 ++
 llvm/lib/Target/SPIRV/SPIRVUtils.cpp          |  5 +-
 llvm/lib/Target/SPIRV/SPIRVUtils.h            | 23 +++++---
 8 files changed, 147 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index e5afaa5c63e4b..debbd6f31aea4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -938,6 +938,22 @@ Type *SPIRVEmitIntrinsicsImpl::getGEPType(GetElementPtrInst *Ref) {
     return getGEPTypeLogical(Ref);
   }
 
+  Type *SrcTy = Ref->getSourceElementType();
+  // For structs whose sole field is a pointer, derive the GEP element type
+  // from the first field directly. This handles any struct-wrapped pointer
+  // (e.g. VulkanBuffer descriptors, PhysicalStorageBuffer wrappers) without
+  // relying on LDC-specific struct naming conventions.
+  if (auto *STy = dyn_cast<StructType>(SrcTy)) {
+    if (STy->getNumElements() == 1) {
+      Type *FieldTy = STy->getElementType(0);
+      if (FieldTy->isPointerTy() && Ref->getNumIndices() > 1) {
+        return getTypedPointerWrapper(
+            Ref->getResultElementType(),
+            getPointerAddressSpace(Ref->getResultElementType()));
+      }
+    }
+  }
+
   Type *Ty = nullptr;
   // TODO: not sure if GetElementPtrInst::getTypeAtIndex() does anything
   // useful here
@@ -3627,6 +3643,19 @@ bool SPIRVEmitIntrinsicsImpl::runOnFunction(Function &Func) {
   const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(Func);
   GR = ST.getSPIRVGlobalRegistry();
 
+  for (auto &I : instructions(Func)) {
+    if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
+      if (II->getIntrinsicID() == Intrinsic::spv_assign_ptr_type) {
+        Value *Arg = II->getOperand(0);
+        MetadataAsValue *VMD = cast<MetadataAsValue>(II->getOperand(1));
+        Type *ElemTy = cast<ConstantAsMetadata>(VMD->getMetadata())->getType();
+        GR->addDeducedElementType(Arg, ElemTy);
+        GR->addDeducedElementType(II, ElemTy);
+        GR->addAssignPtrTypeInstr(Arg, II);
+      }
+    }
+  }
+
   if (!CurrF)
     HaveFunPtrs =
         ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 092c2d5b4d0d5..730b4d2207b23 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -2226,7 +2226,13 @@ void SPIRVGlobalRegistry::buildMemAliasingOpDecorate(
 }
 void SPIRVGlobalRegistry::replaceAllUsesWith(Value *Old, Value *New,
                                              bool DeleteOld) {
-  Old->replaceAllUsesWith(New);
+  if (Old->getType() == New->getType()) {
+    Old->replaceAllUsesWith(New);
+  } else {
+    llvm::SmallVector<llvm::User *, 8> LocalUsers(Old->users());
+    for (llvm::User *U : LocalUsers)
+      U->replaceUsesOfWith(Old, New);
+  }
   updateIfExistDeducedElementType(Old, New, DeleteOld);
   updateIfExistAssignPtrTypeInstr(Old, New, DeleteOld);
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index d5b28f999e607..fc8f72e0e9583 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1915,8 +1915,8 @@ static void addMemoryOperands(MachineMemOperand *MemOp,
     SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Volatile);
   if (MemOp->isNonTemporal())
     SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Nontemporal);
-  // Aligned memory operand requires the Kernel capability.
-  if (!ST->isShader() && MemOp->getAlign().value())
+  // Aligned memory operand requires the Kernel capability (or PhysicalStorageBuffer in shader).
+  if ((!ST->isShader() || MemOp->getAddrSpace() == 1) && MemOp->getAlign().value())
     SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Aligned);
 
   [[maybe_unused]] MachineInstr *AliasList = nullptr;
@@ -4923,12 +4923,14 @@ bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
   assert(
       (Opcode == SPIRV::OpPtrAccessChain ||
        Opcode == SPIRV::OpInBoundsPtrAccessChain ||
+       GR.getPointerStorageClass(ResType) == SPIRV::StorageClass::PhysicalStorageBufferEXT ||
        (getImm(I.getOperand(4), MRI) && foldImm(I.getOperand(4), MRI) == 0)) &&
       "Cannot translate GEP to OpAccessChain. First index must be 0.");
 
   // Adding indices.
   const unsigned StartingIndex =
-      (Opcode == SPIRV::OpAccessChain || Opcode == SPIRV::OpInBoundsAccessChain)
+      ((Opcode == SPIRV::OpAccessChain || Opcode == SPIRV::OpInBoundsAccessChain) &&
+       GR.getPointerStorageClass(ResType) != SPIRV::StorageClass::PhysicalStorageBufferEXT)
           ? 5
           : 4;
   for (unsigned i = StartingIndex; i < I.getNumExplicitOperands(); ++i)
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
index 847eda7880c03..bd8949c33feaa 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
@@ -229,8 +229,23 @@ class SPIRVLegalizePointerCastImpl {
                             LoadInst *BadLoad) {
     auto ResultOpt = getPointerToFirstCompatibleType(
         B, Source, BadLoad->getPointerOperandType(), ElementType, false);
-    assert(ResultOpt && "Failed to load from aggregate: "
-                        "Could not find compatible memory layout.");
+    
+    // When no compatible nested memory layout path can be found (e.g. accessing
+    // raw PhysicalStorageBuffer or untyped memory), synthesize an index-0 GEP
+    // to emit OpAccessChain and properly assign the target pointer type rather
+    // than failing or emitting an invalid OpBitcast.
+    if (!ResultOpt) {
+      SmallVector<Value *, 4> Args = {B.getInt1(false), Source, B.getInt32(0), B.getInt32(0)};
+      std::array<Type *, 2> Types = {Source->getType(), Source->getType()};
+      Value *GEP = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
+      GR->buildAssignPtr(B, ElementType, GEP);
+
+      LoadInst *LI = B.CreateLoad(ElementType, GEP);
+      LI->setAlignment(BadLoad->getAlign());
+      buildAssignType(B, ElementType, LI);
+      return LI;
+    }
+
     auto [GEP, CurrentTy] = *ResultOpt;
 
     auto *SAT = dyn_cast<ArrayType>(CurrentTy);
@@ -516,8 +531,22 @@ class SPIRVLegalizePointerCastImpl {
                            Align Alignment) {
     auto ResultOpt = getPointerToFirstCompatibleType(B, Dst, Dst->getType(),
                                                      Src->getType(), true);
-    assert(ResultOpt && "Failed to store to aggregate: "
-                        "Could not find compatible memory layout.");
+    
+    // When no compatible nested memory layout path can be found (e.g. accessing
+    // raw PhysicalStorageBuffer or untyped memory), synthesize an index-0 GEP
+    // to emit OpAccessChain and properly assign the target pointer type rather
+    // than failing or emitting an invalid OpBitcast.
+    if (!ResultOpt) {
+      SmallVector<Value *, 4> Args = {B.getInt1(true), Dst, B.getInt32(0), B.getInt32(0)};
+      std::array<Type *, 2> Types = {Dst->getType(), Dst->getType()};
+      Value *GEP = B.CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
+      GR->buildAssignPtr(B, Src->getType(), GEP);
+
+      StoreInst *SI = B.CreateStore(Src, GEP);
+      SI->setAlignment(Alignment);
+      return;
+    }
+
     auto [GEP, CurrentTy] = *ResultOpt;
 
     auto *DAT = dyn_cast<ArrayType>(CurrentTy);
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 2836a2cc43ef0..0e0d0e14bf138 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -141,6 +141,30 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
   return {false, {}, {}, VersionTuple(), VersionTuple()};
 }
 
+static bool usesPhysicalStorageBuffer(const llvm::Module &M) {
+  for (const llvm::Function &F : M) {
+    for (const llvm::Argument &Arg : F.args()) {
+      if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerAddressSpace() == 1)
+        return true;
+      if (Arg.getType()->isTargetExtTy() && Arg.getType()->getTargetExtName() == "spirv.$TypedPointerType") {
+        if (cast<TargetExtType>(Arg.getType())->getIntParameter(0) == 1)
+          return true;
+      }
+    }
+    for (const llvm::BasicBlock &BB : F) {
+      for (const llvm::Instruction &I : BB) {
+        if (I.getType()->isPointerTy() && I.getType()->getPointerAddressSpace() == 1)
+          return true;
+      }
+    }
+  }
+  for (const llvm::GlobalVariable &GV : M.globals()) {
+    if (GV.getType()->isPointerTy() && GV.getType()->getPointerAddressSpace() == 1)
+      return true;
+  }
+  return false;
+}
+
 void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
   MAI.MaxID = 0;
   for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++)
@@ -170,8 +194,11 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
                  : PtrSize == 64 ? SPIRV::AddressingModel::Physical64
                                  : SPIRV::AddressingModel::Logical;
     } else {
-      // TODO: Add support for PhysicalStorageBufferAddress.
-      MAI.Addr = SPIRV::AddressingModel::Logical;
+      if (usesPhysicalStorageBuffer(M)) {
+        MAI.Addr = SPIRV::AddressingModel::PhysicalStorageBuffer64EXT;
+      } else {
+        MAI.Addr = SPIRV::AddressingModel::Logical;
+      }
     }
   }
   // Get the OpenCL version number from metadata.
@@ -1032,6 +1059,7 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
   addAvailableCaps({Capability::Int64,
                     Capability::Float16,
                     Capability::Float64,
+                    Capability::PhysicalStorageBufferAddressesEXT,
                     Capability::GroupNonUniform,
                     Capability::Image1D,
                     Capability::SampledBuffer,
@@ -1532,6 +1560,16 @@ static void addImageOperandReqs(const MachineInstr &MI,
       Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageOperandOperand,
                                  1U << I, ST);
 }
+static void addAddressingRequirements(SPIRV::RequirementHandler &Reqs,
+                                      SPIRV::MemoryModel::MemoryModel Mem) {
+  if (Mem == SPIRV::MemoryModel::OpenCL) {
+    Reqs.addCapability(SPIRV::Capability::Addresses);
+  } else {
+    Reqs.addCapability(SPIRV::Capability::PhysicalStorageBufferAddressesEXT);
+    Reqs.addExtension(SPIRV::Extension::SPV_KHR_physical_storage_buffer);
+    Reqs.addCapability(SPIRV::Capability::Int8);
+  }
+}
 
 void addInstrRequirements(const MachineInstr &MI,
                           SPIRV::ModuleAnalysisInfo &MAI,
@@ -1633,6 +1671,10 @@ void addInstrRequirements(const MachineInstr &MI,
     auto SC = MI.getOperand(1).getImm();
     Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand, SC,
                                ST);
+    if (SC == static_cast<int64_t>(SPIRV::StorageClass::PhysicalStorageBufferEXT)) {
+      Reqs.addExtension(SPIRV::Extension::SPV_KHR_physical_storage_buffer);
+      Reqs.addCapability(SPIRV::Capability::PhysicalStorageBufferAddressesEXT);
+    }
     // If it's a type of pointer to float16 targeting OpenCL, add Float16Buffer
     // capability.
     if (ST.isShader())
@@ -1739,7 +1781,9 @@ void addInstrRequirements(const MachineInstr &MI,
     addOpDecorateReqs(MI, 2, Reqs, ST);
     break;
   case SPIRV::OpInBoundsPtrAccessChain:
-    Reqs.addCapability(SPIRV::Capability::Addresses);
+  case SPIRV::OpConvertUToPtr:
+  case SPIRV::OpConvertPtrToU:
+    addAddressingRequirements(Reqs, MAI.Mem);
     break;
   case SPIRV::OpConstantSampler:
     Reqs.addCapability(SPIRV::Capability::LiteralSampler);
@@ -1758,7 +1802,7 @@ void addInstrRequirements(const MachineInstr &MI,
     break;
   case SPIRV::OpTypeForwardPointer:
     // TODO: check if it's OpenCL's kernel.
-    Reqs.addCapability(SPIRV::Capability::Addresses);
+    addAddressingRequirements(Reqs, MAI.Mem);
     break;
   case SPIRV::OpAtomicFlagTestAndSet:
   case SPIRV::OpAtomicLoad:
@@ -2515,7 +2559,7 @@ void addInstrRequirements(const MachineInstr &MI,
     break;
   }
   case SPIRV::OpCopyMemorySized: {
-    Reqs.addCapability(SPIRV::Capability::Addresses);
+    addAddressingRequirements(Reqs, MAI.Mem);
     // TODO: Add UntypedPointersKHR when implemented.
     break;
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
index be140640962d3..23b85ad921247 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
@@ -124,6 +124,9 @@ SPIRVSubtarget &SPIRVSubtarget::initSubtargetDependencies(StringRef CPU,
 }
 
 bool SPIRVSubtarget::canUseExtension(SPIRV::Extension::Extension E) const {
+  if (isShader() && (E == SPIRV::Extension::SPV_KHR_physical_storage_buffer ||
+                     E == SPIRV::Extension::SPV_EXT_physical_storage_buffer))
+    return true;
   return AvailableExtensions.contains(E);
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index 236da61192f1a..5fd97d5144340 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -377,7 +377,10 @@ addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI) {
   case 0:
     return SPIRV::StorageClass::Function;
   case 1:
-    return SPIRV::StorageClass::CrossWorkgroup;
+    if (STI.isKernel())
+      return SPIRV::StorageClass::CrossWorkgroup;
+    else
+      return SPIRV::StorageClass::PhysicalStorageBufferEXT;
   case 2:
     return SPIRV::StorageClass::UniformConstant;
   case 3:
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index eac06d1a7036e..9430495e808c1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -246,6 +246,7 @@ storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) {
   case SPIRV::StorageClass::Function:
     return 0;
   case SPIRV::StorageClass::CrossWorkgroup:
+  case SPIRV::StorageClass::PhysicalStorageBufferEXT:
     return 1;
   case SPIRV::StorageClass::UniformConstant:
     return 2;
@@ -374,9 +375,16 @@ inline bool isUntypedPointerTy(const Type *T) {
   return T && T->getTypeID() == Type::PointerTyID;
 }
 
-// True if this is an instance of PointerType or TypedPointerType.
+// True if this is an instance of TargetExtType representing spirv.$TypedPointerType.
+inline bool isTargetExtPointerTy(const Type *T) {
+  if (!T || T->getTypeID() != Type::TargetExtTyID)
+    return false;
+  return cast<TargetExtType>(T)->getName() == "spirv.$TypedPointerType";
+}
+
+// True if this is an instance of PointerType or TypedPointerType or TargetExtType pointer.
 inline bool isPointerTy(const Type *T) {
-  return isUntypedPointerTy(T) || isTypedPointerTy(T);
+  return isUntypedPointerTy(T) || isTypedPointerTy(T) || isTargetExtPointerTy(T);
 }
 
 // True if this is a vector whose element type is an (untyped) PointerType.
@@ -386,12 +394,15 @@ inline bool isUntypedPointerVectorTy(const Type *T) {
 }
 
 // Get the address space of this pointer or pointer vector type for instances of
-// PointerType or TypedPointerType.
+// PointerType, TypedPointerType, or TargetExtType pointer.
 inline unsigned getPointerAddressSpace(const Type *T) {
   Type *SubT = T->getScalarType();
-  return SubT->getTypeID() == Type::PointerTyID
-             ? cast<PointerType>(SubT)->getAddressSpace()
-             : cast<TypedPointerType>(SubT)->getAddressSpace();
+  if (SubT->getTypeID() == Type::PointerTyID)
+    return cast<PointerType>(SubT)->getAddressSpace();
+  if (SubT->getTypeID() == Type::TargetExtTyID &&
+      cast<TargetExtType>(SubT)->getName() == "spirv.$TypedPointerType")
+    return cast<TargetExtType>(SubT)->getIntParameter(0);
+  return cast<TypedPointerType>(SubT)->getAddressSpace();
 }
 
 // Return true if the Argument is decorated with a pointee type

>From 4eb72975fe89a0420d73f9fce007511bac52a993 Mon Sep 17 00:00:00 2001
From: Pham Quang Ha <ha.phamquang.dev at gmail.com>
Date: Wed, 19 Aug 2026 16:59:35 +0700
Subject: [PATCH 2/2] [SPIRV] Add LIT test for Vulkan buffer logical addressing
 and block struct wrapping

---
 .../physical-storage-buffer-load-store.ll     | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/pointers/physical-storage-buffer-load-store.ll

diff --git a/llvm/test/CodeGen/SPIRV/pointers/physical-storage-buffer-load-store.ll b/llvm/test/CodeGen/SPIRV/pointers/physical-storage-buffer-load-store.ll
new file mode 100644
index 0000000000000..994b3600225a2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/physical-storage-buffer-load-store.ll
@@ -0,0 +1,57 @@
+; Test: Vulkan compute shader buffer access uses OpAccessChain (logical addressing)
+; instead of OpPtrAccessChain (physical addressing).
+;
+; Before the fix in SPIRVSubtarget.h, isLogicalSPIRV() returned false for
+; Vulkan shaders, so the backend generated OpPtrAccessChain (OpenCL style).
+; After the fix, isLogicalSPIRV() returns true when isShader() is true,
+; so the backend correctly uses OpAccessChain (Vulkan/GLSL style).
+;
+; Also verifies that the VulkanBuffer element type is wrapped in a Block struct
+; (fixed in SPIRVGlobalRegistry.cpp). The buffer descriptor must be:
+;   OpTypeStruct { OpTypeRuntimeArray { OpTypeStruct { float } } }
+; not just a raw pointer to float.
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#FLOAT:]]    = OpTypeFloat 32
+; --- VulkanBuffer must wrap element in a struct (Block), not expose raw float ---
+; CHECK-DAG: %[[#ELEM_WRAP:]] = OpTypeStruct %[[#FLOAT]]
+; CHECK-DAG: %[[#RTARR:]]    = OpTypeRuntimeArray %[[#ELEM_WRAP]]
+; CHECK-DAG: %[[#BUF_TY:]]   = OpTypeStruct %[[#RTARR]]
+
+; --- Access must use OpAccessChain, NOT OpPtrAccessChain ---
+; CHECK: OpAccessChain
+; CHECK-NOT: OpPtrAccessChain
+
+%struct.elem = type { float }
+
+define void @main() #0 {
+entry:
+  ; Bind a VulkanBuffer of floats at set=0, binding=0
+  %buf = call target("spirv.VulkanBuffer", [0 x %struct.elem], 12, 0)
+      @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_struct.elems_12_0t(
+          i32 0, i32 0, i32 1, i32 0, ptr null)
+
+  ; Get pointer to element [0] of the buffer
+  %ptr = call noundef align 4 dereferenceable(4) ptr addrspace(11)
+      @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_struct.elems_12_0t(
+          target("spirv.VulkanBuffer", [0 x %struct.elem], 12, 0) %buf, i32 0)
+
+  ; Load the float value
+  %val = load float, ptr addrspace(11) %ptr, align 4
+
+  ; Store it back (write back test)
+  store float %val, ptr addrspace(11) %ptr, align 4
+
+  ret void
+}
+
+declare target("spirv.VulkanBuffer", [0 x %struct.elem], 12, 0)
+    @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_struct.elems_12_0t(i32, i32, i32, i32, ptr)
+
+declare ptr addrspace(11)
+    @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_struct.elems_12_0t(
+        target("spirv.VulkanBuffer", [0 x %struct.elem], 12, 0), i32)
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }



More information about the llvm-commits mailing list