[HLSL][Sema] Use hlsl::BindingInfoBuilder instead of RangeInfo. NFC (#150634)

Clean up some duplicated logic. We had two ways to do the same thing
here, and BindingInfoBuilder is more flexible.
This commit is contained in:
Justin Bogner 2025-08-05 10:47:06 -07:00 committed by GitHub
parent 4a13f0912a
commit 8a2d3f5653
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 138 additions and 541 deletions

View File

@ -39,6 +39,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Frontend/HLSL/HLSLBinding.h"
#include "llvm/Frontend/HLSL/RootSignatureValidations.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DXILABI.h"
@ -1083,6 +1084,102 @@ void SemaHLSL::ActOnFinishRootSignatureDecl(
SemaRef.PushOnScopeChains(SignatureDecl, SemaRef.getCurScope());
}
namespace {
struct PerVisibilityBindingChecker {
SemaHLSL *S;
// We need one builder per `llvm::dxbc::ShaderVisibility` value.
std::array<llvm::hlsl::BindingInfoBuilder, 8> Builders;
struct ElemInfo {
const hlsl::RootSignatureElement *Elem;
llvm::dxbc::ShaderVisibility Vis;
bool Diagnosed;
};
llvm::SmallVector<ElemInfo> ElemInfoMap;
PerVisibilityBindingChecker(SemaHLSL *S) : S(S) {}
void trackBinding(llvm::dxbc::ShaderVisibility Visibility,
llvm::dxil::ResourceClass RC, uint32_t Space,
uint32_t LowerBound, uint32_t UpperBound,
const hlsl::RootSignatureElement *Elem) {
uint32_t BuilderIndex = llvm::to_underlying(Visibility);
assert(BuilderIndex < Builders.size() &&
"Not enough builders for visibility type");
Builders[BuilderIndex].trackBinding(RC, Space, LowerBound, UpperBound,
static_cast<const void *>(Elem));
static_assert(llvm::to_underlying(llvm::dxbc::ShaderVisibility::All) == 0,
"'All' visibility must come first");
if (Visibility == llvm::dxbc::ShaderVisibility::All)
for (size_t I = 1, E = Builders.size(); I < E; ++I)
Builders[I].trackBinding(RC, Space, LowerBound, UpperBound,
static_cast<const void *>(Elem));
ElemInfoMap.push_back({Elem, Visibility, false});
}
ElemInfo &getInfo(const hlsl::RootSignatureElement *Elem) {
auto It = llvm::lower_bound(
ElemInfoMap, Elem,
[](const auto &LHS, const auto &RHS) { return LHS.Elem < RHS; });
assert(It->Elem == Elem && "Element not in map");
return *It;
}
bool checkOverlap() {
llvm::sort(ElemInfoMap, [](const auto &LHS, const auto &RHS) {
return LHS.Elem < RHS.Elem;
});
bool HadOverlap = false;
using llvm::hlsl::BindingInfoBuilder;
auto ReportOverlap = [this, &HadOverlap](
const BindingInfoBuilder &Builder,
const BindingInfoBuilder::Binding &Reported) {
HadOverlap = true;
const auto *Elem =
static_cast<const hlsl::RootSignatureElement *>(Reported.Cookie);
const BindingInfoBuilder::Binding &Previous =
Builder.findOverlapping(Reported);
const auto *PrevElem =
static_cast<const hlsl::RootSignatureElement *>(Previous.Cookie);
ElemInfo &Info = getInfo(Elem);
// We will have already diagnosed this binding if there's overlap in the
// "All" visibility as well as any particular visibility.
if (Info.Diagnosed)
return;
Info.Diagnosed = true;
ElemInfo &PrevInfo = getInfo(PrevElem);
llvm::dxbc::ShaderVisibility CommonVis =
Info.Vis == llvm::dxbc::ShaderVisibility::All ? PrevInfo.Vis
: Info.Vis;
this->S->Diag(Elem->getLocation(), diag::err_hlsl_resource_range_overlap)
<< llvm::to_underlying(Reported.RC) << Reported.LowerBound
<< Reported.isUnbounded() << Reported.UpperBound
<< llvm::to_underlying(Previous.RC) << Previous.LowerBound
<< Previous.isUnbounded() << Previous.UpperBound << Reported.Space
<< CommonVis;
this->S->Diag(PrevElem->getLocation(),
diag::note_hlsl_resource_range_here);
};
for (BindingInfoBuilder &Builder : Builders)
Builder.calculateBindingInfo(ReportOverlap);
return HadOverlap;
}
};
} // end anonymous namespace
bool SemaHLSL::handleRootSignatureElements(
ArrayRef<hlsl::RootSignatureElement> Elements) {
// Define some common error handling functions
@ -1171,147 +1268,67 @@ bool SemaHLSL::handleRootSignatureElements(
}
}
using RangeInfo = llvm::hlsl::rootsig::RangeInfo;
using OverlappingRanges = llvm::hlsl::rootsig::OverlappingRanges;
using InfoPairT = std::pair<RangeInfo, const hlsl::RootSignatureElement *>;
PerVisibilityBindingChecker BindingChecker(this);
SmallVector<std::pair<const llvm::hlsl::rootsig::DescriptorTableClause *,
const hlsl::RootSignatureElement *>>
UnboundClauses;
// 1. Collect RangeInfos
llvm::SmallVector<InfoPairT> InfoPairs;
for (const hlsl::RootSignatureElement &RootSigElem : Elements) {
const llvm::hlsl::rootsig::RootElement &Elem = RootSigElem.getElement();
if (const auto *Descriptor =
std::get_if<llvm::hlsl::rootsig::RootDescriptor>(&Elem)) {
RangeInfo Info;
Info.LowerBound = Descriptor->Reg.Number;
Info.UpperBound = Info.LowerBound; // use inclusive ranges []
uint32_t LowerBound(Descriptor->Reg.Number);
uint32_t UpperBound(LowerBound); // inclusive range
Info.Class =
llvm::dxil::ResourceClass(llvm::to_underlying(Descriptor->Type));
Info.Space = Descriptor->Space;
Info.Visibility = Descriptor->Visibility;
InfoPairs.push_back({Info, &RootSigElem});
BindingChecker.trackBinding(
Descriptor->Visibility,
static_cast<llvm::dxil::ResourceClass>(Descriptor->Type),
Descriptor->Space, LowerBound, UpperBound, &RootSigElem);
} else if (const auto *Constants =
std::get_if<llvm::hlsl::rootsig::RootConstants>(&Elem)) {
RangeInfo Info;
Info.LowerBound = Constants->Reg.Number;
Info.UpperBound = Info.LowerBound; // use inclusive ranges []
uint32_t LowerBound(Constants->Reg.Number);
uint32_t UpperBound(LowerBound); // inclusive range
Info.Class = llvm::dxil::ResourceClass::CBuffer;
Info.Space = Constants->Space;
Info.Visibility = Constants->Visibility;
InfoPairs.push_back({Info, &RootSigElem});
BindingChecker.trackBinding(
Constants->Visibility, llvm::dxil::ResourceClass::CBuffer,
Constants->Space, LowerBound, UpperBound, &RootSigElem);
} else if (const auto *Sampler =
std::get_if<llvm::hlsl::rootsig::StaticSampler>(&Elem)) {
RangeInfo Info;
Info.LowerBound = Sampler->Reg.Number;
Info.UpperBound = Info.LowerBound; // use inclusive ranges []
uint32_t LowerBound(Sampler->Reg.Number);
uint32_t UpperBound(LowerBound); // inclusive range
Info.Class = llvm::dxil::ResourceClass::Sampler;
Info.Space = Sampler->Space;
Info.Visibility = Sampler->Visibility;
InfoPairs.push_back({Info, &RootSigElem});
BindingChecker.trackBinding(
Sampler->Visibility, llvm::dxil::ResourceClass::Sampler,
Sampler->Space, LowerBound, UpperBound, &RootSigElem);
} else if (const auto *Clause =
std::get_if<llvm::hlsl::rootsig::DescriptorTableClause>(
&Elem)) {
RangeInfo Info;
Info.LowerBound = Clause->Reg.Number;
// Relevant error will have already been reported above and needs to be
// fixed before we can conduct range analysis, so shortcut error return
if (Clause->NumDescriptors == 0)
return true;
Info.UpperBound = Clause->NumDescriptors == RangeInfo::Unbounded
? RangeInfo::Unbounded
: Info.LowerBound + Clause->NumDescriptors -
1; // use inclusive ranges []
Info.Class = Clause->Type;
Info.Space = Clause->Space;
// Note: Clause does not hold the visibility this will need to
InfoPairs.push_back({Info, &RootSigElem});
// We'll process these once we see the table element.
UnboundClauses.emplace_back(Clause, &RootSigElem);
} else if (const auto *Table =
std::get_if<llvm::hlsl::rootsig::DescriptorTable>(&Elem)) {
// Table holds the Visibility of all owned Clauses in Table, so iterate
// owned Clauses and update their corresponding RangeInfo
assert(Table->NumClauses <= InfoPairs.size() && "RootElement");
// The last Table->NumClauses elements of Infos are the owned Clauses
// generated RangeInfo
auto TableInfos =
MutableArrayRef<InfoPairT>(InfoPairs).take_back(Table->NumClauses);
for (InfoPairT &Pair : TableInfos)
Pair.first.Visibility = Table->Visibility;
assert(UnboundClauses.size() == Table->NumClauses &&
"Number of unbound elements must match the number of clauses");
for (const auto &[Clause, ClauseElem] : UnboundClauses) {
uint32_t LowerBound(Clause->Reg.Number);
// Relevant error will have already been reported above and needs to be
// fixed before we can conduct range analysis, so shortcut error return
if (Clause->NumDescriptors == 0)
return true;
uint32_t UpperBound = Clause->NumDescriptors == ~0u
? ~0u
: LowerBound + Clause->NumDescriptors - 1;
BindingChecker.trackBinding(
Table->Visibility,
static_cast<llvm::dxil::ResourceClass>(Clause->Type), Clause->Space,
LowerBound, UpperBound, ClauseElem);
}
UnboundClauses.clear();
}
}
// 2. Sort with the RangeInfo <operator to prepare it for findOverlapping
llvm::sort(InfoPairs,
[](InfoPairT A, InfoPairT B) { return A.first < B.first; });
llvm::SmallVector<RangeInfo> Infos;
for (const InfoPairT &Pair : InfoPairs)
Infos.push_back(Pair.first);
// Helpers to report diagnostics
uint32_t DuplicateCounter = 0;
using ElemPair = std::pair<const hlsl::RootSignatureElement *,
const hlsl::RootSignatureElement *>;
auto GetElemPair = [&Infos, &InfoPairs, &DuplicateCounter](
OverlappingRanges Overlap) -> ElemPair {
// Given we sorted the InfoPairs (and by implication) Infos, and,
// that Overlap.B is the item retrieved from the ResourceRange. Then it is
// guarenteed that Overlap.B <= Overlap.A.
//
// So we will find Overlap.B first and then continue to find Overlap.A
// after
auto InfoB = std::lower_bound(Infos.begin(), Infos.end(), *Overlap.B);
auto DistB = std::distance(Infos.begin(), InfoB);
auto PairB = InfoPairs.begin();
std::advance(PairB, DistB);
auto InfoA = std::lower_bound(InfoB, Infos.end(), *Overlap.A);
// Similarily, from the property that we have sorted the RangeInfos,
// all duplicates will be processed one after the other. So
// DuplicateCounter can be re-used for each set of duplicates we
// encounter as we handle incoming errors
DuplicateCounter = InfoA == InfoB ? DuplicateCounter + 1 : 0;
auto DistA = std::distance(InfoB, InfoA) + DuplicateCounter;
auto PairA = PairB;
std::advance(PairA, DistA);
return {PairA->second, PairB->second};
};
auto ReportOverlap = [this, &GetElemPair](OverlappingRanges Overlap) {
auto Pair = GetElemPair(Overlap);
const RangeInfo *Info = Overlap.A;
const hlsl::RootSignatureElement *Elem = Pair.first;
const RangeInfo *OInfo = Overlap.B;
auto CommonVis = Info->Visibility == llvm::dxbc::ShaderVisibility::All
? OInfo->Visibility
: Info->Visibility;
this->Diag(Elem->getLocation(), diag::err_hlsl_resource_range_overlap)
<< llvm::to_underlying(Info->Class) << Info->LowerBound
<< /*unbounded=*/(Info->UpperBound == RangeInfo::Unbounded)
<< Info->UpperBound << llvm::to_underlying(OInfo->Class)
<< OInfo->LowerBound
<< /*unbounded=*/(OInfo->UpperBound == RangeInfo::Unbounded)
<< OInfo->UpperBound << Info->Space << CommonVis;
const hlsl::RootSignatureElement *OElem = Pair.second;
this->Diag(OElem->getLocation(), diag::note_hlsl_resource_range_here);
};
// 3. Invoke find overlapping ranges
llvm::SmallVector<OverlappingRanges> Overlaps =
llvm::hlsl::rootsig::findOverlappingRanges(Infos);
for (OverlappingRanges Overlap : Overlaps)
ReportOverlap(Overlap);
return Overlaps.size() != 0;
return BindingChecker.checkOverlap();
}
void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {

View File

@ -41,114 +41,6 @@ LLVM_ABI bool verifyComparisonFunc(uint32_t ComparisonFunc);
LLVM_ABI bool verifyBorderColor(uint32_t BorderColor);
LLVM_ABI bool verifyLOD(float LOD);
struct RangeInfo {
const static uint32_t Unbounded = ~0u;
// Interval information
uint32_t LowerBound;
uint32_t UpperBound;
// Information retained for determining overlap
llvm::dxil::ResourceClass Class;
uint32_t Space;
llvm::dxbc::ShaderVisibility Visibility;
bool operator==(const RangeInfo &RHS) const {
return std::tie(LowerBound, UpperBound, Class, Space, Visibility) ==
std::tie(RHS.LowerBound, RHS.UpperBound, RHS.Class, RHS.Space,
RHS.Visibility);
}
bool operator<(const RangeInfo &RHS) const {
return std::tie(Class, Space, LowerBound, UpperBound, Visibility) <
std::tie(RHS.Class, RHS.Space, RHS.LowerBound, RHS.UpperBound,
RHS.Visibility);
}
};
class ResourceRange {
public:
using MapT = llvm::IntervalMap<uint32_t, const RangeInfo *, 16,
llvm::IntervalMapInfo<uint32_t>>;
private:
MapT Intervals;
public:
ResourceRange(MapT::Allocator &Allocator) : Intervals(MapT(Allocator)) {}
// Returns a reference to the first RangeInfo that overlaps with
// [Info.LowerBound;Info.UpperBound], or, std::nullopt if there is no overlap
LLVM_ABI std::optional<const RangeInfo *>
getOverlapping(const RangeInfo &Info) const;
// Return the mapped RangeInfo at X or nullptr if no mapping exists
LLVM_ABI const RangeInfo *lookup(uint32_t X) const;
// Removes all entries of the ResourceRange
LLVM_ABI void clear();
// Insert the required (sub-)intervals such that the interval of [a;b] =
// [Info.LowerBound, Info.UpperBound] is covered and points to a valid
// RangeInfo &.
//
// For instance consider the following chain of inserting RangeInfos with the
// intervals denoting the Lower/Upper-bounds:
//
// A = [0;2]
// insert(A) -> false
// intervals: [0;2] -> &A
// B = [5;7]
// insert(B) -> false
// intervals: [0;2] -> &A, [5;7] -> &B
// C = [4;7]
// insert(C) -> true
// intervals: [0;2] -> &A, [4;7] -> &C
// D = [1;5]
// insert(D) -> true
// intervals: [0;2] -> &A, [3;3] -> &D, [4;7] -> &C
// E = [0;unbounded]
// insert(E) -> true
// intervals: [0;unbounded] -> E
//
// Returns a reference to the first RangeInfo that overlaps with
// [Info.LowerBound;Info.UpperBound], or, std::nullopt if there is no overlap
// (equivalent to getOverlapping)
LLVM_ABI std::optional<const RangeInfo *> insert(const RangeInfo &Info);
};
struct OverlappingRanges {
const RangeInfo *A;
const RangeInfo *B;
OverlappingRanges(const RangeInfo *A, const RangeInfo *B) : A(A), B(B) {}
};
/// The following conducts analysis on resource ranges to detect and report
/// any overlaps in resource ranges.
///
/// A resource range overlaps with another resource range if they have:
/// - equivalent ResourceClass (SRV, UAV, CBuffer, Sampler)
/// - equivalent resource space
/// - overlapping visbility
///
/// The algorithm is implemented in the following steps:
///
/// 1. The user will collect RangeInfo from relevant RootElements:
/// - RangeInfo will retain the interval, ResourceClass, Space and Visibility
/// - It will also contain an index so that it can be associated to
/// additional diagnostic information
/// 2. The user is required to sort the RangeInfo's such that they are grouped
/// together by ResourceClass and Space
/// 3. Iterate through the collected RangeInfos by their groups
/// - For each group we will have a ResourceRange for each visibility
/// - As we iterate through we will:
/// A: Insert the current RangeInfo into the corresponding Visibility
/// ResourceRange
/// B: Check for overlap with any overlapping Visibility ResourceRange
LLVM_ABI llvm::SmallVector<OverlappingRanges>
findOverlappingRanges(ArrayRef<RangeInfo> Infos);
} // namespace rootsig
} // namespace hlsl
} // namespace llvm

