[llvm] c66f401 - [DirectX] Implement DXILResourceBindingAnalysis (#137258)
via llvm-commits
llvm-commits at lists.llvm.org
Fri May 9 10:42:34 PDT 2025
Author: Helena Kotas
Date: 2025-05-09T10:42:31-07:00
New Revision: c66f401e1eb6157cd0114634b27004cd78cfe5aa
URL: https://github.com/llvm/llvm-project/commit/c66f401e1eb6157cd0114634b27004cd78cfe5aa
DIFF: https://github.com/llvm/llvm-project/commit/c66f401e1eb6157cd0114634b27004cd78cfe5aa.diff
LOG: [DirectX] Implement DXILResourceBindingAnalysis (#137258)
`DXILResourceBindingAnalysis` analyses explicit resource bindings in the
module and puts together lists of used virtual register spaces and
available virtual register slot ranges for each binding type. It also
stores additional information found during the analysis such as whether
the module uses implicit bindings or if any of the bindings overlap.
This information will be used in `DXILResourceImplicitBindings` pass
(coming soon) to assign register slots to resources with implicit
bindings, and in a post-optimization validation pass that will raise
diagnostic about overlapping bindings.
Part 1/2 of #136786
Added:
llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
Modified:
llvm/include/llvm/Analysis/DXILResource.h
llvm/include/llvm/IR/IntrinsicsDirectX.td
llvm/include/llvm/InitializePasses.h
llvm/lib/Analysis/Analysis.cpp
llvm/lib/Analysis/DXILResource.cpp
llvm/lib/Passes/PassRegistry.def
llvm/unittests/Target/DirectX/CMakeLists.txt
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 3f62981d37acd..cfeed5c4ef76f 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -10,6 +10,7 @@
#define LLVM_ANALYSIS_DXILRESOURCE_H
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
@@ -17,6 +18,8 @@
#include "llvm/Pass.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/DXILABI.h"
+#include <climits>
+#include <cstdint>
namespace llvm {
class CallInst;
@@ -586,6 +589,125 @@ class DXILResourceWrapperPass : public ModulePass {
ModulePass *createDXILResourceWrapperPassPass();
+//===----------------------------------------------------------------------===//
+
+// DXILResourceBindingInfo stores the results of DXILResourceBindingAnalysis
+// which analyses all llvm.dx.resource.handlefrombinding calls in the module
+// and puts together lists of used virtual register spaces and available
+// virtual register slot ranges for each binding type.
+// It also stores additional information found during the analysis such as
+// whether the module uses implicit bindings or if any of the bindings overlap.
+//
+// This information will be used in DXILResourceImplicitBindings pass to assign
+// register slots to resources with implicit bindings, and in a
+// post-optimization validation pass that will raise diagnostic about
+// overlapping bindings.
+//
+// For example for these resource bindings:
+//
+// RWBuffer<float> A[10] : register(u3);
+// RWBuffer<float> B[] : register(u5, space2)
+//
+// The analysis result for UAV binding type will look like this:
+//
+// UAVSpaces {
+// ResClass = ResourceClass::UAV,
+// Spaces = {
+// { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
+// { Space = 2, FreeRanges = {{ 0, 4 }} }
+// }
+// }
+//
+class DXILResourceBindingInfo {
+public:
+ struct BindingRange {
+ uint32_t LowerBound;
+ uint32_t UpperBound;
+ BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {}
+ };
+
+ struct RegisterSpace {
+ uint32_t Space;
+ SmallVector<BindingRange> FreeRanges;
+ RegisterSpace(uint32_t Space) : Space(Space) {
+ FreeRanges.emplace_back(0, UINT32_MAX);
+ }
+ };
+
+ struct BindingSpaces {
+ dxil::ResourceClass RC;
+ llvm::SmallVector<RegisterSpace> Spaces;
+ BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
+ };
+
+private:
+ BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
+ bool ImplicitBinding;
+ bool OverlappingBinding;
+
+ // Populate the resource binding info given explicit resource binding calls
+ // in the module.
+ void populate(Module &M, DXILResourceTypeMap &DRTM);
+
+public:
+ DXILResourceBindingInfo()
+ : SRVSpaces(dxil::ResourceClass::SRV),
+ UAVSpaces(dxil::ResourceClass::UAV),
+ CBufferSpaces(dxil::ResourceClass::CBuffer),
+ SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
+ OverlappingBinding(false) {}
+
+ bool hasImplicitBinding() const { return ImplicitBinding; }
+ bool hasOverlappingBinding() const { return OverlappingBinding; }
+
+ BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
+ switch (RC) {
+ case dxil::ResourceClass::SRV:
+ return SRVSpaces;
+ case dxil::ResourceClass::UAV:
+ return UAVSpaces;
+ case dxil::ResourceClass::CBuffer:
+ return CBufferSpaces;
+ case dxil::ResourceClass::Sampler:
+ return SamplerSpaces;
+ }
+ }
+
+ friend class DXILResourceBindingAnalysis;
+ friend class DXILResourceBindingWrapperPass;
+};
+
+class DXILResourceBindingAnalysis
+ : public AnalysisInfoMixin<DXILResourceBindingAnalysis> {
+ friend AnalysisInfoMixin<DXILResourceBindingAnalysis>;
+
+ static AnalysisKey Key;
+
+public:
+ using Result = DXILResourceBindingInfo;
+
+ DXILResourceBindingInfo run(Module &M, ModuleAnalysisManager &AM);
+};
+
+class DXILResourceBindingWrapperPass : public ModulePass {
+ std::unique_ptr<DXILResourceBindingInfo> BindingInfo;
+
+public:
+ static char ID;
+
+ DXILResourceBindingWrapperPass();
+ ~DXILResourceBindingWrapperPass() override;
+
+ DXILResourceBindingInfo &getBindingInfo() { return *BindingInfo; }
+ const DXILResourceBindingInfo &getBindingInfo() const { return *BindingInfo; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ bool runOnModule(Module &M) override;
+ void releaseMemory() override;
+};
+
+ModulePass *createDXILResourceBindingWrapperPassPass();
+
} // namespace llvm
#endif // LLVM_ANALYSIS_DXILRESOURCE_H
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index b1a27311e2a9c..15209c5d229af 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -27,6 +27,15 @@ def int_dx_resource_handlefrombinding
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
+// Create resource handle with implicit binding in given register space.
+// Returns a `target("dx.")` type appropriate for the kind of resource and
+// the range size and index of the binding.
+def int_dx_resource_handlefromimplicitbinding
+ : DefaultAttrsIntrinsic<
+ [llvm_any_ty],
+ [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
+ [IntrNoMem]>;
+
def int_dx_resource_getpointer
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
[IntrNoMem]>;
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index bff0526d4177a..5a282d17b72c8 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -84,8 +84,9 @@ void initializeDAHPass(PassRegistry &);
void initializeDCELegacyPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
-void initializeDXILResourceWrapperPassPass(PassRegistry &);
+void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
+void initializeDXILResourceWrapperPassPass(PassRegistry &);
void initializeDeadMachineInstructionElimPass(PassRegistry &);
void initializeDebugifyMachineModulePass(PassRegistry &);
void initializeDependenceAnalysisWrapperPassPass(PassRegistry &);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 43ea30f3a9a81..484a456f49f1b 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -27,8 +27,9 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCallGraphViewerPass(Registry);
initializeCycleInfoWrapperPassPass(Registry);
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
- initializeDXILResourceWrapperPassPass(Registry);
+ initializeDXILResourceBindingWrapperPassPass(Registry);
initializeDXILResourceTypeWrapperPassPass(Registry);
+ initializeDXILResourceWrapperPassPass(Registry);
initializeDependenceAnalysisWrapperPassPass(Registry);
initializeDominanceFrontierWrapperPassPass(Registry);
initializeDomViewerWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 96e1b44a17f30..125d2420c6c9a 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -8,7 +8,9 @@
#include "llvm/Analysis/DXILResource.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -19,6 +21,8 @@
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/FormatVariadic.h"
+#include <climits>
+#include <cstdint>
#define DEBUG_TYPE "dxil-resource"
@@ -879,8 +883,126 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
//===----------------------------------------------------------------------===//
+void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
+ struct Binding {
+ ResourceClass RC;
+ uint32_t Space;
+ uint32_t LowerBound;
+ uint32_t UpperBound;
+ Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,
+ uint32_t UpperBound)
+ : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound) {
+ }
+ };
+ SmallVector<Binding> Bindings;
+
+ // collect all of the llvm.dx.resource.handlefrombinding calls;
+ // make a note if there is llvm.dx.resource.handlefromimplicitbinding
+ for (Function &F : M.functions()) {
+ if (!F.isDeclaration())
+ continue;
+
+ switch (F.getIntrinsicID()) {
+ default:
+ continue;
+ case Intrinsic::dx_resource_handlefrombinding: {
+ auto *HandleTy = cast<TargetExtType>(F.getReturnType());
+ ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+ for (User *U : F.users())
+ if (CallInst *CI = dyn_cast<CallInst>(U)) {
+ uint32_t Space =
+ cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
+ uint32_t LowerBound =
+ cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
+ int32_t Size =
+ cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
+
+ // negative size means unbounded resource array;
+ // upper bound register overflow should be detected in Sema
+ assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&
+ "upper bound register overflow");
+ uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
+ Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,
+ UpperBound);
+ }
+ break;
+ }
+ case Intrinsic::dx_resource_handlefromimplicitbinding: {
+ ImplicitBinding = true;
+ break;
+ }
+ }
+ }
+
+ // sort all the collected bindings
+ llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {
+ return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) <
+ std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
+ });
+
+ // remove duplicates
+ Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) {
+ return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound) ==
+ std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound);
+ });
+ if (NewEnd != Bindings.end())
+ Bindings.erase(NewEnd);
+
+ // Go over the sorted bindings and build up lists of free register ranges
+ // for each binding type and used spaces. Bindings are sorted by resource
+ // class, space, and lower bound register slot.
+ BindingSpaces *BS = &SRVSpaces;
+ for (unsigned I = 0, E = Bindings.size(); I != E; ++I) {
+ Binding &B = Bindings[I];
+
+ if (BS->RC != B.RC)
+ // move to the next resource class spaces
+ BS = &getBindingSpaces(B.RC);
+
+ RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)
+ : &BS->Spaces.back();
+ assert(S->Space <= B.Space && "bindings not sorted correctly?");
+ if (B.Space != S->Space)
+ // add new space
+ S = &BS->Spaces.emplace_back(B.Space);
+
+ // the space is full - set flag to report overlapping binding later
+ if (S->FreeRanges.empty()) {
+ OverlappingBinding = true;
+ continue;
+ }
+
+ // adjust the last free range lower bound, split it in two, or remove it
+ BindingRange &LastFreeRange = S->FreeRanges.back();
+ assert(LastFreeRange.UpperBound == UINT32_MAX);
+ if (LastFreeRange.LowerBound == B.LowerBound) {
+ if (B.UpperBound < UINT32_MAX)
+ LastFreeRange.LowerBound = B.UpperBound + 1;
+ else
+ S->FreeRanges.pop_back();
+ } else if (LastFreeRange.LowerBound < B.LowerBound) {
+ LastFreeRange.UpperBound = B.LowerBound - 1;
+ if (B.UpperBound < UINT32_MAX)
+ S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
+ } else {
+ // FIXME: This only detects overlapping bindings that are not an exact
+ // match (llvm/llvm-project#110723)
+ OverlappingBinding = true;
+ if (B.UpperBound < UINT32_MAX)
+ LastFreeRange.LowerBound =
+ std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
+ else
+ S->FreeRanges.pop_back();
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+
AnalysisKey DXILResourceTypeAnalysis::Key;
AnalysisKey DXILResourceAnalysis::Key;
+AnalysisKey DXILResourceBindingAnalysis::Key;
DXILResourceMap DXILResourceAnalysis::run(Module &M,
ModuleAnalysisManager &AM) {
@@ -890,6 +1012,14 @@ DXILResourceMap DXILResourceAnalysis::run(Module &M,
return Data;
}
+DXILResourceBindingInfo
+DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
+ DXILResourceBindingInfo Data;
+ DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+ Data.populate(M, DRTM);
+ return Data;
+}
+
PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
ModuleAnalysisManager &AM) {
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
@@ -952,3 +1082,33 @@ char DXILResourceWrapperPass::ID = 0;
ModulePass *llvm::createDXILResourceWrapperPassPass() {
return new DXILResourceWrapperPass();
}
+
+DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
+ : ModulePass(ID) {}
+
+DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
+
+void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
+ AU.setPreservesAll();
+}
+
+bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
+ BindingInfo.reset(new DXILResourceBindingInfo());
+
+ DXILResourceTypeMap &DRTM =
+ getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+ BindingInfo->populate(M, DRTM);
+
+ return false;
+}
+
+void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }
+
+INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
+ "DXIL Resource Binding Analysis", false, true)
+char DXILResourceBindingWrapperPass::ID = 0;
+
+ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
+ return new DXILResourceWrapperPass();
+}
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index d5d1b2173da69..ea792280ed975 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -24,6 +24,7 @@ MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis())
MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis())
MODULE_ANALYSIS("dxil-resources", DXILResourceAnalysis())
MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis())
+MODULE_ANALYSIS("dxil-resource-bindings", DXILResourceBindingAnalysis())
MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis())
MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis())
MODULE_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis())
diff --git a/llvm/unittests/Target/DirectX/CMakeLists.txt b/llvm/unittests/Target/DirectX/CMakeLists.txt
index b1359b37ad521..2c135c1b4b499 100644
--- a/llvm/unittests/Target/DirectX/CMakeLists.txt
+++ b/llvm/unittests/Target/DirectX/CMakeLists.txt
@@ -22,4 +22,5 @@ add_llvm_target_unittest(DirectXTests
PointerTypeAnalysisTests.cpp
UniqueResourceFromUseTests.cpp
RegisterCostTests.cpp
+ ResourceBindingAnalysisTests.cpp
)
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
new file mode 100644
index 0000000000000..9be4cc3045195
--- /dev/null
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -0,0 +1,291 @@
+//===- llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/DXILABI.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+class ResourceBindingAnalysisTest : public testing::Test {
+protected:
+ PassBuilder *PB;
+ ModuleAnalysisManager *MAM;
+ LLVMContext *Context;
+
+ virtual void SetUp() {
+ PB = new PassBuilder();
+ MAM = new ModuleAnalysisManager();
+ Context = new LLVMContext();
+ PB->registerModuleAnalyses(*MAM);
+ MAM->registerPass([&] { return DXILResourceBindingAnalysis(); });
+ }
+
+ std::unique_ptr<Module> parseAsm(StringRef Asm) {
+ SMDiagnostic Error;
+ std::unique_ptr<Module> M = parseAssemblyString(Asm, Error, *Context);
+ EXPECT_TRUE(M) << "Bad assembly?: " << Error.getMessage();
+ return M;
+ }
+
+ virtual void TearDown() {
+ delete PB;
+ delete MAM;
+ delete Context;
+ }
+
+ void checkExpectedSpaceAndFreeRanges(
+ DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
+ ArrayRef<uint32_t> ExpValues) {
+ EXPECT_EQ(RegSpace.Space, ExpSpace);
+ EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size());
+ unsigned I = 0;
+ for (auto &R : RegSpace.FreeRanges) {
+ EXPECT_EQ(R.LowerBound, ExpValues[I]);
+ EXPECT_EQ(R.UpperBound, ExpValues[I + 1]);
+ I += 2;
+ }
+ }
+};
+
+TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) {
+ // RWBuffer<float> Buf : register(u5);
+ StringRef Assembly = R"(
+define void @main() {
+entry:
+ %handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ // check that UAV has exactly one gap
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 4, 6, UINT32_MAX});
+
+ // check that other kinds of register spaces are all available
+ for (auto RC :
+ {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
+ DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
+ EXPECT_EQ(Spaces.RC, RC);
+ EXPECT_EQ(Spaces.Spaces.size(), 0u);
+ }
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestManyBindings) {
+ // cbuffer CB : register(b3) { int a; }
+ // RWBuffer<float4> A[5] : register(u10, space20);
+ // StructuredBuffer<int> B : register(t5);
+ // RWBuffer<float> C : register(u5);
+ // StructuredBuffer<int> D[5] : register(t0);
+ // RWBuffer<float> E[2] : register(u2);
+ // SamplerState S1 : register(s5, space2);
+ // SamplerState S2 : register(s4, space2);
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleCB = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, i1 false)
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 20, i32 10, i32 5, i32 0, i1 false)
+ %handleB = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false)
+ %handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false)
+ %handleS1 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 5, i32 1, i32 0, i1 false)
+ %handleS2 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false)
+ ; duplicate binding for the same resource
+ %handleD2 = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
+ // verify that consecutive bindings are merged
+ // (SRVSpaces has only one free space range {6, UINT32_MAX}).
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
+
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 1, 4, 4, 6, UINT32_MAX});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
+ {0, 9, 15, UINT32_MAX});
+
+ DXILResourceBindingInfo::BindingSpaces &CBufferSpaces =
+ DRBI.getBindingSpaces(ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.RC, ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0,
+ {0, 2, 4, UINT32_MAX});
+
+ DXILResourceBindingInfo::BindingSpaces &SamplerSpaces =
+ DRBI.getBindingSpaces(ResourceClass::Sampler);
+ EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler);
+ EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SamplerSpaces.Spaces[0], 2,
+ {0, 3, 6, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestUnboundedAndOverlap) {
+ // StructuredBuffer<float> A[] : register(t5);
+ // StructuredBuffer<float> B[3] : register(t0);
+ // StructuredBuffer<float> C[] : register(t0, space2);
+ // StructuredBuffer<float> D : register(t4, space2); /* overlapping */
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 -1, i32 10, i1 false)
+ %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, i1 false)
+ %handleC = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false)
+ %handleD = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(true, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {3, 4});
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[1], 2, {});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestExactOverlap) {
+ // StructuredBuffer<float> A : register(t5);
+ // StructuredBuffer<float> B : register(t5);
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ // FIXME (XFAIL): detecting overlap of two resource with identical binding
+ // is not yet supported (llvm/llvm-project#110723).
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0,
+ {0, 4, 6, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestEndOfRange) {
+ // RWBuffer<float> A : register(u4294967295); /* UINT32_MAX */
+ // RWBuffer<float> B[10] : register(u4294967286, space1);
+ // /* range (UINT32_MAX - 9, UINT32_MAX )*/
+ // RWBuffer<float> C[10] : register(u2147483647, space2);
+ // /* range (INT32_MAX, INT32_MAX + 9) */
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 -1, i32 1, i32 0, i1 false)
+ %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 -10, i32 10, i32 50, i1 false)
+ %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2147483647, i32 10, i32 100, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, {0, UINT32_MAX - 1});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 1, {0, UINT32_MAX - 10});
+ checkExpectedSpaceAndFreeRanges(
+ UAVSpaces.Spaces[2], 2,
+ {0, (uint32_t)INT32_MAX - 1, (uint32_t)INT32_MAX + 10, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestImplicitFlag) {
+ // RWBuffer<float> A : register(u5, space100);
+ // RWBuffer<float> B;
+ StringRef Assembly = R"(
+define void @main() {
+entry:
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 100, i32 5, i32 1, i32 0, i1 false)
+ %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+ EXPECT_EQ(true, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 100,
+ {0, 4, 6, UINT32_MAX});
+}
+
+} // namespace
More information about the llvm-commits
mailing list