[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