From 04956d92acfbad11f078f33916c63c53b1bbf608 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 14 Aug 2026 18:24:20 -0700 Subject: [PATCH 01/11] [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 | 10 + 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, 289 insertions(+), 26 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 +#include namespace llvm { class CallInst; @@ -402,7 +403,8 @@ class ResourceInfo { }; private: - ResourceBinding Binding; + std::optional 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..944d17202c5e0 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -36,6 +36,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> 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 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(F.getReturnType()); + ResourceTypeInfo &RTI = DRTM[HandleTy]; + + for (User *U : F.users()) { + if (CallInst *CI = dyn_cast(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 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 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" < %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 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 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 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 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 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 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

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 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 +} From 46ded031905032fbde74a68ec9b6a2b7443a00a7 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Sat, 15 Aug 2026 00:03:46 -0700 Subject: [PATCH 02/11] [DirectX] Add shader flags for heap resources Fixes #213825 --- llvm/lib/Target/DirectX/DXILShaderFlags.cpp | 12 +++++- .../DirectX/ShaderFlags/heap-resources.ll | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index 64d8dc33e3e60..b7c296b2bde6d 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -274,6 +274,16 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF, } break; } + case Intrinsic::dx_resource_handlefromheap: { + if (auto *ConstInt = dyn_cast(II->getArgOperand(1))) { + bool IsSamplerHeap = ConstInt->getValue().getBoolValue(); + if (IsSamplerHeap) + CSF.SamplerDescriptorHeapIndexing = true; + else + CSF.ResourceDescriptorHeapIndexing = true; + } + break; + } case Intrinsic::dx_resource_load_typedbuffer: { dxil::ResourceTypeInfo &RTI = DRTM[cast(II->getArgOperand(0)->getType())]; @@ -342,7 +352,7 @@ ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M, if (MMDI.ValidatorVersion < VersionTuple(1, 6)) { NumUAVs++; } else { // MMDI.ValidatorVersion >= VersionTuple(1, 6) - uint32_t Size = UAV.getBinding().Size; + uint32_t Size = UAV.getSize(); uint32_t NewNum = NumUAVs + (Size == 0 ? ~0U : Size); if (NewNum < NumUAVs) NewNum = ~0U; diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll new file mode 100644 index 0000000000000..d7e15a65aea3a --- /dev/null +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll @@ -0,0 +1,42 @@ +; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC + +; This test makes sure that the shader flags 'Resource descriptor heap indexing' +; is set when the shader uses CreateHandleFromHeap instruction on a resource +; descriptor heap. + +target triple = "dxil-pc-shadermodel6.6-library" + +; CHECK: Combined Shader Flags for Module +; CHECK-NEXT: Shader Flags Value: 0xc0000000 + +; CHECK: Note: shader requires additional functionality: +; CHECK: Resource descriptor heap indexing +; CHECK: Sampler descriptor heap indexing +; +; CHECK: Function test_1 : 0x40000000 +define void @test_1() "hlsl.export" { + ; RWBuffer Buf = ResourceDescriptorHeap[3] + %typed = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) + @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 3, i1 false) + ret void +} + +; CHECK: Function test_2 : 0x80000000 +define void @test_2() "hlsl.export" { + ; SamplerState Samp = ResourceDescriptorHeap[100]; + %samp = call target("dx.Sampler", 0) + @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 100, i1 true) + ret void +} + +!dx.valver = !{!0} +!0 = !{i32 1, i32 8} + +; DXC: - Name: SFI0 +; DXC-NEXT: Size: 8 +; DXC-NEXT: Flags: +; DXC: ResourceDescriptorHeapIndexing: true +; DXC: SamplerDescriptorHeapIndexing: true +; DXC: NextUnusedBit: false + From a045cb57f62541ff59a16a27eda77df631db01cd Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Sun, 16 Aug 2026 21:51:50 -0700 Subject: [PATCH 03/11] Update size check in DXILShaderFlags --- llvm/lib/Target/DirectX/DXILShaderFlags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index 64d8dc33e3e60..668f2453d9157 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -342,7 +342,7 @@ ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M, if (MMDI.ValidatorVersion < VersionTuple(1, 6)) { NumUAVs++; } else { // MMDI.ValidatorVersion >= VersionTuple(1, 6) - uint32_t Size = UAV.getBinding().Size; + uint32_t Size = UAV.getSize(); uint32_t NewNum = NumUAVs + (Size == 0 ? ~0U : Size); if (NewNum < NumUAVs) NewNum = ~0U; From 63816eb8715ec96d19a0dd04260603c731e22d45 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Sun, 16 Aug 2026 23:01:42 -0700 Subject: [PATCH 04/11] add test to verify that heap resources do not appear in printed resource table or metadata --- .../DirectX/Metadata/cbuffer-metadata.ll | 20 ++++++++++++++++++- .../CodeGen/DirectX/Metadata/srv_metadata.ll | 14 ++++++++++++- .../CodeGen/DirectX/Metadata/uav_metadata.ll | 14 ++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll index e1e17328f355a..1fb20d6575aba 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s -o - -disable-dxil-remove-unused-resources < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -o - -disable-dxil-remove-unused-resources -stop-before=dxil-op-lower 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target triple = "dxil-pc-shadermodel6.6-compute" @@ -22,7 +22,12 @@ target triple = "dxil-pc-shadermodel6.6-compute" @MyConstants.cb = global target("dx.CBuffer", %__cblayout_MyConstants) poison @MyConstants.str = private unnamed_addr constant [12 x i8] c"MyConstants\00", align 1 +%__cblayout_HeapCB = type <{ + <2 x i16> +}> + ; PRINT:; Resource Bindings: +; PRINT-NOT: ; HeapCB ; PRINT-NEXT:; ; PRINT-NEXT:; Name Type Format Dim ID HLSL Bind Count ; PRINT-NEXT:; ---- @@ -67,11 +72,24 @@ define void @test() #0 { %CB3.cb_h = call target("dx.CBuffer", %__cblayout_MyConstants) @llvm.dx.resource.handlefrombinding(i32 15, i32 5, i32 1, i32 0, ptr @MyConstants.str) + ; Resource from heap should not appear anywhere in the resource list + ; since it does not have a binding. + ; + ; struct HeapCB { + ; int16_t2 v; + ; }; + ; ConstantBuffer CB4 = ResourceDescriptorHeap[10]; + %CB4.cb_h = call target("dx.CBuffer", %__cblayout_HeapCB) + @llvm.dx.resource.handlefromheap(i32 10, i1 false) + ret void } attributes #0 = { noinline nounwind "hlsl.shader"="compute" } +; Constant buffer from heap is the only one using { <2 x i16> } and it should not appear in list. +; CHECK-NOT: = type { <2 x i16> } + ; CHECK: %CBuffer.CB1 = type { { float, i32, double, <2 x i32> } } ; CHECK: %CBuffer.CB2 = type { { float, double, float, half, i16, i64, i32 } } ; CHECK: %CBuffer.MyConstants = type { { double, <3 x float>, float, <3 x double>, half, <2 x double>, float, <3 x half>, <3 x half> } } diff --git a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll index cac3c3381837b..67053edb2ca3b 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s -o - -disable-dxil-remove-unused-resources 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -o - -disable-dxil-remove-unused-resources -stop-before=dxil-op-lower 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" target triple = "dxil-pc-shadermodel6.6-compute" @@ -16,6 +16,9 @@ target triple = "dxil-pc-shadermodel6.6-compute" @Array.str = private unnamed_addr constant [6 x i8] c"Array\00", align 1 @Array2.str = private unnamed_addr constant [7 x i8] c"Array2\00", align 1 +; Make sure heap resource does not appear in the resource list. +; PRINT-NOT: ; i16 + ; PRINT:; Resource Bindings: ; PRINT-NEXT:; ; PRINT-NEXT:; Name Type Format Dim ID HLSL Bind Count @@ -84,11 +87,20 @@ define void @test() #0 { %Array2_20_h = call target("dx.TypedBuffer", double, 0, 0, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 0, i32 20, ptr @Array2.str) + ; Resource from heap should not appear anywhere in the resource list + ; since it does not have a binding. Use <2 x i16> element type to make sure + ; it does not match any of the other resources. + %heap_resource = tail call target("dx.TypedBuffer", <2 x i16>, 0, 0, 0) + @llvm.dx.resource.handlefromheap(i32 5, i1 false) + ret void } attributes #0 = { noinline nounwind "hlsl.shader"="compute" } +; Heap resource is the only one using <2 x i16> and it should not appear in list +; CHECK-NOT: = type { <2 x i16> } + ; CHECK: %"Buffer" = type { <4 x half> } ; CHECK: %"Buffer" = type { <2 x float> } ; CHECK: %"Buffer" = type { double } diff --git a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll index 9ab87829730f8..0e9e74dbcafcb 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll @@ -1,6 +1,6 @@ ; RUN: opt -S -dxil-translate-metadata < %s | FileCheck %s ; RUN: opt -S --passes="dxil-pretty-printer" < %s 2>&1 | FileCheck %s --check-prefix=PRINT -; RUN: llc %s -o - -disable-dxil-remove-unused-resources 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT +; RUN: llc %s -o - -disable-dxil-remove-unused-resources -stop-before=dxil-op-lower 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" target triple = "dxil-pc-shadermodel6.6-compute" @@ -19,6 +19,9 @@ target triple = "dxil-pc-shadermodel6.6-compute" @Array.str = private unnamed_addr constant [6 x i8] c"Array\00", align 1 @Array2.str = private unnamed_addr constant [7 x i8] c"Array2\00", align 1 +; Make sure heap resource does not appear in the resource list. +; PRINT-NOT: ; i16 + ; PRINT:; Resource Bindings: ; PRINT-NEXT:; ; PRINT-NEXT:; Name Type Format Dim ID HLSL Bind Count @@ -101,11 +104,20 @@ define void @test() #0 { %Ten_h = call target("dx.TypedBuffer", i64, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 5, i32 22, i32 1, i32 0, ptr @Ten.str) + ; Resource from heap should not appear anywhere in the resource list + ; since it does not have a binding. Use <2 x i16> element type to make sure + ; it does not match any of the other resources. + %heap_resource = tail call target("dx.TypedBuffer", <2 x i16>, 1, 0, 0) + @llvm.dx.resource.handlefromheap(i32 5, i1 false) + ret void } attributes #0 = { noinline nounwind "hlsl.shader"="compute" } +; Heap resource is the only one using <2 x i16> and it should not appear in list +; CHECK-NOT: = type { <2 x i16> } + ; CHECK: %"RWBuffer" = type { <4 x half> } ; CHECK: %"RWBuffer" = type { <2 x float> } ; CHECK: %"RWBuffer" = type { double } From 19f456a1e909d2d962fdd15f2f4c33b93d76e82f Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 17 Aug 2026 17:40:29 -0700 Subject: [PATCH 05/11] code review feedback --- llvm/include/llvm/Analysis/DXILResource.h | 2 +- llvm/test/Analysis/DXILResource/buffer-fromheap.ll | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index f68e5ab34c7b4..d79ded9d02fc9 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -439,7 +439,7 @@ class ResourceInfo { return Binding.value(); } - uint32_t getSize() const { return Binding ? Binding->Size : 1; } + uint32_t getSize() const { return hasBinding() ? Binding->Size : 1; } TargetExtType *getHandleTy() const { return HandleTy; } StringRef getName() const { return Name; } diff --git a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll index 18318690d8459..3cdb9a15dae92 100644 --- a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll +++ b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll @@ -120,7 +120,7 @@ define void @test_typedbuffer() { ; CHECK-NEXT: Buffer Stride: 16 ; CHECK-NEXT: Alignment: 0 -; Make sure this was the last SRV resource in the list. +; Make sure this was the last UAV resource in the list. ; CHECK-NOT: Class: UAV ; struct P { float a; }; @@ -151,7 +151,8 @@ define void @test_typedbuffer() { ; CHECK-NEXT: Kind: CBuffer ; CHECK-NEXT: CBuffer size: 36 -; CHECK-NOT: Class: CVB +; Make sure this was the last CBV resource in the list. +; CHECK-NOT: Class: CBV ; Duplicated resources should not be added to the list ; (created from heap with the same index). From 2e1e167e8706a36c95267cd5c78592db9c8dd1ab Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 18 Aug 2026 12:54:00 -0700 Subject: [PATCH 06/11] code review feedback - update test comments --- .../test/CodeGen/DirectX/ShaderFlags/heap-resources.ll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll index d7e15a65aea3a..cc84d2f8574ad 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll @@ -1,9 +1,11 @@ ; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s ; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC -; This test makes sure that the shader flags 'Resource descriptor heap indexing' -; is set when the shader uses CreateHandleFromHeap instruction on a resource -; descriptor heap. +; This test makes sure that the shader flag 'Resource descriptor heap indexing' +; is set when the shader uses CreateHandleFromHeap instruction to get a resource +; from a resource descriptor heap, and that the shader flag `Sampler descriptor +; heap indexing` is set when the shader uses the same instruction to get a sampler +; from a sampler descriptor heap. target triple = "dxil-pc-shadermodel6.6-library" @@ -24,7 +26,7 @@ define void @test_1() "hlsl.export" { ; CHECK: Function test_2 : 0x80000000 define void @test_2() "hlsl.export" { - ; SamplerState Samp = ResourceDescriptorHeap[100]; + ; SamplerState Samp = SamplerDescriptorHeap[100]; %samp = call target("dx.Sampler", 0) @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 100, i1 true) ret void From eef596ca21f5fbf67c9e5dedf4e5cc61f383324b Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Aug 2026 10:14:15 -0700 Subject: [PATCH 07/11] coder review feedback - switch ResourceBinding and HeapID to std::variant --- llvm/include/llvm/Analysis/DXILResource.h | 33 +++--- llvm/lib/Analysis/DXILResource.cpp | 22 ++-- .../Analysis/DXILResource/buffer-fromheap.ll | 100 ++++++++++-------- 3 files changed, 85 insertions(+), 70 deletions(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index d79ded9d02fc9..14a72aae42aed 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -403,8 +403,7 @@ class ResourceInfo { }; private: - std::optional Binding; - uint32_t HeapResourceID = -1U; + std::variant BindingOrHeapID; TargetExtType *HandleTy; StringRef Name; GlobalVariable *Symbol = nullptr; @@ -417,17 +416,19 @@ class ResourceInfo { ResourceInfo(uint32_t Space, uint32_t LowerBound, uint32_t Size, TargetExtType *HandleTy, StringRef Name = "", GlobalVariable *Symbol = nullptr) - : Binding{ResourceBinding{0, Space, LowerBound, Size}}, + : BindingOrHeapID{ResourceBinding{0, Space, LowerBound, Size}}, HandleTy(HandleTy), Name(Name), Symbol(Symbol) {} ResourceInfo(uint32_t HeapResourceID, TargetExtType *HandleTy) - : Binding{std::nullopt}, HeapResourceID(HeapResourceID), - HandleTy(HandleTy), Name(""), Symbol(nullptr) {} + : BindingOrHeapID{HeapResourceID}, HandleTy(HandleTy), Name(""), + Symbol(nullptr) {} - bool hasBinding() const { return Binding.has_value(); } + bool hasBinding() const { + return std::holds_alternative(BindingOrHeapID); + } void setBindingID(unsigned ID) { assert(hasBinding() && "Resource does not have a binding"); - Binding->BindingID = ID; + std::get(BindingOrHeapID).BindingID = ID; } bool hasCounter() const { @@ -436,10 +437,17 @@ class ResourceInfo { const ResourceBinding &getBinding() const { assert(hasBinding() && "Resource does not have a binding"); - return Binding.value(); + return std::get(BindingOrHeapID); + } + + uint32_t getSize() const { + return hasBinding() ? std::get(BindingOrHeapID).Size : 1; } - uint32_t getSize() const { return hasBinding() ? Binding->Size : 1; } + uint32_t getHeapID() const { + assert(!hasBinding() && "Resource does not have a heap ID"); + return std::get(BindingOrHeapID); + } TargetExtType *getHandleTy() const { return HandleTy; } StringRef getName() const { return Name; } @@ -452,13 +460,12 @@ class ResourceInfo { getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const; bool operator==(const ResourceInfo &RHS) const { - return std::tie(Binding, HandleTy, Symbol, Name, HeapResourceID) == - std::tie(RHS.Binding, RHS.HandleTy, RHS.Symbol, RHS.Name, - RHS.HeapResourceID); + return std::tie(BindingOrHeapID, HandleTy, Symbol, Name) == + std::tie(RHS.BindingOrHeapID, RHS.HandleTy, RHS.Symbol, RHS.Name); } bool operator!=(const ResourceInfo &RHS) const { return !(*this == RHS); } bool operator<(const ResourceInfo &RHS) const { - return Binding < RHS.Binding; + return BindingOrHeapID < RHS.BindingOrHeapID; } LLVM_ABI void print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI, diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index d5339d84d0096..bb113f27128d2 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -673,6 +673,7 @@ 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"); + const ResourceBinding &Binding = getBinding(); LLVMContext &Ctx = M.getContext(); const DataLayout &DL = M.getDataLayout(); @@ -690,13 +691,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))); @@ -802,13 +803,14 @@ void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI, } if (hasBinding()) { + const ResourceBinding &Binding = getBinding(); OS << " Binding:\n" - << " Binding ID: " << Binding->BindingID << "\n" - << " Space: " << Binding->Space << "\n" - << " Lower Bound: " << Binding->LowerBound << "\n" - << " Size: " << Binding->Size << "\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 << " HeapIndexID: " << getHeapID() << "\n"; } OS << " Globally Coherent: " << GloballyCoherent << "\n"; @@ -871,7 +873,7 @@ void DXILResourceMap::populateResourceInfos(Module &M, DXILResourceTypeMap &DRTM) { SmallVector> CIToInfos; - // We needs to assign a unique ID to each resource that is created + // We need 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 IndexToHeapResID; diff --git a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll index 3cdb9a15dae92..9f191391ccce7 100644 --- a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll +++ b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll @@ -12,24 +12,10 @@ define void @test_typedbuffer() { %idx = tail call i32 @llvm.dx.thread.id.in.group(i32 0) - ; Buffer 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) + %srv0 = 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: Resource [[SRV0:0]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 @@ -39,9 +25,9 @@ define void @test_typedbuffer() { ; struct S { float4 a; uint4 b; }; ; StructuredBuffer Buf1 = ResourceDescriptorHeap[ID.x]; - %srv2 = tail call target("dx.RawBuffer", %struct.S, 0, 0) + %srv1 = 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-DAG: Resource [[SRV1:1]]: ; CHECK: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 @@ -51,30 +37,30 @@ define void @test_typedbuffer() { ; 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 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 + ; Buffer Buf2 = ResourceDescriptorHeap[ID.x + 1]; + %add0 = add i32 %idx, 1 + %srv2 = 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 [[SRV2:2]]: +; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Unknown -; CHECK-NEXT: Class: UAV +; CHECK-NEXT: Class: SRV ; CHECK-NEXT: Kind: Buffer -; CHECK-NEXT: IsROV: 0 -; CHECK-NEXT: Element Type: i32 -; CHECK-NEXT: Element Count: 1 +; CHECK-NEXT: Element Type: u32 +; CHECK-NEXT: Element Count: 4 + + +; Make sure this was the last SRV resource in the list. +; CHECK-NOT: Class: SRV ; RWStructuredBuffer Buf6 = ResourceDescriptorHeap[ID.x + 5]; %add5 = add i32 %idx, 5 - %uav3 = tail call target("dx.RawBuffer", double, 1, 0) + %uav0 = 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: Resource [[UAV0:3]]: +; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Unknown @@ -90,8 +76,8 @@ define void @test_typedbuffer() { %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: Resource [[UAV1:4]]: +; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Decrement @@ -109,8 +95,8 @@ define void @test_typedbuffer() { @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: Resource [[UAV2:5]]: +; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Invalid @@ -120,6 +106,22 @@ define void @test_typedbuffer() { ; CHECK-NEXT: Buffer Stride: 16 ; CHECK-NEXT: Alignment: 0 + + ; RWBuffer Buf3 = ResourceDescriptorHeap[ID.x + 2]; + %add2 = add i32 %idx, 2 + %uav3 = 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 [[UAV3:6]]: +; CHECK-NEXT: HeapIndexID: {{[0-9]+}} +; 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 + ; Make sure this was the last UAV resource in the list. ; CHECK-NOT: Class: UAV @@ -129,7 +131,7 @@ define void @test_typedbuffer() { %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: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Unknown @@ -143,7 +145,7 @@ define void @test_typedbuffer() { %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: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 ; CHECK-NEXT: Has Atomic64 Use: 0 ; CHECK-NEXT: Counter Direction: Unknown @@ -164,12 +166,16 @@ define void @test_typedbuffer() { 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 [[SRV0]]: %srv0 = 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 [[SRV1]]: %srv1 = 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 [[SRV2]]: %srv2 = 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 [[UAV0]]: %uav0 = 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 [[UAV1]]: %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 [[UAV2]]: %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 [[UAV3]]: %uav3 = 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 [[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) + +; duplicate calls should map to existing resources in the list, not create new ones +; CHECK-DAG: Call bound to [[SRV1]]: %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) +; CHECK-DAG: Call bound to [[CVB1]]: %cvb1_dupl = tail call target("dx.CBuffer", %Q) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false) From 078a20e3ec3b9d1a3a3fa86f717079033406fa40 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 19 Aug 2026 15:43:11 -0700 Subject: [PATCH 08/11] update include --- llvm/include/llvm/Analysis/DXILResource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 14a72aae42aed..f2921587908ea 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -21,7 +21,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/DXILABI.h" #include -#include +#include namespace llvm { class CallInst; From c8ed1b4ef2b05728c99a8228ecbb50cdb554a2a4 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 20 Aug 2026 15:43:16 -0700 Subject: [PATCH 09/11] Remove bool argument from llvm.dx.resource.handlefromheap (per https://github.com/llvm/wg-hlsl/pull/472) --- llvm/include/llvm/IR/IntrinsicsDirectX.td | 10 ++--- .../Analysis/DXILResource/buffer-fromheap.ll | 44 +++++++++---------- .../DirectX/Metadata/cbuffer-metadata.ll | 2 +- .../CodeGen/DirectX/Metadata/srv_metadata.ll | 2 +- .../CodeGen/DirectX/Metadata/uav_metadata.ll | 2 +- .../resource_from_heap_counter_error.ll | 4 +- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index 944d17202c5e0..562ca035e79ce 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -37,14 +37,10 @@ def int_dx_resource_handlefromimplicitbinding [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. +// type appropriate for the kind of resource given a heap index. Type of +// heap is determined by the intrinsic's resource overload type. def int_dx_resource_handlefromheap - : DefaultAttrsIntrinsic< - [llvm_any_ty], - [llvm_i32_ty, llvm_i1_ty], - [IntrNoMem]>; + : DefaultAttrsIntrinsic<[llvm_any_ty], [llvm_i32_ty], [IntrNoMem]>; def int_dx_resource_getpointer : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_any_ty], diff --git a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll index 9f191391ccce7..8a9af7b0b85c7 100644 --- a/llvm/test/Analysis/DXILResource/buffer-fromheap.ll +++ b/llvm/test/Analysis/DXILResource/buffer-fromheap.ll @@ -14,7 +14,7 @@ define void @test_typedbuffer() { ; ByteAddressBuffer Buf0 = ResourceDescriptorHeap[5]; %srv0 = tail call target("dx.RawBuffer", i8, 0, 0) - @llvm.dx.resource.handlefromheap.tdx.RawBuffer_i8_0_0t(i32 5, i1 false) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_i8_0_0t(i32 5) ; CHECK: Resource [[SRV0:0]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -26,7 +26,7 @@ define void @test_typedbuffer() { ; struct S { float4 a; uint4 b; }; ; StructuredBuffer Buf1 = ResourceDescriptorHeap[ID.x]; %srv1 = tail call target("dx.RawBuffer", %struct.S, 0, 0) - @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx, i1 false) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx) ; CHECK-DAG: Resource [[SRV1:1]]: ; CHECK: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -40,7 +40,7 @@ define void @test_typedbuffer() { ; Buffer Buf2 = ResourceDescriptorHeap[ID.x + 1]; %add0 = add i32 %idx, 1 %srv2 = 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) + @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4i32_0_0_0t(i32 %add0) ; CHECK: Resource [[SRV2:2]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -58,7 +58,7 @@ define void @test_typedbuffer() { ; RWStructuredBuffer Buf6 = ResourceDescriptorHeap[ID.x + 5]; %add5 = add i32 %idx, 5 %uav0 = tail call target("dx.RawBuffer", double, 1, 0) - @llvm.dx.resource.handlefromheap.tdx.RawBuffer_f64_1_0t(i32 %add5, i1 false) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_f64_1_0t(i32 %add5) ; CHECK: Resource [[UAV0:3]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -74,7 +74,7 @@ define void @test_typedbuffer() { ; 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) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add3) %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 [[UAV1:4]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} @@ -92,7 +92,7 @@ define void @test_typedbuffer() { ; 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) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add4) %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 [[UAV2:5]]: @@ -110,7 +110,7 @@ define void @test_typedbuffer() { ; RWBuffer Buf3 = ResourceDescriptorHeap[ID.x + 2]; %add2 = add i32 %idx, 2 %uav3 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) - @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_i32_1_0_1t(i32 %add2, i1 false) + @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_i32_1_0_1t(i32 %add2) ; CHECK: Resource [[UAV3:6]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -129,7 +129,7 @@ define void @test_typedbuffer() { ; ConstantBuffer

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) + @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Pst(i32 %add6) ; CHECK: Resource [[CVB0:7]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -143,7 +143,7 @@ define void @test_typedbuffer() { ; ConstantBuffer 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) + @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7) ; CHECK: Resource [[CVB1:8]]: ; CHECK-NEXT: HeapIndexID: {{[0-9]+}} ; CHECK-NEXT: Globally Coherent: 0 @@ -159,23 +159,23 @@ define void @test_typedbuffer() { ; 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) + @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx) %cvb1_dupl = tail call target("dx.CBuffer", %Q) - @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false) + @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7) ret void } -; CHECK-DAG: Call bound to [[SRV0]]: %srv0 = 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 [[SRV1]]: %srv1 = 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 [[SRV2]]: %srv2 = 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 [[UAV0]]: %uav0 = 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 [[UAV1]]: %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 [[UAV2]]: %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 [[UAV3]]: %uav3 = 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 [[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) +; CHECK-DAG: Call bound to [[SRV0]]: %srv0 = tail call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_i8_0_0t(i32 5) +; CHECK-DAG: Call bound to [[SRV1]]: %srv1 = tail call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx) +; CHECK-DAG: Call bound to [[SRV2]]: %srv2 = tail call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4i32_0_0_0t(i32 %add0) +; CHECK-DAG: Call bound to [[UAV0]]: %uav0 = tail call target("dx.RawBuffer", double, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_f64_1_0t(i32 %add5) +; CHECK-DAG: Call bound to [[UAV1]]: %uav1 = tail call target("dx.RawBuffer", <4 x float>, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add3) +; CHECK-DAG: Call bound to [[UAV2]]: %uav2 = tail call target("dx.RawBuffer", <4 x float>, 1, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_v4f32_1_0t(i32 %add4) +; CHECK-DAG: Call bound to [[UAV3]]: %uav3 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_i32_1_0_1t(i32 %add2) +; CHECK-DAG: Call bound to [[CVB0]]: %cbv0 = tail call target("dx.CBuffer", %P) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Pst(i32 %add6) +; CHECK-DAG: Call bound to [[CVB1]]: %cvb1 = tail call target("dx.CBuffer", %Q) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7) ; duplicate calls should map to existing resources in the list, not create new ones -; CHECK-DAG: Call bound to [[SRV1]]: %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) -; CHECK-DAG: Call bound to [[CVB1]]: %cvb1_dupl = tail call target("dx.CBuffer", %Q) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7, i1 false) +; CHECK-DAG: Call bound to [[SRV1]]: %srv2_dupl = tail call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefromheap.tdx.RawBuffer_s_struct.Ss_0_0t(i32 %idx) +; CHECK-DAG: Call bound to [[CVB1]]: %cvb1_dupl = tail call target("dx.CBuffer", %Q) @llvm.dx.resource.handlefromheap.tdx.CBuffer_s_Qst(i32 %add7) diff --git a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll index 1fb20d6575aba..a43cb0fa46d46 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/cbuffer-metadata.ll @@ -80,7 +80,7 @@ define void @test() #0 { ; }; ; ConstantBuffer CB4 = ResourceDescriptorHeap[10]; %CB4.cb_h = call target("dx.CBuffer", %__cblayout_HeapCB) - @llvm.dx.resource.handlefromheap(i32 10, i1 false) + @llvm.dx.resource.handlefromheap(i32 10) ret void } diff --git a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll index 67053edb2ca3b..9f3625d192d49 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/srv_metadata.ll @@ -91,7 +91,7 @@ define void @test() #0 { ; since it does not have a binding. Use <2 x i16> element type to make sure ; it does not match any of the other resources. %heap_resource = tail call target("dx.TypedBuffer", <2 x i16>, 0, 0, 0) - @llvm.dx.resource.handlefromheap(i32 5, i1 false) + @llvm.dx.resource.handlefromheap(i32 5) ret void } diff --git a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll index 0e9e74dbcafcb..be0cfa8451fa9 100644 --- a/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/uav_metadata.ll @@ -108,7 +108,7 @@ define void @test() #0 { ; since it does not have a binding. Use <2 x i16> element type to make sure ; it does not match any of the other resources. %heap_resource = tail call target("dx.TypedBuffer", <2 x i16>, 1, 0, 0) - @llvm.dx.resource.handlefromheap(i32 5, i1 false) + @llvm.dx.resource.handlefromheap(i32 5) ret void } diff --git a/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll b/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll index dd5101b262b8e..8a7560e0adc15 100644 --- a/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll +++ b/llvm/test/CodeGen/DirectX/resource_from_heap_counter_error.ll @@ -3,10 +3,10 @@ define void @inc_and_dec() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromheap(i32 10, i1 false) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromheap(i32 10) 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) + %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromheap(i32 10) call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle2, i8 1) ret void } From 2b64e78fb95b673e26e963075fe5942e40aa230f Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 21 Aug 2026 15:22:53 -0700 Subject: [PATCH 10/11] Add heap resource handling for ResMayNotAlias, Max64UAV, and hasUAVsAtEveryStage, add tests --- llvm/lib/Target/DirectX/DXILShaderFlags.cpp | 25 +++++++---- .../ShaderFlags/heap-resources-max64uavs.ll | 42 +++++++++++++++++++ .../DirectX/ShaderFlags/heap-resources.ll | 30 ++++++++----- .../uavs-at-every-stage-vs-heap.ll | 36 ++++++++++++++++ 4 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources-max64uavs.ll create mode 100644 llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-vs-heap.ll diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index b7c296b2bde6d..af86bdf6ca09a 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -34,7 +34,10 @@ using namespace llvm::dxil; static bool hasUAVsAtEveryStage(const DXILResourceMap &DRM, const ModuleMetadataInfo &MMDI) { - if (DRM.uavs().empty()) + // Heap resources do not count towards hasUAVsAtEveryStage. + bool HasUAVWithBinding = any_of( + DRM.uavs(), [](const ResourceInfo &RI) { return RI.hasBinding(); }); + if (!HasUAVWithBinding) return false; switch (MMDI.ShaderProfile) { @@ -275,12 +278,14 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF, break; } case Intrinsic::dx_resource_handlefromheap: { - if (auto *ConstInt = dyn_cast(II->getArgOperand(1))) { - bool IsSamplerHeap = ConstInt->getValue().getBoolValue(); - if (IsSamplerHeap) - CSF.SamplerDescriptorHeapIndexing = true; - else - CSF.ResourceDescriptorHeapIndexing = true; + dxil::ResourceTypeInfo &RTI = DRTM[cast(II->getType())]; + bool IsSamplerHeap = RTI.isSampler(); + CSF.SamplerDescriptorHeapIndexing |= IsSamplerHeap; + CSF.ResourceDescriptorHeapIndexing |= !IsSamplerHeap; + + if (!CSF.ResMayNotAlias && CanSetResMayNotAlias && RTI.isUAV() && + MMDI.ValidatorVersion >= VersionTuple(1, 8)) { + CSF.ResMayNotAlias = true; } break; } @@ -348,7 +353,10 @@ ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M, // Set the Max64UAVs flag if the number of UAVs is > 8 uint32_t NumUAVs = 0; - for (auto &UAV : DRM.uavs()) + for (auto &UAV : DRM.uavs()) { + // Heap resources do not count towards Max64UAVs flag. + if (!UAV.hasBinding()) + continue; if (MMDI.ValidatorVersion < VersionTuple(1, 6)) { NumUAVs++; } else { // MMDI.ValidatorVersion >= VersionTuple(1, 6) @@ -358,6 +366,7 @@ ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M, NewNum = ~0U; NumUAVs = NewNum; } + } if (NumUAVs > 8) CSF.Max64UAVs = true; diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources-max64uavs.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources-max64uavs.ll new file mode 100644 index 0000000000000..c18f05189fe6b --- /dev/null +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources-max64uavs.ll @@ -0,0 +1,42 @@ +; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s +; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC + +; This test verifies that the Max64UAVs flag is *not* set if there +; are 9 or more heap UAVs, since a heap UAVs do not count. + +target triple = "dxil-pc-shadermodel6.6-library" + +; CHECK: Combined Shader Flags for Module +; CHECK-NEXT: Shader Flags Value: 0x40000000 + +; CHECK: Note: shader requires additional functionality: +; CHECK-NOT: 64 UAV slots +; CHECK: Resource descriptor heap indexing + +; CHECK: Function test : 0x40000000 +define void @test() "hlsl.export" { + + %1 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 0) + %2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 1) + %3 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 2) + %4 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 3) + %5 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 4) + %6 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 5) + %7 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 6) + %8 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 7) + %9 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap(i32 8) + + ret void +} + +!dx.valver = !{!0} +!0 = !{i32 1, i32 8} + +; DXC: - Name: SFI0 +; DXC-NEXT: Size: 8 +; DXC-NEXT: Flags: +; DXC: Max64UAVs: false +; DXC: ResourceDescriptorHeapIndexing: true +; DXC: SamplerDescriptorHeapIndexing: false +; DXC: NextUnusedBit: false + diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll index cc84d2f8574ad..edc66cf470505 100644 --- a/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/heap-resources.ll @@ -1,26 +1,34 @@ -; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s -; RUN: llc %s -disable-dxil-remove-unused-resources --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC +; RUN: opt -S --passes="print-dx-shader-flags" -mtriple=dxil-pc-shadermodel6.6-library 2>&1 %s | FileCheck %s --check-prefixes=CHECK,CHECK-66 +; RUN: opt -S --passes="print-dx-shader-flags" -mtriple=dxil-pc-shadermodel6.7-library 2>&1 %s | FileCheck %s --check-prefixes=CHECK,CHECK-67 +; RUN: llc %s -disable-dxil-remove-unused-resources -mtriple=dxil-pc-shadermodel6.7-library --filetype=obj -o - | \ +; RUN: obj2yaml | FileCheck %s --check-prefix=DXC ; This test makes sure that the shader flag 'Resource descriptor heap indexing' -; is set when the shader uses CreateHandleFromHeap instruction to get a resource -; from a resource descriptor heap, and that the shader flag `Sampler descriptor -; heap indexing` is set when the shader uses the same instruction to get a sampler -; from a sampler descriptor heap. +; is set when the shader uses llvm.dx.resource.handlefromheap intrinsic to get +; a resource from a resource descriptor heap, and that the shader flag +; `Sampler descriptor heap indexing` is set when the shader uses the same +; intrinsic to get a sampler from a sampler descriptor heap. -target triple = "dxil-pc-shadermodel6.6-library" +; It also checks that the flag "Any UAV may not alias any other UAV" is set for +; a function that uses a heap UAV resource, but only for shader model 6.7 and higher. ; CHECK: Combined Shader Flags for Module -; CHECK-NEXT: Shader Flags Value: 0xc0000000 +; CHECK-66: Shader Flags Value: 0xc0000000 +; CHECK-67: Shader Flags Value: 0x2c0000000 ; CHECK: Note: shader requires additional functionality: ; CHECK: Resource descriptor heap indexing ; CHECK: Sampler descriptor heap indexing + +; CHECK: Note: extra DXIL module flags: +; CHECK-67: Any UAV may not alias any other UAV ; -; CHECK: Function test_1 : 0x40000000 +; CHECK-66: Function test_1 : 0x40000000 +; CHECK-67: Function test_1 : 0x240000000 define void @test_1() "hlsl.export" { ; RWBuffer Buf = ResourceDescriptorHeap[3] %typed = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) - @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 3, i1 false) + @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 3) ret void } @@ -28,7 +36,7 @@ define void @test_1() "hlsl.export" { define void @test_2() "hlsl.export" { ; SamplerState Samp = SamplerDescriptorHeap[100]; %samp = call target("dx.Sampler", 0) - @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 100, i1 true) + @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 100) ret void } diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-vs-heap.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-vs-heap.ll new file mode 100644 index 0000000000000..364147af538cd --- /dev/null +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/uavs-at-every-stage-vs-heap.ll @@ -0,0 +1,36 @@ +; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s +; TODO: Remove this comment and add 'RUN' to the line below once vertex shaders are supported by llc +; llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC + +; This test ensures that a Vertex shader with a UAV resource from heap +; does *not* set the module and shader feature flag UAVsAtEveryStage. + +target triple = "dxil-pc-shadermodel6.5-vertex" + +; CHECK: Combined Shader Flags for Module +; CHECK-NOT: Shader Flags Value: 0x400000000 + +; CHECK: Note: shader requires additional functionality: +; CHECK-NOT: UAVs at every shader stage +; CHECK: Resource descriptor heap indexing + +; CHECK: Function VSMain : 0x40000000 +define void @VSMain() { + ; RWBuffer Buf : register(u0, space0) + %buf0 = call target("dx.TypedBuffer", float, 1, 0, 1) + @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_f32_1_0t(i32 7) + ret void +} + +!dx.valver = !{!1} +!1 = !{i32 1, i32 8} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"dx.resmayalias", i32 1} + +; DXC: - Name: SFI0 +; DXC-NEXT: Size: 8 +; DXC-NEXT: Flags: +; DXC: UAVsAtEveryStage: false +; DXC: NextUnusedBit: false +; DXC: ... From b6f6a490389029934cdecdf296cd7071db71b92d Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 24 Aug 2026 10:33:39 -0700 Subject: [PATCH 11/11] clang-format --- llvm/lib/Target/DirectX/DXILShaderFlags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index af86bdf6ca09a..e404eb8109769 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -34,7 +34,7 @@ using namespace llvm::dxil; static bool hasUAVsAtEveryStage(const DXILResourceMap &DRM, const ModuleMetadataInfo &MMDI) { - // Heap resources do not count towards hasUAVsAtEveryStage. + // Heap resources do not count towards hasUAVsAtEveryStage. bool HasUAVWithBinding = any_of( DRM.uavs(), [](const ResourceInfo &RI) { return RI.hasBinding(); }); if (!HasUAVWithBinding)