[llvm-branch-commits] [llvm] wip: Extract HLSLBinding out of DXILResource (PR #150633)
Justin Bogner via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jul 25 08:32:18 PDT 2025
https://github.com/bogner created https://github.com/llvm/llvm-project/pull/150633
TODO:
- Move unit tests
- Slim API
>From 07535c4db1ed03999b97cf263b086ecc07749efd Mon Sep 17 00:00:00 2001
From: Justin Bogner <mail at justinbogner.com>
Date: Wed, 23 Jul 2025 13:40:01 -0700
Subject: [PATCH] wip: Extract HLSLBinding out of DXILResource
TODO:
- Move unit tests
- Slim API
---
llvm/include/llvm/Analysis/DXILResource.h | 82 ++--------
llvm/include/llvm/Frontend/HLSL/HLSLBinding.h | 154 ++++++++++++++++++
llvm/lib/Analysis/CMakeLists.txt | 1 +
llvm/lib/Analysis/DXILResource.cpp | 136 +---------------
llvm/lib/Frontend/HLSL/CMakeLists.txt | 1 +
llvm/lib/Frontend/HLSL/HLSLBinding.cpp | 141 ++++++++++++++++
.../DirectX/ResourceBindingAnalysisTests.cpp | 27 +--
7 files changed, 330 insertions(+), 212 deletions(-)
create mode 100644 llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
create mode 100644 llvm/lib/Frontend/HLSL/HLSLBinding.cpp
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 956dcbcc33305..56f5c6aaf13e7 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/HLSL/HLSLBinding.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/PassManager.h"
@@ -633,86 +634,29 @@ LLVM_ABI ModulePass *createDXILResourceWrapperPassPass();
// 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);
- }
- // Size == -1 means unbounded array
- LLVM_ABI std::optional<uint32_t> findAvailableBinding(int32_t Size);
- };
-
- struct BindingSpaces {
- dxil::ResourceClass RC;
- llvm::SmallVector<RegisterSpace> Spaces;
- BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
- LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space);
- };
-
-private:
- BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
- bool ImplicitBinding;
- bool OverlappingBinding;
+ hlsl::BindingInfo Bindings;
+ bool HasImplicitBinding = false;
+ bool HasOverlappingBinding = false;
// 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; }
- void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
- 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;
- }
+ bool hasImplicitBinding() const { return HasImplicitBinding; }
+ void setHasImplicitBinding(bool Value) { HasImplicitBinding = Value; }
+ bool hasOverlappingBinding() const { return HasOverlappingBinding; }
+ void setHasOverlappingBinding(bool Value) { HasOverlappingBinding = Value; }
- llvm_unreachable("Invalid resource class");
+ hlsl::BindingInfo::BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
+ return Bindings.getBindingSpaces(RC);
}
- // Size == -1 means unbounded array
LLVM_ABI std::optional<uint32_t>
- findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size);
+ findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size) {
+ return Bindings.findAvailableBinding(RC, Space, Size);
+ }
friend class DXILResourceBindingAnalysis;
friend class DXILResourceBindingWrapperPass;
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
new file mode 100644
index 0000000000000..6e4bd8b71ac4a
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
@@ -0,0 +1,154 @@
+//===- HLSLBinding.h - Representation for resource bindings in HLSL -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file contains objects to represent resource bindings.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_HLSL_HLSLBINDING_H
+#define LLVM_FRONTEND_HLSL_HLSLBINDING_H
+
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/DXILABI.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace llvm {
+namespace hlsl {
+
+/// BindingInfo represents the ranges of bindings and free space for each
+/// `dxil::ResourceClass`. This can represent HLSL-level bindings as well as
+/// bindings described in root signatures, and can be used for analysis of
+/// overlapping or missing bindings as well as for finding space for implicit
+/// bindings.
+///
+/// As an example, given these resource bindings:
+///
+/// RWBuffer<float> A[10] : register(u3);
+/// RWBuffer<float> B[] : register(u5, space2)
+///
+/// The binding info for UAV bindings should look like this:
+///
+/// UAVSpaces {
+/// ResClass = ResourceClass::UAV,
+/// Spaces = {
+/// { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
+/// { Space = 2, FreeRanges = {{ 0, 4 }} }
+/// }
+/// }
+class BindingInfo {
+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);
+ }
+ // Size == -1 means unbounded array
+ LLVM_ABI std::optional<uint32_t> findAvailableBinding(int32_t Size);
+ };
+
+ struct BindingSpaces {
+ dxil::ResourceClass RC;
+ llvm::SmallVector<RegisterSpace> Spaces;
+ BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
+ LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space);
+ };
+
+private:
+ BindingSpaces SRVSpaces{dxil::ResourceClass::SRV};
+ BindingSpaces UAVSpaces{dxil::ResourceClass::UAV};
+ BindingSpaces CBufferSpaces{dxil::ResourceClass::CBuffer};
+ BindingSpaces SamplerSpaces{dxil::ResourceClass::Sampler};
+
+public:
+ 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;
+ }
+
+ llvm_unreachable("Invalid resource class");
+ }
+ const BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) const {
+ return const_cast<BindingInfo *>(this)->getBindingSpaces(RC);
+ }
+
+ // Size == -1 means unbounded array
+ LLVM_ABI std::optional<uint32_t>
+ findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size);
+
+ friend class BindingInfoBuilder;
+};
+
+/// Builder class for creating a /c BindingInfo.
+class BindingInfoBuilder {
+public:
+ struct Binding {
+ const static uint32_t Unbounded = ~0u;
+
+ dxil::ResourceClass RC;
+ uint32_t Space;
+ uint32_t LowerBound;
+ uint32_t UpperBound;
+ const void *Cookie;
+
+ Binding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
+ uint32_t UpperBound, const void *Cookie)
+ : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
+ Cookie(Cookie) {}
+
+ bool isUnbounded() const { return UpperBound == Unbounded; }
+
+ bool operator==(const Binding &RHS) const {
+ return std::tie(RC, Space, LowerBound, UpperBound, Cookie) ==
+ std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound,
+ RHS.Cookie);
+ }
+ bool operator!=(const Binding &RHS) const { return !(*this == RHS); }
+
+ bool operator<(const Binding &RHS) const {
+ return std::tie(RC, Space, LowerBound) <
+ std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
+ }
+ };
+
+private:
+ SmallVector<Binding> Bindings;
+
+public:
+ void trackBinding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
+ uint32_t UpperBound, const void *Cookie) {
+ Bindings.emplace_back(RC, Space, LowerBound, UpperBound, Cookie);
+ }
+ BindingInfo calculateBindingInfo(
+ llvm::function_ref<void(const BindingInfoBuilder &Builder,
+ const Binding &Overlapping)>
+ ReportOverlap);
+
+ /// For use in the \c ReportOverlap callback of \c calculateBindingInfo -
+ /// finds a binding that the \c ReportedBinding overlaps with.
+ const Binding &findOverlapping(const Binding &ReportedBinding) const;
+};
+
+} // namespace hlsl
+} // namespace llvm
+
+#endif // LLVM_FRONTEND_HLSL_HLSLBINDING_H
diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt
index cfde787b17790..16dd6f8b86006 100644
--- a/llvm/lib/Analysis/CMakeLists.txt
+++ b/llvm/lib/Analysis/CMakeLists.txt
@@ -175,6 +175,7 @@ add_llvm_component_library(LLVMAnalysis
LINK_COMPONENTS
BinaryFormat
Core
+ FrontendHLSL
Object
ProfileData
Support
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 179b7b4dc17f9..629fa7cddb9d4 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,18 +995,7 @@ 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;
- Value *Name;
- Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,
- uint32_t UpperBound, Value *Name)
- : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
- Name(Name) {}
- };
- SmallVector<Binding> Bindings;
+ hlsl::BindingInfoBuilder Builder;
// collect all of the llvm.dx.resource.handlefrombinding calls;
// make a note if there is llvm.dx.resource.handlefromimplicitbinding
@@ -1036,133 +1025,20 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
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, Name);
+ Builder.trackBinding(RTI.getResourceClass(), Space, LowerBound,
+ UpperBound, Name);
}
break;
}
case Intrinsic::dx_resource_handlefromimplicitbinding: {
- ImplicitBinding = true;
+ HasImplicitBinding = 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,
- LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound,
- RHS.UpperBound, RHS.Name);
- });
- 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 (const Binding &B : Bindings) {
- 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 - there are no free slots left, or the rest of the
- // slots are taken by an unbouded array. Set flag to report overlapping
- // binding later.
- if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < UINT32_MAX) {
- OverlappingBinding = true;
- continue;
- }
-
- // adjust the last free range lower bound, split it in two, or remove it
- BindingRange &LastFreeRange = S->FreeRanges.back();
- 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 {
- OverlappingBinding = true;
- if (B.UpperBound < UINT32_MAX)
- LastFreeRange.LowerBound =
- std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
- else
- S->FreeRanges.pop_back();
- }
- }
-}
-
-// returns std::nulopt if binding could not be found in given space
-std::optional<uint32_t>
-DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
- uint32_t Space, int32_t Size) {
- BindingSpaces &BS = getBindingSpaces(RC);
- RegisterSpace &RS = BS.getOrInsertSpace(Space);
- return RS.findAvailableBinding(Size);
-}
-
-DXILResourceBindingInfo::RegisterSpace &
-DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
- for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
- if (I->Space == Space)
- return *I;
- if (I->Space < Space)
- continue;
- return *Spaces.insert(I, Space);
- }
- return Spaces.emplace_back(Space);
-}
-
-std::optional<uint32_t>
-DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
- assert((Size == -1 || Size > 0) && "invalid size");
-
- if (FreeRanges.empty())
- return std::nullopt;
-
- // unbounded array
- if (Size == -1) {
- BindingRange &Last = FreeRanges.back();
- if (Last.UpperBound != UINT32_MAX)
- // this space is already occupied by an unbounded array
- return std::nullopt;
- uint32_t RegSlot = Last.LowerBound;
- FreeRanges.pop_back();
- return RegSlot;
- }
-
- // single resource or fixed-size array
- for (BindingRange &R : FreeRanges) {
- // compare the size as uint64_t to prevent overflow for range (0,
- // UINT32_MAX)
- if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
- continue;
- uint32_t RegSlot = R.LowerBound;
- // This might create a range where (LowerBound == UpperBound + 1). When
- // that happens, the next time this function is called the range will
- // skipped over by the check above (at this point Size is always > 0).
- R.LowerBound += Size;
- return RegSlot;
- }
-
- return std::nullopt;
+ Bindings = Builder.calculateBindingInfo(
+ [this](auto, auto) { this->HasOverlappingBinding = true; });
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Frontend/HLSL/CMakeLists.txt b/llvm/lib/Frontend/HLSL/CMakeLists.txt
index 534346920ff19..3d225770e8d5b 100644
--- a/llvm/lib/Frontend/HLSL/CMakeLists.txt
+++ b/llvm/lib/Frontend/HLSL/CMakeLists.txt
@@ -1,5 +1,6 @@
add_llvm_component_library(LLVMFrontendHLSL
CBuffer.cpp
+ HLSLBinding.cpp
HLSLResource.cpp
HLSLRootSignature.cpp
RootSignatureMetadata.cpp
diff --git a/llvm/lib/Frontend/HLSL/HLSLBinding.cpp b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
new file mode 100644
index 0000000000000..c2ae4a44bb5d0
--- /dev/null
+++ b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
@@ -0,0 +1,141 @@
+//===- HLSLBinding.cpp - Representation for resource bindings in HLSL -----===//
+//
+// 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/Frontend/HLSL/HLSLBinding.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm;
+using namespace hlsl;
+
+std::optional<uint32_t>
+BindingInfo::findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
+ int32_t Size) {
+ BindingSpaces &BS = getBindingSpaces(RC);
+ RegisterSpace &RS = BS.getOrInsertSpace(Space);
+ return RS.findAvailableBinding(Size);
+}
+
+BindingInfo::RegisterSpace &
+BindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
+ for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
+ if (I->Space == Space)
+ return *I;
+ if (I->Space < Space)
+ continue;
+ return *Spaces.insert(I, Space);
+ }
+ return Spaces.emplace_back(Space);
+}
+
+std::optional<uint32_t>
+BindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
+ assert((Size == -1 || Size > 0) && "invalid size");
+
+ if (FreeRanges.empty())
+ return std::nullopt;
+
+ // unbounded array
+ if (Size == -1) {
+ BindingRange &Last = FreeRanges.back();
+ if (Last.UpperBound != UINT32_MAX)
+ // this space is already occupied by an unbounded array
+ return std::nullopt;
+ uint32_t RegSlot = Last.LowerBound;
+ FreeRanges.pop_back();
+ return RegSlot;
+ }
+
+ // single resource or fixed-size array
+ for (BindingRange &R : FreeRanges) {
+ // compare the size as uint64_t to prevent overflow for range (0,
+ // UINT32_MAX)
+ if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
+ continue;
+ uint32_t RegSlot = R.LowerBound;
+ // This might create a range where (LowerBound == UpperBound + 1). When
+ // that happens, the next time this function is called the range will
+ // skipped over by the check above (at this point Size is always > 0).
+ R.LowerBound += Size;
+ return RegSlot;
+ }
+
+ return std::nullopt;
+}
+
+BindingInfo BindingInfoBuilder::calculateBindingInfo(
+ llvm::function_ref<void(const BindingInfoBuilder &Builder,
+ const Binding &Overlapping)>
+ ReportOverlap) {
+ // sort all the collected bindings
+ llvm::stable_sort(Bindings);
+
+ // remove duplicates
+ Binding *NewEnd = llvm::unique(Bindings);
+ if (NewEnd != Bindings.end())
+ Bindings.erase(NewEnd);
+
+ BindingInfo Info;
+
+ // 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.
+ BindingInfo::BindingSpaces *BS =
+ &Info.getBindingSpaces(dxil::ResourceClass::SRV);
+ for (const Binding &B : Bindings) {
+ if (BS->RC != B.RC)
+ // move to the next resource class spaces
+ BS = &Info.getBindingSpaces(B.RC);
+
+ BindingInfo::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 - there are no free slots left, or the rest of the
+ // slots are taken by an unbounded array. Set flag to report overlapping
+ // binding later.
+ if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < UINT32_MAX) {
+ ReportOverlap(*this, B);
+ continue;
+ }
+ // adjust the last free range lower bound, split it in two, or remove it
+ BindingInfo::BindingRange &LastFreeRange = S->FreeRanges.back();
+ 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 {
+ ReportOverlap(*this, B);
+ if (B.UpperBound < UINT32_MAX)
+ LastFreeRange.LowerBound =
+ std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
+ else
+ S->FreeRanges.pop_back();
+ }
+ }
+
+ return Info;
+}
+
+const BindingInfoBuilder::Binding &BindingInfoBuilder::findOverlapping(
+ const BindingInfoBuilder::Binding &ReportedBinding) const {
+ for (const BindingInfoBuilder::Binding &Other : Bindings)
+ if (ReportedBinding.LowerBound <= Other.UpperBound &&
+ Other.LowerBound <= ReportedBinding.UpperBound)
+ return Other;
+
+ llvm_unreachable("Searching for overlap for binding that does not overlap");
+}
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index 6cd1a48d8d5fd..7cd83bedd6140 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -45,9 +45,10 @@ class ResourceBindingAnalysisTest : public testing::Test {
delete Context;
}
- void checkExpectedSpaceAndFreeRanges(
- DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
- ArrayRef<uint32_t> ExpValues) {
+ void
+ checkExpectedSpaceAndFreeRanges(hlsl::BindingInfo::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;
@@ -78,7 +79,7 @@ define void @main() {
EXPECT_EQ(false, DRBI.hasOverlappingBinding());
// check that UAV has exactly one gap
- DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
@@ -88,7 +89,7 @@ define void @main() {
// 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);
+ hlsl::BindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
EXPECT_EQ(Spaces.RC, RC);
EXPECT_EQ(Spaces.Spaces.size(), 0u);
}
@@ -129,7 +130,7 @@ define void @main() {
EXPECT_EQ(false, DRBI.hasImplicitBinding());
EXPECT_EQ(false, DRBI.hasOverlappingBinding());
- DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
@@ -137,7 +138,7 @@ define void @main() {
// (SRVSpaces has only one free space range {6, UINT32_MAX}).
checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
- DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
@@ -146,14 +147,14 @@ define void @main() {
checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
{0, 9, 15, UINT32_MAX});
- DXILResourceBindingInfo::BindingSpaces &CBufferSpaces =
+ hlsl::BindingInfo::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 =
+ hlsl::BindingInfo::BindingSpaces &SamplerSpaces =
DRBI.getBindingSpaces(ResourceClass::Sampler);
EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler);
EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u);
@@ -185,7 +186,7 @@ define void @main() {
EXPECT_EQ(false, DRBI.hasImplicitBinding());
EXPECT_EQ(true, DRBI.hasOverlappingBinding());
- DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.Spaces.size(), 2u);
@@ -215,7 +216,7 @@ define void @main() {
EXPECT_EQ(false, DRBI.hasImplicitBinding());
EXPECT_EQ(true, DRBI.hasOverlappingBinding());
- DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
@@ -248,7 +249,7 @@ define void @main() {
EXPECT_EQ(false, DRBI.hasImplicitBinding());
EXPECT_EQ(false, DRBI.hasOverlappingBinding());
- DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
@@ -278,7 +279,7 @@ define void @main() {
EXPECT_EQ(true, DRBI.hasImplicitBinding());
EXPECT_EQ(false, DRBI.hasOverlappingBinding());
- DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
More information about the llvm-branch-commits
mailing list