View File

@ -180,140 +180,6 @@ bool verifyBorderColor(uint32_t BorderColor) {
bool verifyLOD(float LOD) { return !std::isnan(LOD); }
std::optional<const RangeInfo *>
ResourceRange::getOverlapping(const RangeInfo &Info) const {
MapT::const_iterator Interval = Intervals.find(Info.LowerBound);
if (!Interval.valid() || Info.UpperBound < Interval.start())
return std::nullopt;
return Interval.value();
}
const RangeInfo *ResourceRange::lookup(uint32_t X) const {
return Intervals.lookup(X, nullptr);
}
void ResourceRange::clear() { return Intervals.clear(); }
std::optional<const RangeInfo *> ResourceRange::insert(const RangeInfo &Info) {
uint32_t LowerBound = Info.LowerBound;
uint32_t UpperBound = Info.UpperBound;
std::optional<const RangeInfo *> Res = std::nullopt;
MapT::iterator Interval = Intervals.begin();
while (true) {
if (UpperBound < LowerBound)
break;
Interval.advanceTo(LowerBound);
if (!Interval.valid()) // No interval found
break;
// Let Interval = [x;y] and [LowerBound;UpperBound] = [a;b] and note that
// a <= y implicitly from Intervals.find(LowerBound)
if (UpperBound < Interval.start())
break; // found interval does not overlap with inserted one
if (!Res.has_value()) // Update to be the first found intersection
Res = Interval.value();
if (Interval.start() <= LowerBound && UpperBound <= Interval.stop()) {
// x <= a <= b <= y implies that [a;b] is covered by [x;y]
// -> so we don't need to insert this, report an overlap
return Res;
} else if (LowerBound <= Interval.start() &&
Interval.stop() <= UpperBound) {
// a <= x <= y <= b implies that [x;y] is covered by [a;b]
// -> so remove the existing interval that we will cover with the
// overwrite
Interval.erase();
} else if (LowerBound < Interval.start() && UpperBound <= Interval.stop()) {
// a < x <= b <= y implies that [a; x] is not covered but [x;b] is
// -> so set b = x - 1 such that [a;x-1] is now the interval to insert
UpperBound = Interval.start() - 1;
} else if (Interval.start() <= LowerBound && Interval.stop() < UpperBound) {
// a < x <= b <= y implies that [y; b] is not covered but [a;y] is
// -> so set a = y + 1 such that [y+1;b] is now the interval to insert
LowerBound = Interval.stop() + 1;
}
}
assert(LowerBound <= UpperBound && "Attempting to insert an empty interval");
Intervals.insert(LowerBound, UpperBound, &Info);
return Res;
}
llvm::SmallVector<OverlappingRanges>
findOverlappingRanges(ArrayRef<RangeInfo> Infos) {
// It is expected that Infos is filled with valid RangeInfos and that
// they are sorted with respect to the RangeInfo <operator
assert(llvm::is_sorted(Infos) && "Ranges must be sorted");
llvm::SmallVector<OverlappingRanges> Overlaps;
using GroupT = std::pair<dxil::ResourceClass, /*Space*/ uint32_t>;
// First we will init our state to track:
if (Infos.size() == 0)
return Overlaps; // No ranges to overlap
GroupT CurGroup = {Infos[0].Class, Infos[0].Space};
// Create a ResourceRange for each Visibility
ResourceRange::MapT::Allocator Allocator;
std::array<ResourceRange, 8> Ranges = {
ResourceRange(Allocator), // All
ResourceRange(Allocator), // Vertex
ResourceRange(Allocator), // Hull
ResourceRange(Allocator), // Domain
ResourceRange(Allocator), // Geometry
ResourceRange(Allocator), // Pixel
ResourceRange(Allocator), // Amplification
ResourceRange(Allocator), // Mesh
};
// Reset the ResourceRanges for when we iterate through a new group
auto ClearRanges = [&Ranges]() {
for (ResourceRange &Range : Ranges)
Range.clear();
};
// Iterate through collected RangeInfos
for (const RangeInfo &Info : Infos) {
GroupT InfoGroup = {Info.Class, Info.Space};
// Reset our ResourceRanges when we enter a new group
if (CurGroup != InfoGroup) {
ClearRanges();
CurGroup = InfoGroup;
}
// Insert range info into corresponding Visibility ResourceRange
ResourceRange &VisRange = Ranges[llvm::to_underlying(Info.Visibility)];
if (std::optional<const RangeInfo *> Overlapping = VisRange.insert(Info))
Overlaps.push_back(OverlappingRanges(&Info, Overlapping.value()));
// Check for overlap in all overlapping Visibility ResourceRanges
//
// If the range that we are inserting has ShaderVisiblity::All it needs to
// check for an overlap in all other visibility types as well.
// Otherwise, the range that is inserted needs to check that it does not
// overlap with ShaderVisibility::All.
//
// OverlapRanges will be an ArrayRef to all non-all visibility
// ResourceRanges in the former case and it will be an ArrayRef to just the
// all visiblity ResourceRange in the latter case.
ArrayRef<ResourceRange> OverlapRanges =
Info.Visibility == llvm::dxbc::ShaderVisibility::All
? ArrayRef<ResourceRange>{Ranges}.drop_front()
: ArrayRef<ResourceRange>{Ranges}.take_front();
for (const ResourceRange &Range : OverlapRanges)
if (std::optional<const RangeInfo *> Overlapping =
Range.getOverlapping(Info))
Overlaps.push_back(OverlappingRanges(&Info, Overlapping.value()));
}
return Overlaps;
}
} // namespace rootsig
} // namespace hlsl
} // namespace llvm

