Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -13054,6 +13054,11 @@ def err_invalid_hlsl_resource_type: Error<
def err_hlsl_spirv_only: Error<"%0 is only available for the SPIR-V target">;
def err_hlsl_vk_literal_must_contain_constant: Error<"the argument to vk::Literal must be a vk::integral_constant">;

def err_hlsl_resource_range_overlap: Error<
"resource ranges %select{t|u|b|s}0[%1;%2] and %select{t|u|b|s}3[%4;%5] "
"overlap within space = %6 and visibility = "
"%select{All|Vertex|Hull|Domain|Geometry|Pixel|Amplification|Mesh}7">;

// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ class SemaHLSL : public SemaBase {
SourceLocation Loc, IdentifierInfo *DeclIdent,
SmallVector<llvm::hlsl::rootsig::RootElement> &Elements);

// Returns true when D is invalid and a diagnostic was produced
bool handleRootSignatureDecl(HLSLRootSignatureDecl *D, SourceLocation Loc);
void handleRootSignatureAttr(Decl *D, const ParsedAttr &AL);
void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL);
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
Expand Down
131 changes: 130 additions & 1 deletion clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Frontend/HLSL/HLSLRootSignatureUtils.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -1068,10 +1069,139 @@ void SemaHLSL::ActOnFinishRootSignatureDecl(
SemaRef.getASTContext(), /*DeclContext=*/SemaRef.CurContext, Loc,
DeclIdent, Elements);

// Perform validation of constructs here
if (handleRootSignatureDecl(SignatureDecl, Loc))
return;

SignatureDecl->setImplicit();
SemaRef.PushOnScopeChains(SignatureDecl, SemaRef.getCurScope());
}

bool SemaHLSL::handleRootSignatureDecl(HLSLRootSignatureDecl *D,
SourceLocation Loc) {
auto Elements = D->getRootElements();

// 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>;

// 1. Collect RangeInfos
llvm::SmallVector<RangeInfo> Infos;
for (const auto &Elem : Elements) {
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 []

Info.Class =
llvm::dxil::ResourceClass(llvm::to_underlying(Descriptor->Type));
Info.Space = Descriptor->Space;
Info.Vis = Descriptor->Visibility;
Infos.push_back(Info);
}
}

// 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;
SmallVector<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 CommonVis = Info->Vis == llvm::hlsl::rootsig::ShaderVisibility::All
? OInfo->Vis
: Info->Vis;
this->Diag(Loc, diag::err_hlsl_resource_range_overlap)
<< llvm::to_underlying(Info->Class) << Info->LowerBound
<< Info->UpperBound << llvm::to_underlying(OInfo->Class)
<< OInfo->LowerBound << OInfo->UpperBound << Info->Space << CommonVis;
};

// 3: Iterate throught 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.Vis)];
if (auto 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.
//
// Maps will be an ArrayRef to all non-all visibility RangeMaps in the
// former case and it will be an ArrayRef to just the all visiblity
// RangeMap in the latter case.
MutableArrayRef<ResourceRange> OverlapRanges =
Info.Vis == llvm::hlsl::rootsig::ShaderVisibility::All
? MutableArrayRef<ResourceRange>{Ranges}.drop_front()
: MutableArrayRef<ResourceRange>{Ranges}.take_front();

for (ResourceRange &Range : OverlapRanges)
if (auto Overlapping = Range.getOverlapping(Info))
ReportOverlap(&Info, Overlapping.value());
}

return HadOverlap;
}

void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
if (AL.getNumArgs() != 1) {
Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
Expand All @@ -1093,7 +1223,6 @@ void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) {
if (SemaRef.LookupQualifiedName(R, D->getDeclContext()))
if (auto *SignatureDecl =
dyn_cast<HLSLRootSignatureDecl>(R.getFoundDecl())) {
// Perform validation of constructs here
D->addAttr(::new (getASTContext()) RootSignatureAttr(
getASTContext(), AL, Ident, SignatureDecl));
}
Expand Down
26 changes: 26 additions & 0 deletions clang/test/SemaHLSL/RootSignature-resource-ranges-err.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - %s -verify

#define Overlap0 "CBV(b42), CBV(b42)"

[RootSignature(Overlap0)] // expected-error {{resource ranges b[42;42] and b[42;42] overlap within space = 0 and visibility = All}}
void bad_root_signature_0() {}

#define Overlap1 "SRV(t0, space = 3), SRV(t0, space = 3)"

[RootSignature(Overlap1)] // expected-error {{resource ranges t[0;0] and t[0;0] overlap within space = 3 and visibility = All}}
void bad_root_signature_1() {}

#define Overlap2 "UAV(u0, visibility = SHADER_VISIBILITY_PIXEL), UAV(u0, visibility = SHADER_VISIBILITY_PIXEL)"

[RootSignature(Overlap2)] // expected-error {{resource ranges u[0;0] and u[0;0] overlap within space = 0 and visibility = Pixel}}
void bad_root_signature_2() {}

#define Overlap3 "UAV(u0, visibility = SHADER_VISIBILITY_ALL), UAV(u0, visibility = SHADER_VISIBILITY_PIXEL)"

[RootSignature(Overlap3)] // expected-error {{resource ranges u[0;0] and u[0;0] overlap within space = 0 and visibility = Pixel}}
void bad_root_signature_3() {}

#define Overlap4 "UAV(u0, visibility = SHADER_VISIBILITY_PIXEL), UAV(u0, visibility = SHADER_VISIBILITY_ALL)"

[RootSignature(Overlap4)] // expected-error {{resource ranges u[0;0] and u[0;0] overlap within space = 0 and visibility = Pixel}}
void bad_root_signature_4() {}
22 changes: 22 additions & 0 deletions clang/test/SemaHLSL/RootSignature-resource-ranges.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - %s -verify
// expected-no-diagnostics

#define NoOverlap0 "CBV(b0), CBV(b1)"

[RootSignature(NoOverlap0)]
void valid_root_signature_0() {}

#define NoOverlap1 "CBV(b0, visibility = SHADER_VISIBILITY_DOMAIN), CBV(b0, visibility = SHADER_VISIBILITY_PIXEL)"

[RootSignature(NoOverlap1)]
void valid_root_signature_1() {}

#define NoOverlap2 "CBV(b0, space = 1), CBV(b0, space = 2)"

[RootSignature(NoOverlap2)]
void valid_root_signature_2() {}

#define NoOverlap3 "CBV(b0), SRV(t0)"

[RootSignature(NoOverlap3)]
void valid_root_signature_3() {}
13 changes: 10 additions & 3 deletions llvm/include/llvm/Frontend/HLSL/HLSLRootSignatureUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,17 @@ class MetadataBuilder {
SmallVector<Metadata *> GeneratedMetadata;
};

// RangeInfo holds the information to correctly construct a ResourceRange
// and retains this information to be used for displaying a better diagnostic
struct RangeInfo {
const static uint32_t Unbounded = ~0u;
const static uint32_t Unbounded = static_cast<uint32_t>(-1);

// Interval information
uint32_t LowerBound;
uint32_t UpperBound;

// Information retained for diagnostics
llvm::dxil::ResourceClass Class;
uint32_t Space;
ShaderVisibility Vis;
};

class ResourceRange {
Expand All @@ -98,6 +102,9 @@ class ResourceRange {
// Return the mapped RangeInfo at X or nullptr if no mapping exists
const RangeInfo *lookup(uint32_t X) const;

// Removes all entries of the ResourceRange
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 &.
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Frontend/HLSL/HLSLRootSignatureUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ 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;
Expand Down
Loading