[llvm] [DirectX] Add support for heap resources to `DXILResourceMap` (PR #216454)
Helena Kotas via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 21:41:01 PDT 2026
https://github.com/hekota created https://github.com/llvm/llvm-project/pull/216454
- `DXILResourceMap` now handles a new `llvm.dx.resource.handlefromheap` intrisics and adds the heap resources to the resource map.
- A new member `HeapResourceID` has been added to `ResourceInfo` to distinguish between heap resource instances created from different indices. The `HeapResourceID` is unique for each heap index `Value*`, so multiple handle creation calls using the same index `Value*` resolve to the same resource.
- Heap resources do not have register bindings, so the `Binding` member on `ResourceInfo` is now optional.
- All places that were always expecting binding are updated to handle heap resources. In most cases that means skipping them, such as when generating DXIL resource metadata, creating PSV resource entries or pretty-printing the resource table comment for the module disassembly output.
- Diagnostics of conflicting increment and decrement operations now works on heap resources.
>From 1815a912c5294ba4e68018dd8251dce4ca2bc583 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 14 Aug 2026 18:24:20 -0700
Subject: [PATCH] [DirectX] Add support for heap resources to
`DXILResourceMap`.
- `DXILResourceMap` now handles a new `llvm.dx.resource.handlefromheap` intrisics
and adds the heap resources to the resource map.
- A new member `HeapResourceID` has been added to `ResourceInfo` to distinguish
between heap resource instances created from different indices. The `HeapResourceID`
is unique for each heap index `Value*`, so multiple handle creation calls using
the same index `Value*` resolve to the same resource.
- Heap resources do not have register bindings, so the `Binding` member
on `ResourceInfo` is now optional.
- All places that were always expecting binding are updated to handle
heap resources. In most cases that means skipping them, such as when
generating DXIL resource metadata, creating PSV resource entries or
pretty-printing the resource table comment for the module disassembly
output.
- Diagnostics of conflicting increment and decrement operations now works on heap
resources.
---
llvm/include/llvm/Analysis/DXILResource.h | 31 +++-
llvm/include/llvm/IR/IntrinsicsDirectX.td | 13 +-
llvm/lib/Analysis/DXILResource.cpp | 64 +++++--
.../lib/Target/DirectX/DXContainerGlobals.cpp | 8 +
llvm/lib/Target/DirectX/DXILPrettyPrinter.cpp | 2 +
.../Target/DirectX/DXILTranslateMetadata.cpp | 14 +-
.../Analysis/DXILResource/buffer-fromheap.ll | 174 ++++++++++++++++++
.../resource_from_heap_counter_error.ll | 12 ++
8 files changed, 290 insertions(+), 28 deletions(-)
create mode 100644 llvm/test/Analysis/DXILResource/buffer-fromheap.ll
create mode 100644 llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index b0ac8f94875a9..f68e5ab34c7b4 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -21,6 +21,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DXILABI.h"
#include <cstdint>
+#include <optional>
namespace llvm {
class CallInst;
@@ -402,7 +403,8 @@ class ResourceInfo {
};
private:
- ResourceBinding Binding;
+ std::optional<ResourceBinding> Binding;
+ uint32_t HeapResourceID = -1U;
TargetExtType *HandleTy;
StringRef Name;
GlobalVariable *Symbol = nullptr;
@@ -415,16 +417,30 @@ class ResourceInfo {
ResourceInfo(uint32_t Space, uint32_t LowerBound, uint32_t Size,
TargetExtType *HandleTy, StringRef Name = "",
GlobalVariable *Symbol = nullptr)
- : Binding{0, Space, LowerBound, Size}, HandleTy(HandleTy), Name(Name),
- Symbol(Symbol) {}
+ : Binding{ResourceBinding{0, Space, LowerBound, Size}},
+ HandleTy(HandleTy), Name(Name), Symbol(Symbol) {}
- void setBindingID(unsigned ID) { Binding.BindingID = ID; }
+ ResourceInfo(uint32_t HeapResourceID, TargetExtType *HandleTy)
+ : Binding{std::nullopt}, HeapResourceID(HeapResourceID),
+ HandleTy(HandleTy), Name(""), Symbol(nullptr) {}
+
+ bool hasBinding() const { return Binding.has_value(); }
+ void setBindingID(unsigned ID) {
+ assert(hasBinding() && "Resource does not have a binding");
+ Binding->BindingID = ID;
+ }
bool hasCounter() const {
return CounterDirection != ResourceCounterDirection::Unknown;
}
- const ResourceBinding &getBinding() const { return Binding; }
+ const ResourceBinding &getBinding() const {
+ assert(hasBinding() && "Resource does not have a binding");
+ return Binding.value();
+ }
+
+ uint32_t getSize() const { return Binding ? Binding->Size : 1; }
+
TargetExtType *getHandleTy() const { return HandleTy; }
StringRef getName() const { return Name; }
@@ -436,8 +452,9 @@ class ResourceInfo {
getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const;
bool operator==(const ResourceInfo &RHS) const {
- return std::tie(Binding, HandleTy, Symbol, Name) ==
- std::tie(RHS.Binding, RHS.HandleTy, RHS.Symbol, RHS.Name);
+ return std::tie(Binding, HandleTy, Symbol, Name, HeapResourceID) ==
+ std::tie(RHS.Binding, RHS.HandleTy, RHS.Symbol, RHS.Name,
+ RHS.HeapResourceID);
}
bool operator!=(const ResourceInfo &RHS) const { return !(*this == RHS); }
bool operator<(const ResourceInfo &RHS) const {
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 9c9b2032035e3..e3b8a4e57ddc7 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -19,8 +19,7 @@ def int_dx_flattened_thread_id_in_group : Intrinsic<[llvm_i32_ty], [], [IntrNoMe
// Create resource handle given binding information. Returns a `target("dx.")`
// type appropriate for the kind of resource given a register space ID, lower
-// bound and range size of the binding, as well as an index and an indicator
-// whether that index may be non-uniform.
+// bound and range size of the binding.
def int_dx_resource_handlefrombinding
: DefaultAttrsIntrinsic<
[llvm_any_ty],
@@ -36,6 +35,16 @@ def int_dx_resource_handlefromimplicitbinding
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty],
[IntrNoMem]>;
+// Create resource handle from a descriptor heap. Returns a `target("dx.")`
+// type appropriate for the kind of resource given a heap index, a boolean
+// indicator whether the index is for a CVB/SRV/UAV heap or a Sampler
+// heap.
+def int_dx_resource_handlefromheap
+ : DefaultAttrsIntrinsic<
+ [llvm_any_ty],
+ [llvm_i32_ty, llvm_i1_ty],
+ [IntrNoMem]>;
+
def int_dx_resource_getpointer
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_any_ty],
[IntrConvergent, IntrNoMem]>;
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 413521ac64d61..d5339d84d0096 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -660,7 +660,7 @@ void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {
GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) {
assert(!Symbol && "Symbol has already been created");
Type *ResTy = Ty;
- int64_t Size = Binding.Size;
+ int64_t Size = getSize();
if (Size != 1)
// unbounded arrays are represented as zero-sized arrays in LLVM IR
ResTy = ArrayType::get(Ty, Size == ~0u ? 0 : Size);
@@ -672,6 +672,8 @@ GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) {
MDTuple *ResourceInfo::getAsMetadata(Module &M,
dxil::ResourceTypeInfo &RTI) const {
+ assert(hasBinding() && "Resource must not be from heap to get metadata");
+
LLVMContext &Ctx = M.getContext();
const DataLayout &DL = M.getDataLayout();
@@ -688,13 +690,13 @@ MDTuple *ResourceInfo::getAsMetadata(Module &M,
Constant::getIntegerValue(I1Ty, APInt(1, V)));
};
- MDVals.push_back(getIntMD(Binding.BindingID));
+ MDVals.push_back(getIntMD(Binding->BindingID));
assert(Symbol && "Cannot yet create useful resource metadata without symbol");
MDVals.push_back(ValueAsMetadata::get(Symbol));
MDVals.push_back(MDString::get(Ctx, Name));
- MDVals.push_back(getIntMD(Binding.Space));
- MDVals.push_back(getIntMD(Binding.LowerBound));
- MDVals.push_back(getIntMD(Binding.Size == 0 ? ~0u : Binding.Size));
+ MDVals.push_back(getIntMD(Binding->Space));
+ MDVals.push_back(getIntMD(Binding->LowerBound));
+ MDVals.push_back(getIntMD(Binding->Size == 0 ? ~0u : Binding->Size));
if (RTI.isCBuffer()) {
MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));
@@ -799,11 +801,15 @@ void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,
OS << "\n";
}
- OS << " Binding:\n"
- << " Binding ID: " << Binding.BindingID << "\n"
- << " Space: " << Binding.Space << "\n"
- << " Lower Bound: " << Binding.LowerBound << "\n"
- << " Size: " << Binding.Size << "\n";
+ if (hasBinding()) {
+ OS << " Binding:\n"
+ << " Binding ID: " << Binding->BindingID << "\n"
+ << " Space: " << Binding->Space << "\n"
+ << " Lower Bound: " << Binding->LowerBound << "\n"
+ << " Size: " << Binding->Size << "\n";
+ } else {
+ OS << " HeapIndexID: " << HeapResourceID << "\n";
+ }
OS << " Globally Coherent: " << GloballyCoherent << "\n";
OS << " Has Atomic64 Use: " << HasAtomic64Use << "\n";
@@ -865,6 +871,12 @@ void DXILResourceMap::populateResourceInfos(Module &M,
DXILResourceTypeMap &DRTM) {
SmallVector<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> CIToInfos;
+ // We needs to assign a unique ID to each resource that is created
+ // from a heap. The ID must be unique for each unique Index value so
+ // we can differentiate between resources instances of the same type.
+ DenseMap<Value *, uint32_t> IndexToHeapResID;
+ uint32_t NextHeapResID = 0;
+
for (Function &F : M.functions()) {
if (!F.isDeclaration())
continue;
@@ -896,6 +908,28 @@ void DXILResourceMap::populateResourceInfos(Module &M,
break;
}
+ case Intrinsic::dx_resource_handlefromheap: {
+ auto *HandleTy = cast<TargetExtType>(F.getReturnType());
+ ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+ for (User *U : F.users()) {
+ if (CallInst *CI = dyn_cast<CallInst>(U)) {
+ LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n");
+ Value *Index = CI->getArgOperand(0);
+ uint32_t HeapResID;
+ auto Pos = IndexToHeapResID.find(Index);
+ if (Pos == IndexToHeapResID.end()) {
+ HeapResID = NextHeapResID++;
+ IndexToHeapResID[Index] = HeapResID;
+ } else {
+ HeapResID = Pos->second;
+ }
+ ResourceInfo RI = ResourceInfo{HeapResID, HandleTy};
+ CIToInfos.emplace_back(CI, RI, RTI);
+ }
+ }
+ break;
+ }
}
}
@@ -938,8 +972,8 @@ void DXILResourceMap::populateResourceInfos(Module &M,
FirstCBuffer = std::min({FirstCBuffer, FirstSampler});
FirstUAV = std::min({FirstUAV, FirstCBuffer});
- // Adjust the resource binding to use the next ID.
- RI.setBindingID(NextID++);
+ if (RI.hasBinding())
+ RI.setBindingID(NextID++);
}
}
@@ -1046,9 +1080,11 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
switch (CI->getIntrinsicID()) {
// Found the create, return the binding
- case Intrinsic::dx_resource_handlefrombinding: {
+ case Intrinsic::dx_resource_handlefrombinding:
+ case Intrinsic::dx_resource_handlefromheap: {
auto Pos = CallMap.find(CI);
- assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map");
+ assert(Pos != CallMap.end() &&
+ "handle initialization call must be in resource map");
return {&Infos[Pos->second]};
}
default:
diff --git a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
index c4cc76d457367..7182c30ead174 100644
--- a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
+++ b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
@@ -285,17 +285,23 @@ void DXContainerGlobals::addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV) {
};
for (const dxil::ResourceInfo &RI : DRM.cbuffers()) {
+ if (!RI.hasBinding())
+ continue;
const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
PSV.Resources.push_back(MakeBinding(Binding, dxbc::PSV::ResourceType::CBV,
dxil::ResourceKind::CBuffer));
}
for (const dxil::ResourceInfo &RI : DRM.samplers()) {
+ if (!RI.hasBinding())
+ continue;
const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
PSV.Resources.push_back(MakeBinding(Binding,
dxbc::PSV::ResourceType::Sampler,
dxil::ResourceKind::Sampler));
}
for (const dxil::ResourceInfo &RI : DRM.srvs()) {
+ if (!RI.hasBinding())
+ continue;
const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
@@ -311,6 +317,8 @@ void DXContainerGlobals::addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV) {
MakeBinding(Binding, ResType, TypeInfo.getResourceKind()));
}
for (const dxil::ResourceInfo &RI : DRM.uavs()) {
+ if (!RI.hasBinding())
+ continue;
const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
diff --git a/llvm/lib/Target/DirectX/DXILPrettyPrinter.cpp b/llvm/lib/Target/DirectX/DXILPrettyPrinter.cpp
index 272e2db675431..35990848e0e1d 100644
--- a/llvm/lib/Target/DirectX/DXILPrettyPrinter.cpp
+++ b/llvm/lib/Target/DirectX/DXILPrettyPrinter.cpp
@@ -243,6 +243,8 @@ static void prettyPrintResources(raw_ostream &OS, const DXILResourceMap &DRM,
// TODO: Do we want to sort these by binding or something like that?
for (const dxil::ResourceInfo &RI : DRM) {
+ if (!RI.hasBinding())
+ continue;
const dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
dxil::ResourceClass RC = RTI.getResourceClass();
diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
index 240d5814d33d4..bde0729e38e0c 100644
--- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
+++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
@@ -92,19 +92,23 @@ static NamedMDNode *emitResourceMetadata(Module &M, DXILResourceMap &DRM,
LLVMContext &Context = M.getContext();
for (ResourceInfo &RI : DRM)
- if (!RI.hasSymbol())
+ if (RI.hasBinding() && !RI.hasSymbol())
RI.createSymbol(M,
DRTM[RI.getHandleTy()].createElementStruct(RI.getName()));
SmallVector<Metadata *> SRVs, UAVs, CBufs, Smps;
for (const ResourceInfo &RI : DRM.srvs())
- SRVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
+ if (RI.hasBinding())
+ SRVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
for (const ResourceInfo &RI : DRM.uavs())
- UAVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
+ if (RI.hasBinding())
+ UAVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
for (const ResourceInfo &RI : DRM.cbuffers())
- CBufs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
+ if (RI.hasBinding())
+ CBufs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
for (const ResourceInfo &RI : DRM.samplers())
- Smps.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
+ if (RI.hasBinding())
+ Smps.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()]));
Metadata *SRVMD = SRVs.empty() ? nullptr : MDNode::get(Context, SRVs);
Metadata *UAVMD = UAVs.empty() ? nullptr : MDNode::get(Context, UAVs);
diff --git a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll
new file mode 100644
index 0000000000000..18318690d8459
--- /dev/null
+++ b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll
@@ -0,0 +1,174 @@
+; RUN: opt -S -disable-output -passes="print<dxil-resources>" < %s 2>&1 | FileCheck %s
+
+%struct.S = type { <4 x float>, <4 x i32> }
+%P = type <{ float }>
+%Q = type <{ <{ [2 x <{ float, target("dx.Padding", 12) }>], float }> }>
+
+; The resources in this test are created in the same order as they appear after
+; sorting in the ResourceInfo list because FileCheck cannot match multiline sections
+; of text in arbitrary order.
+
+define void @test_typedbuffer() {
+
+ %idx = tail call i32 @llvm.dx.thread.id.in.group(i32 0)
+
+ ; Buffer<uint4> Buf2 = ResourceDescriptorHeap[ID.x + 1];
+ %add0 = add i32 %idx, 1
+ %srv0 = tail call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4i32_0_0_0t(i32 %add0, i1 false)
+; CHECK: Resource [[SRV0:0]]:
+; CHECK-NEXT: HeapIndexID: {{[0-9]+}}
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: SRV
+; CHECK-NEXT: Kind: Buffer
+; CHECK-NEXT: Element Type: u32
+; CHECK-NEXT: Element Count: 4
+
+ ; ByteAddressBuffer Buf0 = ResourceDescriptorHeap[5];
+ %srv1 = tail call target("dx.RawBuffer", i8, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_i8_0_0t(i32 5, i1 false)
+; CHECK: Resource [[SRV1:1]]:
+; CHECK-NEXT: HeapIndexID: {{[0-9]+}}
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: SRV
+; CHECK-NEXT: Kind: RawBuffer
+
+ ; struct S { float4 a; uint4 b; };
+ ; StructuredBuffer<S> Buf1 = ResourceDescriptorHeap[ID.x];
+ %srv2 = tail call target("dx.RawBuffer", %struct.S, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx, i1 false)
+; CHECK-DAG: Resource [[SRV2:2]]:
+; CHECK: HeapIndexID: {{[0-9]+}}
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: SRV
+; CHECK-NEXT: Kind: StructuredBuffer
+; CHECK-NEXT: Buffer Stride: 32
+; CHECK-NEXT: Alignment: 4
+
+; Make sure this was the last SRV resource in the list.
+; CHECK-NOT: Class: SRV
+
+ ; RWBuffer<int> Buf3 = ResourceDescriptorHeap[ID.x + 2];
+ %add2 = add i32 %idx, 2
+ %uav0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_i32_1_0_1t(i32 %add2, i1 false)
+; CHECK: Resource [[UAV0:3]]:
+; CHECK-NEXT: HeapIndexID: 7
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: UAV
+; CHECK-NEXT: Kind: Buffer
+; CHECK-NEXT: IsROV: 0
+; CHECK-NEXT: Element Type: i32
+; CHECK-NEXT: Element Count: 1
+
+ ; RWStructuredBuffer<double> Buf6 = ResourceDescriptorHeap[ID.x + 5];
+ %add5 = add i32 %idx, 5
+ %uav3 = tail call target("dx.RawBuffer", double, 1, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_f64_1_0t(i32 %add5, i1 false)
+; CHECK: Resource [[UAV1:4]]:
+; CHECK-NEXT: HeapIndexID: 2
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: UAV
+; CHECK-NEXT: Kind: StructuredBuffer
+; CHECK-NEXT: IsROV: 0
+; CHECK-NEXT: Buffer Stride: 8
+; CHECK-NEXT: Alignment: 0
+
+ ; RWStructuredBuffer<float4> Buf4 = ResourceDescriptorHeap[ID.x + 3];
+ ; Buf4.DecrementCounter();
+ %add3 = add i32 %idx, 3
+ %uav1 = tail call target("dx.RawBuffer", <4 x float>, 1, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add3, i1 false)
+ %count0 = tail call noundef i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_v4f32_1_0t(target("dx.RawBuffer", <4 x float>, 1, 0) %uav1, i8 -1)
+; CHECK: Resource [[UAV2:5]]:
+; CHECK-NEXT: HeapIndexID: 5
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Decrement
+; CHECK-NEXT: Class: UAV
+; CHECK-NEXT: Kind: StructuredBuffer
+; CHECK-NEXT: IsROV: 0
+; CHECK-NEXT: Buffer Stride: 16
+; CHECK-NEXT: Alignment: 0
+
+ ; RWStructuredBuffer<float4> Buf5 = ResourceDescriptorHeap[ID.x + 4];
+ ; Buf5.DecrementCounter();
+ ; Buf5.IncrementCounter();
+ %add4 = add i32 %idx, 4
+ %uav2 = tail call target("dx.RawBuffer", <4 x float>, 1, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add4, i1 false)
+ %14 = tail call noundef i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_v4f32_1_0t(target("dx.RawBuffer", <4 x float>, 1, 0) %uav2, i8 -1)
+ %15 = tail call noundef i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_v4f32_1_0t(target("dx.RawBuffer", <4 x float>, 1, 0) %uav2, i8 1)
+; CHECK: Resource [[UAV3:6]]:
+; CHECK-NEXT: HeapIndexID: 6
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Invalid
+; CHECK-NEXT: Class: UAV
+; CHECK-NEXT: Kind: StructuredBuffer
+; CHECK-NEXT: IsROV: 0
+; CHECK-NEXT: Buffer Stride: 16
+; CHECK-NEXT: Alignment: 0
+
+; Make sure this was the last SRV resource in the list.
+; CHECK-NOT: Class: UAV
+
+ ; struct P { float a; };
+ ; ConstantBuffer<P> CB1 = ResourceDescriptorHeap[ID.x + 6];
+ %add6 = add i32 %idx, 6
+ %cbv0 = tail call target("dx.CBuffer", %P)
+ @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Pst(i32 %add6, i1 false)
+; CHECK: Resource [[CVB0:7]]:
+; CHECK-NEXT: HeapIndexID: 0
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: CBV
+; CHECK-NEXT: Kind: CBuffer
+; CHECK-NEXT: CBuffer size: 4
+
+ ; struct Q { float b[3]; };
+ ; ConstantBuffer<Q> CB2 = ResourceDescriptorHeap[ID.x + 7];
+ %add7 = add i32 %idx, 7
+ %cvb1 = tail call target("dx.CBuffer", %Q)
+ @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false)
+; CHECK: Resource [[CVB1:8]]:
+; CHECK-NEXT: HeapIndexID: 1
+; CHECK-NEXT: Globally Coherent: 0
+; CHECK-NEXT: Has Atomic64 Use: 0
+; CHECK-NEXT: Counter Direction: Unknown
+; CHECK-NEXT: Class: CBV
+; CHECK-NEXT: Kind: CBuffer
+; CHECK-NEXT: CBuffer size: 36
+
+; CHECK-NOT: Class: CVB
+
+; Duplicated resources should not be added to the list
+; (created from heap with the same index).
+ %srv2_dupl = tail call target("dx.RawBuffer", %struct.S, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx, i1 false)
+ %cvb1_dupl = tail call target("dx.CBuffer", %Q)
+ @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false)
+
+ ret void
+}
+
+; CHECK-DAG: Call bound to [[SRV0]]: %srv0 = tail call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4i32_0_0_0t(i32 %add0, i1 false)
+; CHECK-DAG: Call bound to [[SRV1]]: %srv1 = tail call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_i8_0_0t(i32 5, i1 false)
+; CHECK-DAG: Call bound to [[SRV2]]: %srv2 = tail call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx, i1 false)
+; CHECK-DAG: Call bound to [[UAV0]]: %uav0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_i32_1_0_1t(i32 %add2, i1 false)
+; CHECK-DAG: Call bound to [[UAV1]]: %uav3 = tail call target("dx.RawBuffer", double, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_f64_1_0t(i32 %add5, i1 false)
+; CHECK-DAG: Call bound to [[UAV2]]: %uav1 = tail call target("dx.RawBuffer", <4 x float>, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add3, i1 false)
+; CHECK-DAG: Call bound to [[UAV3]]: %uav2 = tail call target("dx.RawBuffer", <4 x float>, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add4, i1 false)
+; CHECK-DAG: Call bound to [[CVB0]]: %cbv0 = tail call target("dx.CBuffer", %P) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Pst(i32 %add6, i1 false)
+; CHECK-DAG: Call bound to [[CVB1]]: %cvb1 = tail call target("dx.CBuffer", %Q) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false)
diff --git a/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll b/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll
new file mode 100644
index 0000000000000..dd5101b262b8e
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll
@@ -0,0 +1,12 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+; CHECK: RWStructuredBuffers may increment or decrement their counters, but not both.
+
+define void @inc_and_dec() {
+entry:
+ %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromheap(i32 10, i1 false)
+ call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1)
+
+ %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromheap(i32 10, i1 false)
+ call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle2, i8 1)
+ ret void
+}
More information about the llvm-commits
mailing list