[clang] 7ccdc59 - [HLSL][RootSignature] Add basic parameter validations of Root Elements (#145795)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 13 10:23:42 PDT 2025
Author: Finn Plummer
Date: 2025-07-13T10:23:38-07:00
New Revision: 7ccdc595f8ecca0bc477c3e17683c52dca440845
URL: https://github.com/llvm/llvm-project/commit/7ccdc595f8ecca0bc477c3e17683c52dca440845
DIFF: https://github.com/llvm/llvm-project/commit/7ccdc595f8ecca0bc477c3e17683c52dca440845.diff
LOG: [HLSL][RootSignature] Add basic parameter validations of Root Elements (#145795)
In this pr we go through and enforce the various bounded parameter
values for non-flag values and the valid flag combinations for
`RootDescriptor` and `DescriptorRange` flags.
For reference of the required checks, please see here:
https://github.com/llvm/wg-hlsl/blob/main/proposals/0002-root-signature-in-clang.md#validations-in-sema.
- Updates `SemaHLSL` to iterate through `RootElement`s and verify that
all non-flag parameters are within their bounds
- Updates `SemaHLSL` to iterate through `RootElement`s and verify that
all flag parameters are a valid combination
- Extends `RootSignature-err.hlsl` with testcases for all invalid
specifications
- Adds `RootSignature-flags-err.hlsl` with testcase for invalid flag
specifications
Resolves: https://github.com/llvm/llvm-project/issues/129940
Added:
clang/test/SemaHLSL/RootSignature-flags-err.hlsl
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaHLSL.cpp
clang/test/AST/HLSL/RootSignatures-AST.hlsl
clang/test/CodeGenHLSL/RootSignature.hlsl
clang/test/SemaHLSL/RootSignature-err.hlsl
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b285309e0b3ca..577adc30ba2fa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13094,6 +13094,9 @@ 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_invalid_rootsig_value : Error<"value must be in the range [%0, %1]">;
+def err_hlsl_invalid_rootsig_flag : Error< "invalid flags for version 1.%0">;
+
def subst_hlsl_format_ranges: TextSubstitution<
"%select{t|u|b|s}0[%1;%select{%3]|unbounded)}2">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9af32138f9385..4875c25c76988 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -43,7 +43,9 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/TargetParser/Triple.h"
+#include <cmath>
#include <cstddef>
#include <iterator>
#include <utility>
@@ -1083,6 +1085,92 @@ void SemaHLSL::ActOnFinishRootSignatureDecl(
bool SemaHLSL::handleRootSignatureElements(
ArrayRef<hlsl::RootSignatureElement> Elements) {
+ // Define some common error handling functions
+ bool HadError = false;
+ auto ReportError = [this, &HadError](SourceLocation Loc, uint32_t LowerBound,
+ uint32_t UpperBound) {
+ HadError = true;
+ this->Diag(Loc, diag::err_hlsl_invalid_rootsig_value)
+ << LowerBound << UpperBound;
+ };
+
+ auto ReportFloatError = [this, &HadError](SourceLocation Loc,
+ float LowerBound,
+ float UpperBound) {
+ HadError = true;
+ this->Diag(Loc, diag::err_hlsl_invalid_rootsig_value)
+ << llvm::formatv("{0:f}", LowerBound).sstr<6>()
+ << llvm::formatv("{0:f}", UpperBound).sstr<6>();
+ };
+
+ auto VerifyRegister = [ReportError](SourceLocation Loc, uint32_t Register) {
+ if (!llvm::hlsl::rootsig::verifyRegisterValue(Register))
+ ReportError(Loc, 0, 0xfffffffe);
+ };
+
+ auto VerifySpace = [ReportError](SourceLocation Loc, uint32_t Space) {
+ if (!llvm::hlsl::rootsig::verifyRegisterSpace(Space))
+ ReportError(Loc, 0, 0xffffffef);
+ };
+
+ const uint32_t Version =
+ llvm::to_underlying(SemaRef.getLangOpts().HLSLRootSigVer);
+ const uint32_t VersionEnum = Version - 1;
+ auto ReportFlagError = [this, &HadError, VersionEnum](SourceLocation Loc) {
+ HadError = true;
+ this->Diag(Loc, diag::err_hlsl_invalid_rootsig_flag)
+ << /*version minor*/ VersionEnum;
+ };
+
+ // Iterate through the elements and do basic validations
+ for (const hlsl::RootSignatureElement &RootSigElem : Elements) {
+ SourceLocation Loc = RootSigElem.getLocation();
+ const llvm::hlsl::rootsig::RootElement &Elem = RootSigElem.getElement();
+ if (const auto *Descriptor =
+ std::get_if<llvm::hlsl::rootsig::RootDescriptor>(&Elem)) {
+ VerifyRegister(Loc, Descriptor->Reg.Number);
+ VerifySpace(Loc, Descriptor->Space);
+
+ if (!llvm::hlsl::rootsig::verifyRootDescriptorFlag(
+ Version, llvm::to_underlying(Descriptor->Flags)))
+ ReportFlagError(Loc);
+ } else if (const auto *Constants =
+ std::get_if<llvm::hlsl::rootsig::RootConstants>(&Elem)) {
+ VerifyRegister(Loc, Constants->Reg.Number);
+ VerifySpace(Loc, Constants->Space);
+ } else if (const auto *Sampler =
+ std::get_if<llvm::hlsl::rootsig::StaticSampler>(&Elem)) {
+ VerifyRegister(Loc, Sampler->Reg.Number);
+ VerifySpace(Loc, Sampler->Space);
+
+ assert(!std::isnan(Sampler->MaxLOD) && !std::isnan(Sampler->MinLOD) &&
+ "By construction, parseFloatParam can't produce a NaN from a "
+ "float_literal token");
+
+ if (!llvm::hlsl::rootsig::verifyMaxAnisotropy(Sampler->MaxAnisotropy))
+ ReportError(Loc, 0, 16);
+ if (!llvm::hlsl::rootsig::verifyMipLODBias(Sampler->MipLODBias))
+ ReportFloatError(Loc, -16.f, 15.99);
+ } else if (const auto *Clause =
+ std::get_if<llvm::hlsl::rootsig::DescriptorTableClause>(
+ &Elem)) {
+ VerifyRegister(Loc, Clause->Reg.Number);
+ VerifySpace(Loc, Clause->Space);
+
+ if (!llvm::hlsl::rootsig::verifyNumDescriptors(Clause->NumDescriptors)) {
+ // NumDescriptor could techincally be ~0u but that is reserved for
+ // unbounded, so the diagnostic will not report that as a valid int
+ // value
+ ReportError(Loc, 1, 0xfffffffe);
+ }
+
+ if (!llvm::hlsl::rootsig::verifyDescriptorRangeFlag(
+ Version, llvm::to_underlying(Clause->Type),
+ llvm::to_underlying(Clause->Flags)))
+ ReportFlagError(Loc);
+ }
+ }
+
using RangeInfo = llvm::hlsl::rootsig::RangeInfo;
using OverlappingRanges = llvm::hlsl::rootsig::OverlappingRanges;
using InfoPairT = std::pair<RangeInfo, const hlsl::RootSignatureElement *>;
@@ -1130,7 +1218,10 @@ bool SemaHLSL::handleRootSignatureElements(
&Elem)) {
RangeInfo Info;
Info.LowerBound = Clause->Reg.Number;
- assert(0 < Clause->NumDescriptors && "Verified as part of TODO(#129940)");
+ // 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 -
diff --git a/clang/test/AST/HLSL/RootSignatures-AST.hlsl b/clang/test/AST/HLSL/RootSignatures-AST.hlsl
index 27c40430c9d0a..df06165f1f1f9 100644
--- a/clang/test/AST/HLSL/RootSignatures-AST.hlsl
+++ b/clang/test/AST/HLSL/RootSignatures-AST.hlsl
@@ -13,14 +13,14 @@
#define SampleRS "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
"DENY_VERTEX_SHADER_ROOT_ACCESS), " \
- "CBV(b0, space = 1, flags = DATA_STATIC), " \
+ "CBV(b0, space = 1, flags = DATA_VOLATILE), " \
"SRV(t0), " \
"UAV(u0), " \
"DescriptorTable( CBV(b1), " \
"SRV(t1, numDescriptors = 8, " \
- " flags = DESCRIPTORS_VOLATILE), " \
+ " flags = DATA_VOLATILE | DESCRIPTORS_VOLATILE), " \
"UAV(u1, numDescriptors = unbounded, " \
- " flags = DESCRIPTORS_VOLATILE)), " \
+ " flags = DATA_VOLATILE | DESCRIPTORS_VOLATILE)), " \
"DescriptorTable(Sampler(s0, space=1, numDescriptors = 4)), " \
"RootConstants(num32BitConstants=3, b10), " \
"StaticSampler(s1)," \
@@ -34,7 +34,7 @@
// CHECK-SAME: RootElements{
// CHECK-SAME: RootFlags(AllowInputAssemblerInputLayout | DenyVertexShaderRootAccess),
// CHECK-SAME: RootCBV(b0,
-// CHECK-SAME: space = 1, visibility = All, flags = DataStatic
+// CHECK-SAME: space = 1, visibility = All, flags = DataVolatile
// CHECK-SAME: ),
// CHECK-SAME: RootSRV(t0,
// CHECK-SAME: space = 0, visibility = All,
@@ -50,10 +50,10 @@
// CHECK-V1_1-SAME: flags = DataStaticWhileSetAtExecute
// CHECK-SAME: ),
// CHECK-SAME: SRV(
-// CHECK-SAME: t1, numDescriptors = 8, space = 0, offset = DescriptorTableOffsetAppend, flags = DescriptorsVolatile
+// CHECK-SAME: t1, numDescriptors = 8, space = 0, offset = DescriptorTableOffsetAppend, flags = DescriptorsVolatile | DataVolatile
// CHECK-SAME: ),
// CHECK-SAME: UAV(
-// CHECK-SAME: u1, numDescriptors = unbounded, space = 0, offset = DescriptorTableOffsetAppend, flags = DescriptorsVolatile
+// CHECK-SAME: u1, numDescriptors = unbounded, space = 0, offset = DescriptorTableOffsetAppend, flags = DescriptorsVolatile | DataVolatile
// CHECK-SAME: ),
// CHECK-SAME: DescriptorTable(
// CHECK-SAME: numClauses = 3, visibility = All
@@ -92,14 +92,14 @@ void same_rs_main() {}
#define SampleSameRS \
"RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
"DENY_VERTEX_SHADER_ROOT_ACCESS), " \
- "CBV(b0, space = 1, flags = DATA_STATIC), " \
+ "CBV(b0, space = 1, flags = DATA_VOLATILE), " \
"SRV(t0), " \
"UAV(u0), " \
"DescriptorTable( CBV(b1), " \
- "SRV(t1, numDescriptors = 8, " \
- " flags = DESCRIPTORS_VOLATILE), " \
- "UAV(u1, numDescriptors = unbounded, " \
- " flags = DESCRIPTORS_VOLATILE)), " \
+ " SRV(t1, numDescriptors = 8, " \
+ " flags = DATA_VOLATILE | DESCRIPTORS_VOLATILE), " \
+ " UAV(u1, numDescriptors = unbounded, " \
+ " flags = DATA_VOLATILE | DESCRIPTORS_VOLATILE)), " \
"DescriptorTable(Sampler(s0, space=1, numDescriptors = 4)), " \
"RootConstants(num32BitConstants=3, b10), " \
"StaticSampler(s1)," \
diff --git a/clang/test/CodeGenHLSL/RootSignature.hlsl b/clang/test/CodeGenHLSL/RootSignature.hlsl
index 6618ca741aa9d..bc40bdd79ce59 100644
--- a/clang/test/CodeGenHLSL/RootSignature.hlsl
+++ b/clang/test/CodeGenHLSL/RootSignature.hlsl
@@ -76,7 +76,8 @@ void RootDescriptorsEntry() {}
// CHECK-SAME: i32 2, i32 3, i32 5,
// checking mipLODBias, maxAnisotropy, comparisonFunc, borderColor
-// CHECK-SAME: float 0x40403999A0000000, i32 9, i32 3, i32 2,
+// note: the hex value is the float bit representation of 12.45
+// CHECK-SAME: float 0x4028E66660000000, i32 9, i32 3, i32 2,
// checking minLOD, maxLOD
// CHECK-SAME: float -1.280000e+02, float 1.280000e+02,
@@ -90,7 +91,7 @@ void RootDescriptorsEntry() {}
" addressU = TEXTURE_ADDRESS_MIRROR, " \
" addressV = TEXTURE_ADDRESS_CLAMP, " \
" addressW = TEXTURE_ADDRESS_MIRRORONCE, " \
- " mipLODBias = 32.45f, maxAnisotropy = 9, " \
+ " mipLODBias = 12.45f, maxAnisotropy = 9, " \
" comparisonFunc = COMPARISON_EQUAL, " \
" borderColor = STATIC_BORDER_COLOR_OPAQUE_WHITE, " \
" minLOD = -128.f, maxLOD = 128.f, " \
diff --git a/clang/test/SemaHLSL/RootSignature-err.hlsl b/clang/test/SemaHLSL/RootSignature-err.hlsl
index 4b53a127d2adb..e517274d937b0 100644
--- a/clang/test/SemaHLSL/RootSignature-err.hlsl
+++ b/clang/test/SemaHLSL/RootSignature-err.hlsl
@@ -103,3 +103,39 @@ void bad_root_signature_22() {}
// expected-error at +1 {{invalid value of RootFlags}}
[RootSignature("RootFlags(local_root_signature | root_flag_typo)")]
void bad_root_signature_23() {}
+
+// Basic validation of register value and space
+
+// expected-error at +2 {{value must be in the range [0, 4294967294]}}
+// expected-error at +1 {{value must be in the range [0, 4294967279]}}
+[RootSignature("CBV(b4294967295, space = 4294967280)")]
+void basic_validation_0() {}
+
+// expected-error at +2 {{value must be in the range [0, 4294967294]}}
+// expected-error at +1 {{value must be in the range [0, 4294967279]}}
+[RootSignature("RootConstants(b4294967295, space = 4294967280, num32BitConstants = 1)")]
+void basic_validation_1() {}
+
+// expected-error at +2 {{value must be in the range [0, 4294967294]}}
+// expected-error at +1 {{value must be in the range [0, 4294967279]}}
+[RootSignature("StaticSampler(s4294967295, space = 4294967280)")]
+void basic_validation_2() {}
+
+// expected-error at +2 {{value must be in the range [0, 4294967294]}}
+// expected-error at +1 {{value must be in the range [0, 4294967279]}}
+[RootSignature("DescriptorTable(SRV(t4294967295, space = 4294967280))")]
+void basic_validation_3() {}
+
+// expected-error at +2 {{value must be in the range [1, 4294967294]}}
+// expected-error at +1 {{value must be in the range [1, 4294967294]}}
+[RootSignature("DescriptorTable(UAV(u0, numDescriptors = 0), Sampler(s0, numDescriptors = 0))")]
+void basic_validation_4() {}
+
+// expected-error at +2 {{value must be in the range [0, 16]}}
+// expected-error at +1 {{value must be in the range [-16.00, 15.99]}}
+[RootSignature("StaticSampler(s0, maxAnisotropy = 17, mipLODBias = -16.000001)")]
+void basic_validation_5() {}
+
+// expected-error at +1 {{value must be in the range [-16.00, 15.99]}}
+[RootSignature("StaticSampler(s0, mipLODBias = 15.990001)")]
+void basic_validation_6() {}
diff --git a/clang/test/SemaHLSL/RootSignature-flags-err.hlsl b/clang/test/SemaHLSL/RootSignature-flags-err.hlsl
new file mode 100644
index 0000000000000..9449d33cee1ad
--- /dev/null
+++ b/clang/test/SemaHLSL/RootSignature-flags-err.hlsl
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -fsyntax-only \
+// RUN: -fdx-rootsignature-version=rootsig_1_0 %s -verify=v10
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -fsyntax-only \
+// RUN: -fdx-rootsignature-version=rootsig_1_1 %s -verify=v11
+
+// Root Descriptor Flags:
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("CBV(b0, flags = DATA_STATIC)")]
+void bad_root_descriptor_flags_0() {}
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("CBV(b0, flags = DATA_STATIC_WHILE_SET_AT_EXECUTE)")]
+void bad_root_descriptor_flags_1() {}
+
+// v10-error at +2 {{invalid flags for version 1.0}}
+// v11-error at +1 {{invalid flags for version 1.1}}
+[RootSignature("CBV(b0, flags = DATA_STATIC | DATA_VOLATILE)")]
+void bad_root_descriptor_flags_2() {}
+
+// Descriptor Range Flags:
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DATA_VOLATILE))")]
+void bad_descriptor_range_flags_0() {}
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DATA_STATIC))")]
+void bad_descriptor_range_flags_1() {}
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DATA_STATIC_WHILE_SET_AT_EXECUTE | DESCRIPTORS_VOLATILE))")]
+void bad_descriptor_range_flags_2() {}
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DESCRIPTORS_VOLATILE))")]
+void bad_descriptor_range_flags_3() {}
+
+// v10-error at +1 {{invalid flags for version 1.0}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS))")]
+void bad_descriptor_range_flags_4() {}
+
+// v10-error at +2 {{invalid flags for version 1.0}}
+// v11-error at +1 {{invalid flags for version 1.1}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DATA_STATIC | DATA_STATIC_WHILE_SET_AT_EXECUTE))")]
+void bad_descriptor_range_flags_5() {}
+
+// v10-error at +2 {{invalid flags for version 1.0}}
+// v11-error at +1 {{invalid flags for version 1.1}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DESCRIPTORS_VOLATILE | DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS))")]
+void bad_descriptor_range_flags_6() {}
+
+// v10-error at +2 {{invalid flags for version 1.0}}
+// v11-error at +1 {{invalid flags for version 1.1}}
+[RootSignature("DescriptorTable(CBV(b0, flags = DESCRIPTORS_VOLATILE | DATA_STATIC))")]
+void bad_descriptor_range_flags_7() {}
+
More information about the cfe-commits
mailing list