View File

@ -14,7 +14,6 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(LLVMFrontendTests
HLSLBindingTest.cpp
HLSLRootSignatureDumpTest.cpp
HLSLRootSignatureRangesTest.cpp
OpenACCTest.cpp
OpenMPContextTest.cpp
OpenMPIRBuilderTest.cpp

View File

@ -1,177 +0,0 @@
//===------ HLSLRootSignatureRangeTest.cpp - RootSignature Range 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/RootSignatureValidations.h"
#include "gtest/gtest.h"
using namespace llvm::hlsl::rootsig;
namespace {
TEST(HLSLRootSignatureTest, NoOverlappingInsertTests) {
// Ensures that there is never a reported overlap
ResourceRange::MapT::Allocator Allocator;
ResourceRange Range(Allocator);
RangeInfo A;
A.LowerBound = 0;
A.UpperBound = 3;
EXPECT_EQ(Range.insert(A), std::nullopt);
RangeInfo B;
B.LowerBound = 4;
B.UpperBound = 7;
EXPECT_EQ(Range.insert(B), std::nullopt);
RangeInfo C;
C.LowerBound = 10;
C.UpperBound = RangeInfo::Unbounded;
EXPECT_EQ(Range.insert(C), std::nullopt);
// A = [0;3]
EXPECT_EQ(Range.lookup(0), &A);
EXPECT_EQ(Range.lookup(2), &A);
EXPECT_EQ(Range.lookup(3), &A);
// B = [4;7]
EXPECT_EQ(Range.lookup(4), &B);
EXPECT_EQ(Range.lookup(5), &B);
EXPECT_EQ(Range.lookup(7), &B);
EXPECT_EQ(Range.lookup(8), nullptr);
EXPECT_EQ(Range.lookup(9), nullptr);
// C = [10;unbounded]
EXPECT_EQ(Range.lookup(10), &C);
EXPECT_EQ(Range.lookup(42), &C);
EXPECT_EQ(Range.lookup(98237423), &C);
EXPECT_EQ(Range.lookup(RangeInfo::Unbounded), &C);
}
TEST(HLSLRootSignatureTest, SingleOverlappingInsertTests) {
// Ensures that we correctly report an overlap when we insert a range that
// overlaps with one other range but does not cover (replace) it
ResourceRange::MapT::Allocator Allocator;
ResourceRange Range(Allocator);
RangeInfo A;
A.LowerBound = 1;
A.UpperBound = 5;
EXPECT_EQ(Range.insert(A), std::nullopt);
RangeInfo B;
B.LowerBound = 0;
B.UpperBound = 2;
EXPECT_EQ(Range.insert(B).value(), &A);
RangeInfo C;
C.LowerBound = 4;
C.UpperBound = RangeInfo::Unbounded;
EXPECT_EQ(Range.insert(C).value(), &A);
// A = [1;5]
EXPECT_EQ(Range.lookup(1), &A);
EXPECT_EQ(Range.lookup(2), &A);
EXPECT_EQ(Range.lookup(3), &A);
EXPECT_EQ(Range.lookup(4), &A);
EXPECT_EQ(Range.lookup(5), &A);
// B = [0;0]
EXPECT_EQ(Range.lookup(0), &B);
// C = [6; unbounded]
EXPECT_EQ(Range.lookup(6), &C);
EXPECT_EQ(Range.lookup(RangeInfo::Unbounded), &C);
}
TEST(HLSLRootSignatureTest, MultipleOverlappingInsertTests) {
// Ensures that we correctly report an overlap when inserted range
// overlaps more than one range and it does not cover (replace) either
// range. In this case it will just fill in the interval between the two
ResourceRange::MapT::Allocator Allocator;
ResourceRange Range(Allocator);
RangeInfo A;
A.LowerBound = 0;
A.UpperBound = 2;
EXPECT_EQ(Range.insert(A), std::nullopt);
RangeInfo B;
B.LowerBound = 4;
B.UpperBound = 6;
EXPECT_EQ(Range.insert(B), std::nullopt);
RangeInfo C;
C.LowerBound = 1;
C.UpperBound = 5;
EXPECT_EQ(Range.insert(C).value(), &A);
// A = [0;2]
EXPECT_EQ(Range.lookup(0), &A);
EXPECT_EQ(Range.lookup(1), &A);
EXPECT_EQ(Range.lookup(2), &A);
// B = [4;6]
EXPECT_EQ(Range.lookup(4), &B);
EXPECT_EQ(Range.lookup(5), &B);
EXPECT_EQ(Range.lookup(6), &B);
// C = [3;3]
EXPECT_EQ(Range.lookup(3), &C);
}
TEST(HLSLRootSignatureTest, CoverInsertTests) {
// Ensures that we correctly report an overlap when inserted range
// covers one or more ranges
ResourceRange::MapT::Allocator Allocator;
ResourceRange Range(Allocator);
RangeInfo A;
A.LowerBound = 0;
A.UpperBound = 2;
EXPECT_EQ(Range.insert(A), std::nullopt);
RangeInfo B;
B.LowerBound = 4;
B.UpperBound = 5;
EXPECT_EQ(Range.insert(B), std::nullopt);
// Covers B
RangeInfo C;
C.LowerBound = 4;
C.UpperBound = 6;
EXPECT_EQ(Range.insert(C).value(), &B);
// A = [0;2]
// C = [4;6] <- covers reference to B
EXPECT_EQ(Range.lookup(0), &A);
EXPECT_EQ(Range.lookup(1), &A);
EXPECT_EQ(Range.lookup(2), &A);
EXPECT_EQ(Range.lookup(3), nullptr);
EXPECT_EQ(Range.lookup(4), &C);
EXPECT_EQ(Range.lookup(5), &C);
EXPECT_EQ(Range.lookup(6), &C);
// Covers all other ranges
RangeInfo D;
D.LowerBound = 0;
D.UpperBound = 7;
EXPECT_EQ(Range.insert(D).value(), &A);
// D = [0;7] <- Covers reference to A and C
EXPECT_EQ(Range.lookup(0), &D);
EXPECT_EQ(Range.lookup(1), &D);
EXPECT_EQ(Range.lookup(2), &D);
EXPECT_EQ(Range.lookup(3), &D);
EXPECT_EQ(Range.lookup(4), &D);
EXPECT_EQ(Range.lookup(5), &D);
EXPECT_EQ(Range.lookup(6), &D);
EXPECT_EQ(Range.lookup(7), &D);
}
} // namespace