llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-driver Author: Joshua Batista (bob80905) <details> <summary>Changes</summary> This PR adds validation for register numbers. Register numbers ought never to exceed UINT32_MAX, or 4294967295 Additionally, resource arrays will have each resource element bound sequentially, and those resource's register numbers should not exceed UINT32_MAX, or 4294967295. Even though not explicitly given a register number, their effective register number is also validated. This accounts for nested resource declarations and resource arrays too. Fixes https://github.com/llvm/llvm-project/issues/136809 --- Full diff: https://github.com/llvm/llvm-project/pull/174027.diff 12 Files Affected: - (modified) clang/include/clang/Basic/CodeGenOptions.def (+4-1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+1) - (modified) clang/include/clang/Options/Options.td (+7) - (modified) clang/lib/CodeGen/CGHLSLRuntime.cpp (+3-1) - (modified) clang/lib/Driver/ToolChains/Clang.cpp (+1) - (modified) clang/lib/Driver/ToolChains/HLSL.cpp (+6) - (modified) clang/lib/Sema/SemaHLSL.cpp (+102) - (added) clang/test/CodeGenHLSL/all-resources-bound.hlsl (+7) - (added) clang/test/Options/all_resources_bound.hlsl (+10) - (added) clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl (+47) - (modified) llvm/lib/Target/DirectX/DXILShaderFlags.cpp (+7) - (added) llvm/test/CodeGen/DirectX/ShaderFlags/all-resources-bound.ll (+25) ``````````diff diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index a059803c433e3..a1572578fb141 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -485,9 +485,12 @@ CODEGENOPT(ImportCallOptimization, 1, 0, Benign) /// (BlocksRuntime) on Windows. CODEGENOPT(StaticClosure, 1, 0, Benign) -/// Assume that UAVs/SRVs may alias +/// Assume that UAVs/SRVs may alias if enabled CODEGENOPT(ResMayAlias, 1, 0, Benign) +/// Assume that all resources are bound if enabled +CODEGENOPT(AllResourcesBound, 1, 0, Benign) + /// Controls how unwind v2 (epilog) information should be generated for x64 /// Windows. ENUM_CODEGENOPT(WinX64EHUnwindV2, WinX64EHUnwindV2Mode, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 51b6eba965103..a4f8bb44b1c40 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13326,6 +13326,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/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 8cd31a3be109a..04756ce486eaf 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -9612,6 +9612,13 @@ class DXCJoinedOrSeparate<string name> : Option<["/", "-"], name, KIND_JOINED_OR_SEPARATE>, Group<dxc_Group>, Visibility<[DXCOption]>; +def dxc_all_resources_bound : DXCFlag<"all-resources-bound">, + HelpText<"Enables agressive flattening">; +def hlsl_all_resources_bound : Option<["/", "-"], "hlsl-all-resources-bound", KIND_FLAG>, + Group<dxc_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enables agressive flattening">, + MarshallingInfoFlag<CodeGenOpts<"AllResourcesBound">>; def dxc_col_major : DXCFlag<"Zpc">, HelpText<"Pack matrices in column-major order">; def dxc_row_major : DXCFlag<"Zpr">, HelpText<"Pack matrices in row-major order">; def dxc_no_stdinc : DXCFlag<"hlsl-no-stdinc">, diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 2e9602d1b3793..4c0349d4be7bf 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -483,7 +483,9 @@ void CGHLSLRuntime::finishCodeGen() { addDxilValVersion(TargetOpts.DxilValidatorVersion, M); if (CodeGenOpts.ResMayAlias) M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.resmayalias", 1); - + if (CodeGenOpts.AllResourcesBound) + M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, + "dx.allresourcesbound", 1); // NativeHalfType corresponds to the -fnative-half-type clang option which is // aliased by clang-dxc's -enable-16bit-types option. This option is used to // set the UseNativeLowPrecision DXIL module flag in the DirectX backend diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 310f3b58a211e..2721bac473070 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3699,6 +3699,7 @@ static void RenderOpenCLOptions(const ArgList &Args, ArgStringList &CmdArgs, static void RenderHLSLOptions(const ArgList &Args, ArgStringList &CmdArgs, types::ID InputType) { const unsigned ForwardedArguments[] = { + options::OPT_hlsl_all_resources_bound, options::OPT_dxil_validator_version, options::OPT_res_may_alias, options::OPT_D, diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp index b76a3d7287975..587481b5623e1 100644 --- a/clang/lib/Driver/ToolChains/HLSL.cpp +++ b/clang/lib/Driver/ToolChains/HLSL.cpp @@ -417,6 +417,12 @@ HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, getDriver().Diag(diag::err_drv_dxc_invalid_matrix_layout); for (Arg *A : Args) { + if (A->getOption().getID() == options::OPT_dxc_all_resources_bound) { + DAL->AddFlagArg(nullptr, + Opts.getOption(options::OPT_hlsl_all_resources_bound)); + A->claim(); + continue; + } if (A->getOption().getID() == options::OPT_dxil_validator_version) { StringRef ValVerStr = A->getValue(); if (!isLegalValidatorVersion(ValVerStr, getDriver())) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index de3b946fd3b99..023ecc153af7e 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2359,6 +2359,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(); @@ -2418,6 +2511,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/CodeGenHLSL/all-resources-bound.hlsl b/clang/test/CodeGenHLSL/all-resources-bound.hlsl new file mode 100644 index 0000000000000..22703cc6eb0e5 --- /dev/null +++ b/clang/test/CodeGenHLSL/all-resources-bound.hlsl @@ -0,0 +1,7 @@ +// RUN: %clang_dxc -all-resources-bound -T lib_6_3 -HV 202x -Vd -Xclang -emit-llvm %s | FileCheck %s --check-prefix=FLAG +// RUN: %clang_dxc -T lib_6_3 -HV 202x -Xclang -emit-llvm %s | FileCheck %s --check-prefix=NOFLAG + +// FLAG-DAG: ![[ARB:.*]] = !{i32 1, !"dx.allresourcesbound", i32 1} +// FLAG-DAG: !llvm.module.flags = !{{{.*}}![[ARB]]{{.*}}} + +// NOFLAG-NOT: dx.allresourcesbound diff --git a/clang/test/Options/all_resources_bound.hlsl b/clang/test/Options/all_resources_bound.hlsl new file mode 100644 index 0000000000000..4c8fe7124f3be --- /dev/null +++ b/clang/test/Options/all_resources_bound.hlsl @@ -0,0 +1,10 @@ +// RUN: %clang_dxc -T lib_6_4 -all-resources-bound %s 2>&1 -### | FileCheck -check-prefix=ARB %s +// RUN: %clang_dxc -T lib_6_4 %s 2>&1 -### | FileCheck -check-prefix=NO_ARB %s + +// ARB: hlsl-all-resources-bound +// NO_ARB-NOT: hlsl-all-resources-bound +// assert expected CC1 option is present +float4 main(float4 a : A) : SV_TARGET +{ + return -a.yxxx; +} 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..70a226337ef6a --- /dev/null +++ b/clang/test/SemaHLSL/resource_binding_attr_error_uint32_max.hlsl @@ -0,0 +1,47 @@ +// 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]; +}; + +// 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 + +// 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 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> Buf2[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]; +}; + +// no errors expected, all 100 register numbers are occupied here +RWBuffer<float> Buf3[10][10] : register(u4294967194); diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index e0049dc75c0db..a4320209c53d6 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -287,6 +287,13 @@ ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M, if (CanSetResMayNotAlias && MMDI.ValidatorVersion < VersionTuple(1, 8)) CSF.ResMayNotAlias = !DRM.uavs().empty(); + // The command line option -all-resources-bound will set the + // dx.allresourcesbound module flag to 1 + if (auto *AllResourcesBound = mdconst::extract_or_null<ConstantInt>( + M.getModuleFlag("dx.allresourcesbound"))) + if (AllResourcesBound->getValue().getBoolValue()) + CSF.AllResourcesBound = true; + return CSF; } diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/all-resources-bound.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/all-resources-bound.ll new file mode 100644 index 0000000000000..14ddcfafdf5de --- /dev/null +++ b/llvm/test/CodeGen/DirectX/ShaderFlags/all-resources-bound.ll @@ -0,0 +1,25 @@ +; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s + +; This test checks to ensure that setting the LLVM module flag +; "dx.allresourcesbound" to 1 sets the corresponding DXIL shader flag + +target triple = "dxil-pc-shadermodel6.8-library" + +; CHECK: Combined Shader Flags for Module +; CHECK-NEXT: Shader Flags Value: 0x00000100 + +; CHECK: Note: shader requires additional functionality: +; CHECK-NEXT: extra DXIL module flags: +; CHECK-NEXT: All resources bound for the duration of shader execution + + +; CHECK: Function main : 0x00000100 +define float @main() #0 { + ret float 0.0 +} + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"dx.allresourcesbound", i32 1} + +attributes #0 = { convergent norecurse nounwind "hlsl.export"} `````````` </details> https://github.com/llvm/llvm-project/pull/174027 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
