[llvm] 3f066f5 - [HLSL][DirectX] Extract HLSLBinding out of DXILResource. NFC (#150633)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 31 08:35:50 PDT 2025
Author: Justin Bogner
Date: 2025-07-31T08:35:47-07:00
New Revision: 3f066f5fcfbab3784b2ecc54744c2d7000a8c4fc
URL: https://github.com/llvm/llvm-project/commit/3f066f5fcfbab3784b2ecc54744c2d7000a8c4fc
DIFF: https://github.com/llvm/llvm-project/commit/3f066f5fcfbab3784b2ecc54744c2d7000a8c4fc.diff
LOG: [HLSL][DirectX] Extract HLSLBinding out of DXILResource. NFC (#150633)
We extract the binding logic out of the DXILResource analysis passes into the
FrontendHLSL library. This will allow us to use this logic for resource and
root signature bindings in both the DirectX backend and the HLSL frontend.
Added:
llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
llvm/lib/Frontend/HLSL/HLSLBinding.cpp
llvm/unittests/Frontend/HLSLBindingTest.cpp
Modified:
llvm/include/llvm/Analysis/DXILResource.h
llvm/lib/Analysis/CMakeLists.txt
llvm/lib/Analysis/DXILResource.cpp
llvm/lib/Frontend/HLSL/CMakeLists.txt
llvm/unittests/Frontend/CMakeLists.txt
llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 956dcbcc33305..93c6bfb057ef5 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,25 @@ 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");
- }
-
- // 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..70a2eeb632f1b
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h
@@ -0,0 +1,162 @@
+//===- 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 = 0u, FreeRanges = {{ 0u, 2u }, { 13u, ~0u }} },
+/// { Space = 2u, FreeRanges = {{ 0u, 4u }} }
+/// }
+/// }
+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, ~0u);
+ }
+ // 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 {
+ 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 == ~0U; }
+
+ 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);
+ }
+ /// Calculate the binding info - \c ReportOverlap will be called once for each
+ /// overlapping binding.
+ BindingInfo calculateBindingInfo(
+ llvm::function_ref<void(const BindingInfoBuilder &Builder,
+ const Binding &Overlapping)>
+ ReportOverlap);
+
+ /// Calculate the binding info - \c HasOverlap will be set to indicate whether
+ /// there are any overlapping bindings.
+ BindingInfo calculateBindingInfo(bool &HasOverlap) {
+ HasOverlap = false;
+ return calculateBindingInfo(
+ [&HasOverlap](auto, auto) { HasOverlap = true; });
+ }
+
+ /// 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 1959ab6e510a3..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 unbounded 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..d581311f22028
--- /dev/null
+++ b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
@@ -0,0 +1,142 @@
+//===- 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 It = Spaces.begin(), End = Spaces.end(); It != End; ++It) {
+ if (It->Space == Space)
+ return *It;
+ if (It->Space < Space)
+ continue;
+ return *Spaces.insert(It, 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 != ~0u)
+ // 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, ~0u)
+ 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. Report the overlapping to the
+ // caller.
+ if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < ~0u) {
+ 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 < ~0u)
+ LastFreeRange.LowerBound = B.UpperBound + 1;
+ else
+ S->FreeRanges.pop_back();
+ } else if (LastFreeRange.LowerBound < B.LowerBound) {
+ LastFreeRange.UpperBound = B.LowerBound - 1;
+ if (B.UpperBound < ~0u)
+ S->FreeRanges.emplace_back(B.UpperBound + 1, ~0u);
+ } else {
+ // We don't have room here. Report the overlapping binding to the caller
+ // and mark any extra space this binding would use as unavailable.
+ ReportOverlap(*this, B);
+ if (B.UpperBound < ~0u)
+ 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/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt
index 281d509227a46..6e4ba5dc0c019 100644
--- a/llvm/unittests/Frontend/CMakeLists.txt
+++ b/llvm/unittests/Frontend/CMakeLists.txt
@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_unittest(LLVMFrontendTests
+ HLSLBindingTest.cpp
HLSLRootSignatureDumpTest.cpp
HLSLRootSignatureRangesTest.cpp
OpenACCTest.cpp
diff --git a/llvm/unittests/Frontend/HLSLBindingTest.cpp b/llvm/unittests/Frontend/HLSLBindingTest.cpp
new file mode 100644
index 0000000000000..bd6a0ff724cf4
--- /dev/null
+++ b/llvm/unittests/Frontend/HLSLBindingTest.cpp
@@ -0,0 +1,273 @@
+//===------ HLSLBindingTest.cpp - Resource binding tests ------------------===//
+//
+// 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/ArrayRef.h"
+#include "llvm/Support/DXILABI.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+MATCHER_P(HasSpecificValue, Value, "") {
+ return arg.has_value() && *arg == Value;
+}
+
+static 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;
+ for (auto &R : RegSpace.FreeRanges) {
+ EXPECT_EQ(R.LowerBound, ExpValues[I]);
+ EXPECT_EQ(R.UpperBound, ExpValues[I + 1]);
+ I += 2;
+ }
+}
+
+TEST(HLSLBindingTest, TestTrivialCase) {
+ hlsl::BindingInfoBuilder Builder;
+
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/nullptr);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_FALSE(HasOverlap);
+
+ // check that UAV has exactly one gap
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
+ Info.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, {0u, 4u, 6u, ~0u});
+
+ // check that other kinds of register spaces are all available
+ for (auto RC :
+ {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
+ hlsl::BindingInfo::BindingSpaces &Spaces = Info.getBindingSpaces(RC);
+ EXPECT_EQ(Spaces.RC, RC);
+ EXPECT_EQ(Spaces.Spaces.size(), 0u);
+ }
+}
+
+TEST(HLSLBindingTest, TestManyBindings) {
+ hlsl::BindingInfoBuilder Builder;
+
+ // 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);
+ Builder.trackBinding(ResourceClass::CBuffer, /*Space=*/0, /*LowerBound=*/3,
+ /*UpperBound=*/3, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/20, /*LowerBound=*/10,
+ /*UpperBound=*/14, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/0,
+ /*UpperBound=*/4, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/2,
+ /*UpperBound=*/3, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::Sampler, /*Space=*/2, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::Sampler, /*Space=*/2, /*LowerBound=*/4,
+ /*UpperBound=*/4, /*Cookie=*/nullptr);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_FALSE(HasOverlap);
+
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
+ Info.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, ~0u}).
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6u, ~0u});
+
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
+ Info.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0u, 1u, 4u, 4u, 6u, ~0u});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20, {0u, 9u, 15u, ~0u});
+
+ hlsl::BindingInfo::BindingSpaces &CBufferSpaces =
+ Info.getBindingSpaces(ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.RC, ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0,
+ {0u, 2u, 4u, ~0u});
+
+ hlsl::BindingInfo::BindingSpaces &SamplerSpaces =
+ Info.getBindingSpaces(ResourceClass::Sampler);
+ EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler);
+ EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SamplerSpaces.Spaces[0], 2,
+ {0u, 3u, 6u, ~0u});
+}
+
+TEST(HLSLBindingTest, TestUnboundedAndOverlap) {
+ hlsl::BindingInfoBuilder Builder;
+
+ // StructuredBuffer<float> A[] : register(t5);
+ // StructuredBuffer<float> B[3] : register(t0);
+ // StructuredBuffer<float> C[] : register(t0, space2);
+ // StructuredBuffer<float> D : register(t4, space2); /* overlapping */
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/~0u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/0,
+ /*UpperBound=*/2, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/2, /*LowerBound=*/0,
+ /*UpperBound=*/~0u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/2, /*LowerBound=*/4,
+ /*UpperBound=*/4, /*Cookie=*/nullptr);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_TRUE(HasOverlap);
+
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
+ Info.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(HLSLBindingTest, TestExactOverlap) {
+ hlsl::BindingInfoBuilder Builder;
+
+ // Since the bindings overlap exactly we need sigil values to
diff erentiate
+ // them.
+ char ID1;
+ char ID2;
+
+ // StructuredBuffer<float> A : register(t5);
+ // StructuredBuffer<float> B : register(t5);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/&ID1);
+ Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5,
+ /*UpperBound=*/5, /*Cookie=*/&ID2);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_TRUE(HasOverlap);
+
+ hlsl::BindingInfo::BindingSpaces &SRVSpaces =
+ Info.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {0u, 4u, 6u, ~0u});
+}
+
+TEST(HLSLBindingTest, TestEndOfRange) {
+ hlsl::BindingInfoBuilder Builder;
+
+ // 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) */
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/~0u,
+ /*UpperBound=*/~0u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/~0u - 9u,
+ /*UpperBound=*/~0u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/2,
+ /*LowerBound=*/2147483647u,
+ /*UpperBound=*/2147483647u + 9u, /*Cookie=*/nullptr);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_FALSE(HasOverlap);
+
+ hlsl::BindingInfo::BindingSpaces &UAVSpaces =
+ Info.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
+ checkExpectedSpaceAndFreeRanges(
+ UAVSpaces.Spaces[0], 0, {0, std::numeric_limits<uint32_t>::max() - 1});
+ checkExpectedSpaceAndFreeRanges(
+ UAVSpaces.Spaces[1], 1, {0, std::numeric_limits<uint32_t>::max() - 10});
+ checkExpectedSpaceAndFreeRanges(
+ UAVSpaces.Spaces[2], 2,
+ {0, static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1u,
+ static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 10u,
+ std::numeric_limits<uint32_t>::max()});
+}
+
+TEST(HLSLBindingTest, TestFindAvailable) {
+ hlsl::BindingInfoBuilder Builder;
+
+ // RWBuffer<float> A : register(u5);
+ // RWBuffer<float> B : register(u5, space1);
+ // RWBuffer<float> C : register(u11, space1);
+ // RWBuffer<float> D[] : register(u1, space2);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5u,
+ /*UpperBound=*/5u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/2u,
+ /*UpperBound=*/2u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/6u,
+ /*UpperBound=*/6u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/2, /*LowerBound=*/1u,
+ /*UpperBound=*/~0u, /*Cookie=*/nullptr);
+ Builder.trackBinding(ResourceClass::UAV, /*Space=*/3, /*LowerBound=*/~0u - 1,
+ /*UpperBound=*/~0u - 1, /*Cookie=*/nullptr);
+ bool HasOverlap;
+ hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap);
+
+ EXPECT_FALSE(HasOverlap);
+
+ // In space 0, we find room for a small binding at the beginning and
+ // a large binding after `A`'s binding.
+ std::optional<uint32_t> V =
+ Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/0, /*Size=*/1);
+ EXPECT_THAT(V, HasSpecificValue(0u));
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/0, /*Size=*/100);
+ EXPECT_THAT(V, HasSpecificValue(6u));
+
+ // In space 1, we try to fit larger bindings in the fill the gaps. Note that
+ // we do this largest to smallest and observe that the gaps that are earlier
+ // still exist.
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/4);
+ EXPECT_THAT(V, HasSpecificValue(7u));
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/3);
+ EXPECT_THAT(V, HasSpecificValue(3u));
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/2);
+ EXPECT_THAT(V, HasSpecificValue(0u));
+ // At this point, we've used all of the contiguous space up to 11u
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/1);
+ EXPECT_THAT(V, HasSpecificValue(11u));
+
+ // Space 2 is mostly full, we can only fit into the room at the beginning.
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/2, /*Size=*/2);
+ EXPECT_FALSE(V.has_value());
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/2, /*Size=*/1);
+ EXPECT_THAT(V, HasSpecificValue(0u));
+
+ // Finding space for an unbounded array is a bit funnier. it always needs to
+ // go a the end of the available space.
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/3,
+ /*Size=*/~0u);
+ // Note that we end up with a size 1 array here, starting at ~0u.
+ EXPECT_THAT(V, HasSpecificValue(~0u));
+ V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/4,
+ /*Size=*/~0u);
+ // In an empty space we find the slot at the beginning.
+ EXPECT_THAT(V, HasSpecificValue(0u));
+}
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index 6cd1a48d8d5fd..d4715bee51d1d 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -44,19 +44,6 @@ class ResourceBindingAnalysisTest : public testing::Test {
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) {
@@ -76,103 +63,16 @@ define void @main() {
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, ptr null)
- %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 20, i32 10, i32 5, i32 0, i1 false, ptr null)
- %handleB = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr null)
- %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr null)
- %handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false, ptr null)
- %handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false, ptr null)
- %handleS1 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 5, i32 1, i32 0, i1 false, ptr null)
- %handleS2 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null)
- ; 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, ptr null)
- 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 */
+TEST_F(ResourceBindingAnalysisTest, TestOverlap) {
+ // StructuredBuffer<float> A[] : register(t0, space2);
+ // StructuredBuffer<float> B : register(t4, space2); /* overlapping */
StringRef Assembly = R"(
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, ptr null)
- %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, i1 false, ptr null)
- %handleC = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false, ptr null)
- %handleD = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null)
+ %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false, ptr null)
+ %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null)
ret void
}
)";
@@ -184,13 +84,6 @@ define void @main() {
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) {
@@ -214,49 +107,6 @@ define void @main() {
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(), 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, ptr null)
- %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 -10, i32 10, i32 50, i1 false, ptr null)
- %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2147483647, i32 10, i32 100, i1 false, ptr null)
- 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) {
@@ -275,15 +125,8 @@ define void @main() {
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});
+ EXPECT_TRUE(DRBI.hasImplicitBinding());
+ EXPECT_FALSE(DRBI.hasOverlappingBinding());
}
} // namespace
More information about the llvm-commits
mailing list