Skip to content

[NFC][HLSL] Move resource range logic from SemaHLSL to RootSignatureValidations #147117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 10 additions & 91 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,29 +1079,8 @@ void SemaHLSL::ActOnFinishRootSignatureDecl(

bool SemaHLSL::handleRootSignatureDecl(HLSLRootSignatureDecl *D,
SourceLocation Loc) {
// 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 following algorithm is implemented in the following steps:
//
// 1. Collect RangeInfo from relevant RootElements:
// - RangeInfo will retain the interval, ResourceClass, Space and Visibility
// 2. Sort the RangeInfo's such that they are grouped together by
// ResourceClass and Space (GroupT defined below)
// 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
using RangeInfo = llvm::hlsl::rootsig::RangeInfo;
using ResourceRange = llvm::hlsl::rootsig::ResourceRange;
using GroupT = std::pair<ResourceClass, /*Space*/ uint32_t>;
using OverlappingRanges = llvm::hlsl::rootsig::OverlappingRanges;

// 1. Collect RangeInfos
llvm::SmallVector<RangeInfo> Infos;
Expand Down Expand Up @@ -1166,40 +1145,10 @@ bool SemaHLSL::handleRootSignatureDecl(HLSLRootSignatureDecl *D,
}
}

// 2. Sort the RangeInfo's by their GroupT to form groupings
std::sort(Infos.begin(), Infos.end(), [](RangeInfo A, RangeInfo B) {
return std::tie(A.Class, A.Space) < std::tie(B.Class, B.Space);
});

// 3. First we will init our state to track:
if (Infos.size() == 0)
return false; // No ranges to overlap
GroupT CurGroup = {Infos[0].Class, Infos[0].Space};
bool HadOverlap = false;

// 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();
};

// Helper to report diagnostics
auto ReportOverlap = [this, Loc, &HadOverlap](const RangeInfo *Info,
const RangeInfo *OInfo) {
HadOverlap = true;
auto ReportOverlap = [this, Loc](OverlappingRanges Overlap) {
const RangeInfo *Info = Overlap.A;
const RangeInfo *OInfo = Overlap.B;
auto CommonVis = Info->Visibility == llvm::dxbc::ShaderVisibility::All
? OInfo->Visibility
: Info->Visibility;
Expand All @@ -1212,42 +1161,12 @@ bool SemaHLSL::handleRootSignatureDecl(HLSLRootSignatureDecl *D,
<< OInfo->UpperBound << Info->Space << CommonVis;
};

// 3: 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;
}

// 3A: Insert range info into corresponding Visibility ResourceRange
ResourceRange &VisRange = Ranges[llvm::to_underlying(Info.Visibility)];
if (std::optional<const RangeInfo *> Overlapping = VisRange.insert(Info))
ReportOverlap(&Info, Overlapping.value());

// 3B: 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))
ReportOverlap(&Info, Overlapping.value());
}

return HadOverlap;
llvm::SmallVector<OverlappingRanges> Overlaps =
llvm::hlsl::rootsig::findOverlappingRanges(Infos);
for (OverlappingRanges Overlap : Overlaps)
ReportOverlap(Overlap);

return Overlaps.size() != 0;
}

void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
Expand Down
34 changes: 33 additions & 1 deletion llvm/include/llvm/Frontend/HLSL/RootSignatureValidations.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct RangeInfo {
uint32_t LowerBound;
uint32_t UpperBound;

// Information retained for diagnostics
// Information retained for determining overlap
llvm::dxil::ResourceClass Class;
uint32_t Space;
llvm::dxbc::ShaderVisibility Visibility;
Expand Down Expand Up @@ -103,6 +103,38 @@ class ResourceRange {
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. 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::SmallVector<OverlappingRanges>
findOverlappingRanges(llvm::SmallVector<RangeInfo> &Infos);

} // namespace rootsig
} // namespace hlsl
} // namespace llvm
Expand Down
73 changes: 73 additions & 0 deletions llvm/lib/Frontend/HLSL/RootSignatureValidations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,79 @@ std::optional<const RangeInfo *> ResourceRange::insert(const RangeInfo &Info) {
return Res;
}

llvm::SmallVector<OverlappingRanges>
findOverlappingRanges(llvm::SmallVector<RangeInfo> &Infos) {
// 1. The user has provided the corresponding range information
llvm::SmallVector<OverlappingRanges> Overlaps;
using GroupT = std::pair<dxil::ResourceClass, /*Space*/ uint32_t>;

// 2. Sort the RangeInfo's by their GroupT to form groupings
std::sort(Infos.begin(), Infos.end(), [](RangeInfo A, RangeInfo B) {
return std::tie(A.Class, A.Space) < std::tie(B.Class, B.Space);
});

// 3. 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
};
Comment on lines +242 to +252
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not new, so mostly a thought for later, but it'd be nice if we could static_assert or otherwise ensure that we got the full set of dxbc::ShaderVisibility here.


// Reset the ResourceRanges for when we iterate through a new group
auto ClearRanges = [&Ranges]() {
for (ResourceRange &Range : Ranges)
Range.clear();
};

// 3: 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;
}

// 3A: 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()));

// 3B: 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
Loading