https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/174028
>From 44ff1d1605aefc0dbd197397db9124346dd2411b Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Tue, 30 Dec 2025 13:46:14 -0800 Subject: [PATCH 1/8] first attempt --- .../clang/Basic/DiagnosticSemaKinds.td | 1 + clang/lib/Sema/SemaHLSL.cpp | 102 ++++++++++++++++++ ...esource_binding_attr_error_uint32_max.hlsl | 31 ++++++ 3 files changed, 134 insertions(+) create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6c6a26614ad0e..3807d1dab728a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13336,6 +13336,7 @@ def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in b def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError; def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError; def err_hlsl_unsupported_register_number : Error<"register number should be an integer">; +def err_hlsl_register_number_too_large : Error<"register number should not exceed UINT32_MAX, 4294967295">; def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">; def err_hlsl_space_on_global_constant : Error<"register space cannot be specified on global constants">; def warn_hlsl_implicit_binding : Warning<"resource has implicit register binding">, InGroup<HLSLImplicitBinding>, DefaultIgnore; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index a6de1cd550212..7760b1f791374 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2361,6 +2361,99 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc, return ValidateMultipleRegisterAnnotations(S, D, RegType); } +bool ExceedsUInt32Max(llvm::StringRef S) { + constexpr size_t MaxDigits = 10; // UINT32_MAX = 4294967295 + if (S.size() > MaxDigits) + return true; + + if (S.size() < MaxDigits) + return false; + + return S.compare("4294967295") > 0; +} + +// return false if the slot count exceeds the limit, true otherwise +static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount, + const llvm::APInt &Limit, + ASTContext &Ctx, + uint64_t Multiplier = 1) { + Ty = Ty.getCanonicalType(); + const Type *T = Ty.getTypePtr(); + + // Early exit if already overflowed + if (SlotCount.ugt(Limit)) + return false; + + // Case 1: array type + if (const auto *AT = dyn_cast<ArrayType>(T)) { + uint64_t Count = 1; + + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) { + Count = CAT->getSize().getZExtValue(); + } + // TODO: how do we handle non constant resource arrays? + + QualType ElemTy = AT->getElementType(); + + return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx, + Multiplier * Count); + } + + // Case 2: resource leaf + if (T->isHLSLResourceRecord()) { + llvm::APInt Add(SlotCount.getBitWidth(), Multiplier); + SlotCount += Add; + return SlotCount.ule(Limit); + } + + // Case 3: struct / record + if (const auto *RT = dyn_cast<RecordType>(T)) { + const RecordDecl *RD = RT->getDecl(); + for (const FieldDecl *Field : RD->fields()) { + if (!AccumulateHLSLResourceSlots(Field->getType(), SlotCount, Limit, Ctx, + Multiplier)) + return false; + } + return true; + } + + // Case 4: everything else + return true; +} + +// return true if there is something invalid, false otherwise +bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl, + ASTContext &Ctx) { + if (ExceedsUInt32Max(SlotNumStr)) + return true; + + llvm::APInt SlotNum; + if (SlotNumStr.getAsInteger(10, SlotNum)) + return false; + SlotNum = SlotNum.zext(64); + + // uint32_max isn't 64 bits, but this int should + // have a 64 bit width in case it is compared to + // another 64 bit-width value. Assert failure otherwise. + llvm::APInt Limit(64, UINT32_MAX); + VarDecl *VD = dyn_cast<VarDecl>(TheDecl); + if (VD) { + AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx); + return SlotNum.ugt(Limit); + } + // handle the cbuffer case + HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl); + if (HBD) { + // resources cannot be put within a cbuffer, so no need + // to analyze the structure since the register number + // won't be pushed any higher. + return SlotNum.ugt(Limit); + } + + // we don't expect any other decl type, so fail + return true; +} + void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) { QualType Ty = VD->getType(); @@ -2420,6 +2513,15 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { return; } StringRef SlotNumStr = Slot.substr(1); + + // Validate register number. It should not exceed UINT32_MAX, + // including if the resource type is an array that starts + // before UINT32_MAX, but ends afterwards. + if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext())) { + Diag(SlotLoc, diag::err_hlsl_register_number_too_large); + return; + } + unsigned N; if (SlotNumStr.getAsInteger(10, N)) { Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl new file mode 100644 index 0000000000000..81a51fb601e54 --- /dev/null +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + +// test semantic validation for register numbers that exceed UINT32_MAX + +struct S { + RWBuffer<float> A[4]; + RWBuffer<int> B[10]; +}; + +// test that S.A carries the register number over the limit and emits the error +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +S s : register(u4294967294); // UINT32_MAX - 1 + +// test the error is also triggered when analyzing S.B +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +S s2 : register(u4294967289); + +// test a standard resource array +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +RWBuffer<float> Buf[10] : register(u4294967294); + +// test directly an excessively high register number. +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +RWBuffer<float> A : register(u9995294967294); + +// test a struct within a cbuffer +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +cbuffer MyCB : register(b9995294967294) { + float F[4]; + int I[10]; +}; >From 6ffc9ec83357b45c59439a0eaaad5081c64f9d49 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Tue, 30 Dec 2025 13:57:39 -0800 Subject: [PATCH 2/8] add some more tests --- ...resource_binding_attr_error_uint32_max.hlsl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index 81a51fb601e54..70a226337ef6a 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -7,6 +7,11 @@ struct S { RWBuffer<int> B[10]; }; +// do some more nesting +struct S2 { + S a[3]; +}; + // test that S.A carries the register number over the limit and emits the error // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} S s : register(u4294967294); // UINT32_MAX - 1 @@ -15,9 +20,17 @@ S s : register(u4294967294); // UINT32_MAX - 1 // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} S s2 : register(u4294967289); + +// test the error is also triggered when analyzing S2.a[1].B +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +S2 s3 : register(u4294967275); + +// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +RWBuffer<float> Buf[10][10] : register(u4294967234); + // test a standard resource array // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} -RWBuffer<float> Buf[10] : register(u4294967294); +RWBuffer<float> Buf2[10] : register(u4294967294); // test directly an excessively high register number. // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} @@ -29,3 +42,6 @@ cbuffer MyCB : register(b9995294967294) { float F[4]; int I[10]; }; + +// no errors expected, all 100 register numbers are occupied here +RWBuffer<float> Buf3[10][10] : register(u4294967194); >From 8d838fcfcf86ce2a78ab795e0f9518eeb2b12e5e Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Tue, 30 Dec 2025 14:07:09 -0800 Subject: [PATCH 3/8] adjust comment --- clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index 70a226337ef6a..d2e089391143c 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -36,7 +36,7 @@ RWBuffer<float> Buf2[10] : register(u4294967294); // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} RWBuffer<float> A : register(u9995294967294); -// test a struct within a cbuffer +// test a cbuffer directly with an excessively high register number. // expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} cbuffer MyCB : register(b9995294967294) { float F[4]; >From 8968c821e246667c24c8d7430aa89e54bb3c8dfc Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Wed, 31 Dec 2025 00:39:59 -0800 Subject: [PATCH 4/8] address Damyan --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaHLSL.cpp | 59 +++++++------------ ...esource_binding_attr_error_uint32_max.hlsl | 20 ++++--- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3807d1dab728a..38205d6087f68 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13336,7 +13336,7 @@ def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in b def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError; def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError; def err_hlsl_unsupported_register_number : Error<"register number should be an integer">; -def err_hlsl_register_number_too_large : Error<"register number should not exceed UINT32_MAX, 4294967295">; +def err_hlsl_register_number_too_large : Error<"register number should not exceed 4294967295">; def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">; def err_hlsl_space_on_global_constant : Error<"register space cannot be specified on global constants">; def warn_hlsl_implicit_binding : Warning<"resource has implicit register binding">, InGroup<HLSLImplicitBinding>, DefaultIgnore; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 7760b1f791374..157c6c0f6def7 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2361,27 +2361,15 @@ static bool DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc, return ValidateMultipleRegisterAnnotations(S, D, RegType); } -bool ExceedsUInt32Max(llvm::StringRef S) { - constexpr size_t MaxDigits = 10; // UINT32_MAX = 4294967295 - if (S.size() > MaxDigits) - return true; - - if (S.size() < MaxDigits) - return false; - - return S.compare("4294967295") > 0; -} - // return false if the slot count exceeds the limit, true otherwise -static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount, - const llvm::APInt &Limit, - ASTContext &Ctx, +static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, + const uint64_t &Limit, ASTContext &Ctx, uint64_t Multiplier = 1) { Ty = Ty.getCanonicalType(); const Type *T = Ty.getTypePtr(); // Early exit if already overflowed - if (SlotCount.ugt(Limit)) + if (SlotCount > Limit) return false; // Case 1: array type @@ -2391,19 +2379,17 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount, if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) { Count = CAT->getSize().getZExtValue(); } - // TODO: how do we handle non constant resource arrays? QualType ElemTy = AT->getElementType(); - return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx, Multiplier * Count); } // Case 2: resource leaf if (T->isHLSLResourceRecord()) { - llvm::APInt Add(SlotCount.getBitWidth(), Multiplier); - SlotCount += Add; - return SlotCount.ule(Limit); + + SlotCount += Multiplier; + return SlotCount <= Limit; } // Case 3: struct / record @@ -2422,24 +2408,17 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, llvm::APInt &SlotCount, } // return true if there is something invalid, false otherwise -bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl, - ASTContext &Ctx) { - if (ExceedsUInt32Max(SlotNumStr)) - return true; - - llvm::APInt SlotNum; +static bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl, + ASTContext &Ctx) { + uint64_t SlotNum; if (SlotNumStr.getAsInteger(10, SlotNum)) return false; - SlotNum = SlotNum.zext(64); - // uint32_max isn't 64 bits, but this int should - // have a 64 bit width in case it is compared to - // another 64 bit-width value. Assert failure otherwise. - llvm::APInt Limit(64, UINT32_MAX); + uint64_t Limit = UINT32_MAX; VarDecl *VD = dyn_cast<VarDecl>(TheDecl); if (VD) { AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx); - return SlotNum.ugt(Limit); + return SlotNum > Limit; } // handle the cbuffer case HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl); @@ -2447,7 +2426,7 @@ bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl, // resources cannot be put within a cbuffer, so no need // to analyze the structure since the register number // won't be pushed any higher. - return SlotNum.ugt(Limit); + return SlotNum > Limit; } // we don't expect any other decl type, so fail @@ -2514,6 +2493,13 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { } StringRef SlotNumStr = Slot.substr(1); + unsigned N; + // validate that the stringref has a non-empty number + if (SlotNumStr.empty() || !llvm::all_of(SlotNumStr, llvm::isDigit)) { + Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); + return; + } + // Validate register number. It should not exceed UINT32_MAX, // including if the resource type is an array that starts // before UINT32_MAX, but ends afterwards. @@ -2522,11 +2508,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { return; } - unsigned N; - if (SlotNumStr.getAsInteger(10, N)) { - Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); - return; - } + // this shouldn't fail now that it's validated. + assert(!SlotNumStr.getAsInteger(10, N)); SlotNum = N; } diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index d2e089391143c..9f2f4b0a7d816 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -13,31 +13,31 @@ struct S2 { }; // test that S.A carries the register number over the limit and emits the error -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} S s : register(u4294967294); // UINT32_MAX - 1 // test the error is also triggered when analyzing S.B -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} S s2 : register(u4294967289); // test the error is also triggered when analyzing S2.a[1].B -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} S2 s3 : register(u4294967275); -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} RWBuffer<float> Buf[10][10] : register(u4294967234); // test a standard resource array -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} RWBuffer<float> Buf2[10] : register(u4294967294); // test directly an excessively high register number. -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} RWBuffer<float> A : register(u9995294967294); // test a cbuffer directly with an excessively high register number. -// expected-error@+1 {{register number should not exceed UINT32_MAX, 4294967295}} +// expected-error@+1 {{register number should not exceed 4294967295}} cbuffer MyCB : register(b9995294967294) { float F[4]; int I[10]; @@ -45,3 +45,9 @@ cbuffer MyCB : register(b9995294967294) { // no errors expected, all 100 register numbers are occupied here RWBuffer<float> Buf3[10][10] : register(u4294967194); + +// expected-error@+1 {{register number should be an integer}} +RWBuffer<float> Buf4[10][10] : register(ud); + +// expected-error@+1 {{expected <numeric_constant>}} +RWBuffer<float> BadBuf[10][10] : register(u); >From 34d30832c0f017206de32824324f76ac9a431c83 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Wed, 31 Dec 2025 10:50:30 -0800 Subject: [PATCH 5/8] try to fix macos issue --- clang/lib/Sema/SemaHLSL.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 157c6c0f6def7..207fd7abb827e 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2408,7 +2408,7 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, } // return true if there is something invalid, false otherwise -static bool ValidateRegisterNumber(StringRef SlotNumStr, Decl *TheDecl, +static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl, ASTContext &Ctx) { uint64_t SlotNum; if (SlotNumStr.getAsInteger(10, SlotNum)) @@ -2491,7 +2491,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { Diag(SlotLoc, diag::warn_hlsl_deprecated_register_type_i); return; } - StringRef SlotNumStr = Slot.substr(1); + const StringRef SlotNumStr = Slot.substr(1); unsigned N; // validate that the stringref has a non-empty number @@ -2508,8 +2508,10 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { return; } - // this shouldn't fail now that it's validated. - assert(!SlotNumStr.getAsInteger(10, N)); + if (SlotNumStr.getAsInteger(10, N)) { + Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); + return; + } SlotNum = N; } >From 05d31181e14f3f6e18034b5e49230a4c3a7efc3f Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Wed, 31 Dec 2025 12:33:58 -0800 Subject: [PATCH 6/8] address Finn --- clang/lib/Sema/SemaHLSL.cpp | 34 +++++++++++-------- ...esource_binding_attr_error_uint32_max.hlsl | 7 ++-- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 207fd7abb827e..e30f5b4e5d8dc 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2376,9 +2376,8 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, if (const auto *AT = dyn_cast<ArrayType>(T)) { uint64_t Count = 1; - if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) { + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) Count = CAT->getSize().getZExtValue(); - } QualType ElemTy = AT->getElementType(); return AccumulateHLSLResourceSlots(ElemTy, SlotCount, Limit, Ctx, @@ -2395,11 +2394,11 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, // Case 3: struct / record if (const auto *RT = dyn_cast<RecordType>(T)) { const RecordDecl *RD = RT->getDecl(); - for (const FieldDecl *Field : RD->fields()) { + for (const FieldDecl *Field : RD->fields()) if (!AccumulateHLSLResourceSlots(Field->getType(), SlotCount, Limit, Ctx, Multiplier)) return false; - } + return true; } @@ -2409,24 +2408,32 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, // return true if there is something invalid, false otherwise static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl, - ASTContext &Ctx) { + ASTContext &Ctx, unsigned &Result) { uint64_t SlotNum; if (SlotNumStr.getAsInteger(10, SlotNum)) return false; uint64_t Limit = UINT32_MAX; - VarDecl *VD = dyn_cast<VarDecl>(TheDecl); - if (VD) { + if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) { AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx); - return SlotNum > Limit; + + bool TooHigh = SlotNum > Limit; + if (!TooHigh) + SlotNumStr.getAsInteger(10, Result); + return TooHigh; } // handle the cbuffer case HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl); if (HBD) { + SlotNumStr.getAsInteger(10, Result); // resources cannot be put within a cbuffer, so no need // to analyze the structure since the register number // won't be pushed any higher. - return SlotNum > Limit; + bool TooHigh = SlotNum > Limit; + if (!TooHigh) + SlotNumStr.getAsInteger(10, Result); + + return TooHigh; } // we don't expect any other decl type, so fail @@ -2494,7 +2501,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { const StringRef SlotNumStr = Slot.substr(1); unsigned N; - // validate that the stringref has a non-empty number + + // validate that the slot number is a non-empty number if (SlotNumStr.empty() || !llvm::all_of(SlotNumStr, llvm::isDigit)) { Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); return; @@ -2503,15 +2511,11 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) { // Validate register number. It should not exceed UINT32_MAX, // including if the resource type is an array that starts // before UINT32_MAX, but ends afterwards. - if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext())) { + if (ValidateRegisterNumber(SlotNumStr, TheDecl, getASTContext(), N)) { Diag(SlotLoc, diag::err_hlsl_register_number_too_large); return; } - if (SlotNumStr.getAsInteger(10, N)) { - Diag(SlotLoc, diag::err_hlsl_unsupported_register_number); - return; - } SlotNum = N; } diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index 9f2f4b0a7d816..7ed084aec2cfd 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -2,6 +2,10 @@ // test semantic validation for register numbers that exceed UINT32_MAX + +// expected-error@+1 {{expected <numeric_constant>}} +RWBuffer<float> BadBuf[10][10] : register(u); + struct S { RWBuffer<float> A[4]; RWBuffer<int> B[10]; @@ -48,6 +52,3 @@ RWBuffer<float> Buf3[10][10] : register(u4294967194); // expected-error@+1 {{register number should be an integer}} RWBuffer<float> Buf4[10][10] : register(ud); - -// expected-error@+1 {{expected <numeric_constant>}} -RWBuffer<float> BadBuf[10][10] : register(u); >From d7150e90fe2219528f3a98fbd88b4f0a7ee1b961 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Wed, 31 Dec 2025 12:45:53 -0800 Subject: [PATCH 7/8] one quick move --- clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++ .../test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl index afd7d407c34c2..66b42825c5cf7 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl @@ -34,6 +34,9 @@ cbuffer D : register(b 2, space3) {} // expected-error@+1 {{expected <numeric_constant>}} cbuffer E : register(u-1) {}; +// expected-error@+1 {{expected <numeric_constant>}} +cbuffer F : register(u) {}; + // expected-error@+1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}} static MyTemplatedSRV<float> U : register(u5); diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index 7ed084aec2cfd..6f35aed0f2ccb 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -2,10 +2,6 @@ // test semantic validation for register numbers that exceed UINT32_MAX - -// expected-error@+1 {{expected <numeric_constant>}} -RWBuffer<float> BadBuf[10][10] : register(u); - struct S { RWBuffer<float> A[4]; RWBuffer<int> B[10]; >From 59412b504c4e5753478eee82970e8e861a5b3ee2 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Wed, 31 Dec 2025 13:29:42 -0800 Subject: [PATCH 8/8] fix off by one error, and add test --- clang/lib/Sema/SemaHLSL.cpp | 41 +++++++++++-------- ...esource_binding_attr_error_uint32_max.hlsl | 12 ++++-- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index e30f5b4e5d8dc..db96b49f93a9f 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2386,9 +2386,14 @@ static bool AccumulateHLSLResourceSlots(QualType Ty, uint64_t &SlotCount, // Case 2: resource leaf if (T->isHLSLResourceRecord()) { + // Validate highest slot used + uint64_t EndSlot = SlotCount + Multiplier - 1; + if (EndSlot > Limit) + return false; - SlotCount += Multiplier; - return SlotCount <= Limit; + // Advance SlotCount past the consumed range + SlotCount = EndSlot + 1; + return true; } // Case 3: struct / record @@ -2411,29 +2416,33 @@ static bool ValidateRegisterNumber(const StringRef SlotNumStr, Decl *TheDecl, ASTContext &Ctx, unsigned &Result) { uint64_t SlotNum; if (SlotNumStr.getAsInteger(10, SlotNum)) - return false; + return true; - uint64_t Limit = UINT32_MAX; + const uint64_t Limit = UINT32_MAX; if (VarDecl *VD = dyn_cast<VarDecl>(TheDecl)) { - AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx); + uint64_t BaseSlot = SlotNum; + + if (!AccumulateHLSLResourceSlots(VD->getType(), SlotNum, Limit, Ctx)) + return true; + + // After AccumulateHLSLResourceSlots runs, SlotNum is now + // the first free slot; last used was SlotNum - 1 + if (BaseSlot > Limit) + return true; - bool TooHigh = SlotNum > Limit; - if (!TooHigh) - SlotNumStr.getAsInteger(10, Result); - return TooHigh; + SlotNumStr.getAsInteger(10, Result); + return false; } // handle the cbuffer case - HLSLBufferDecl *HBD = dyn_cast<HLSLBufferDecl>(TheDecl); - if (HBD) { - SlotNumStr.getAsInteger(10, Result); + if (dyn_cast<HLSLBufferDecl>(TheDecl)) { // resources cannot be put within a cbuffer, so no need // to analyze the structure since the register number // won't be pushed any higher. - bool TooHigh = SlotNum > Limit; - if (!TooHigh) - SlotNumStr.getAsInteger(10, Result); + if (SlotNum > Limit) + return true; - return TooHigh; + SlotNumStr.getAsInteger(10, Result); + return false; } // we don't expect any other decl type, so fail diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl index 6f35aed0f2ccb..780d35aa0b346 100644 --- a/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -43,8 +43,14 @@ cbuffer MyCB : register(b9995294967294) { int I[10]; }; -// no errors expected, all 100 register numbers are occupied here -RWBuffer<float> Buf3[10][10] : register(u4294967194); // expected-error@+1 {{register number should be an integer}} -RWBuffer<float> Buf4[10][10] : register(ud); +RWBuffer<float> Buf3[10][10] : register(ud); + +// this should work +RWBuffer<float> GoodBuf : register(u4294967295); + +// no errors expected, all 100 register numbers are occupied here +RWBuffer<float> GoodBufArray[10][10] : register(u4294967194); + + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
