diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 9276554bebf9d..e59849add69f1 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -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 Builders; + + struct ElemInfo { + const hlsl::RootSignatureElement *Elem; + llvm::dxbc::ShaderVisibility Vis; + bool Diagnosed; + }; + llvm::SmallVector 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(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(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(Reported.Cookie); + const BindingInfoBuilder::Binding &Previous = + Builder.findOverlapping(Reported); + const auto *PrevElem = + static_cast(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 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; + PerVisibilityBindingChecker BindingChecker(this); + SmallVector> + UnboundClauses; - // 1. Collect RangeInfos - llvm::SmallVector InfoPairs; for (const hlsl::RootSignatureElement &RootSigElem : Elements) { const llvm::hlsl::rootsig::RootElement &Elem = RootSigElem.getElement(); if (const auto *Descriptor = std::get_if(&Elem)) { - RangeInfo Info; - Info.LowerBound = Descriptor->Reg.Number; - Info.UpperBound = Info.LowerBound; // use inclusive ranges [] - - Info.Class = - llvm::dxil::ResourceClass(llvm::to_underlying(Descriptor->Type)); - Info.Space = Descriptor->Space; - Info.Visibility = Descriptor->Visibility; + uint32_t LowerBound(Descriptor->Reg.Number); + uint32_t UpperBound(LowerBound); // inclusive range - InfoPairs.push_back({Info, &RootSigElem}); + BindingChecker.trackBinding( + Descriptor->Visibility, + static_cast(Descriptor->Type), + Descriptor->Space, LowerBound, UpperBound, &RootSigElem); } else if (const auto *Constants = std::get_if(&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(&Elem)) { - RangeInfo Info; - Info.LowerBound = Sampler->Reg.Number; - Info.UpperBound = Info.LowerBound; // use inclusive ranges [] - - Info.Class = llvm::dxil::ResourceClass::Sampler; - Info.Space = Sampler->Space; - Info.Visibility = Sampler->Visibility; + uint32_t LowerBound(Sampler->Reg.Number); + uint32_t UpperBound(LowerBound); // inclusive range - 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( &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(&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(InfoPairs).take_back(Table->NumClauses); - for (InfoPairT &Pair : TableInfos) - Pair.first.Visibility = Table->Visibility; - } - } - - // 2. Sort with the RangeInfo Infos; - for (const InfoPairT &Pair : InfoPairs) - Infos.push_back(Pair.first); - - // Helpers to report diagnostics - uint32_t DuplicateCounter = 0; - using ElemPair = std::pair; - 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 Overlaps = - llvm::hlsl::rootsig::findOverlappingRanges(Infos); - for (OverlappingRanges Overlap : Overlaps) - ReportOverlap(Overlap); + 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(Clause->Type), Clause->Space, + LowerBound, UpperBound, ClauseElem); + } + UnboundClauses.clear(); + } + } - return Overlaps.size() != 0; + return BindingChecker.checkOverlap(); } void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) { diff --git a/llvm/include/llvm/Frontend/HLSL/RootSignatureValidations.h b/llvm/include/llvm/Frontend/HLSL/RootSignatureValidations.h index f1e223da95241..fde32a1fff591 100644 --- a/llvm/include/llvm/Frontend/HLSL/RootSignatureValidations.h +++ b/llvm/include/llvm/Frontend/HLSL/RootSignatureValidations.h @@ -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>; - -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 - 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 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 -findOverlappingRanges(ArrayRef Infos); - } // namespace rootsig } // namespace hlsl } // namespace llvm diff --git a/llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp b/llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp index f11c7d2033bfb..9d84aa838f476 100644 --- a/llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp +++ b/llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp @@ -180,140 +180,6 @@ bool verifyBorderColor(uint32_t BorderColor) { bool verifyLOD(float LOD) { return !std::isnan(LOD); } -std::optional -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 ResourceRange::insert(const RangeInfo &Info) { - uint32_t LowerBound = Info.LowerBound; - uint32_t UpperBound = Info.UpperBound; - - std::optional 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 -findOverlappingRanges(ArrayRef Infos) { - // It is expected that Infos is filled with valid RangeInfos and that - // they are sorted with respect to the RangeInfo Overlaps; - using GroupT = std::pair; - - // 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 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 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 OverlapRanges = - Info.Visibility == llvm::dxbc::ShaderVisibility::All - ? ArrayRef{Ranges}.drop_front() - : ArrayRef{Ranges}.take_front(); - - for (const ResourceRange &Range : OverlapRanges) - if (std::optional Overlapping = - Range.getOverlapping(Info)) - Overlaps.push_back(OverlappingRanges(&Info, Overlapping.value())); - } - - return Overlaps; -} - } // namespace rootsig } // namespace hlsl } // namespace llvm diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt index 6e4ba5dc0c019..2cf3e69b27ffd 100644 --- a/llvm/unittests/Frontend/CMakeLists.txt +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -13,7 +13,6 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(LLVMFrontendTests HLSLBindingTest.cpp HLSLRootSignatureDumpTest.cpp - HLSLRootSignatureRangesTest.cpp OpenACCTest.cpp OpenMPContextTest.cpp OpenMPIRBuilderTest.cpp diff --git a/llvm/unittests/Frontend/HLSLRootSignatureRangesTest.cpp b/llvm/unittests/Frontend/HLSLRootSignatureRangesTest.cpp deleted file mode 100644 index be3f51e0e83d5..0000000000000 --- a/llvm/unittests/Frontend/HLSLRootSignatureRangesTest.cpp +++ /dev/null @@ -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