[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:
parent
4a13f0912a
commit
8a2d3f5653
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -14,7 +14,6 @@ set(LLVM_LINK_COMPONENTS
|
||||
add_llvm_unittest(LLVMFrontendTests
|
||||
HLSLBindingTest.cpp
|
||||
HLSLRootSignatureDumpTest.cpp
|
||||
HLSLRootSignatureRangesTest.cpp
|
||||
OpenACCTest.cpp
|
||||
OpenMPContextTest.cpp
|
||||
OpenMPIRBuilderTest.cpp
|
||||
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user