From e93a0d0d1ed790173e23247fa2833cdac30b8268 Mon Sep 17 00:00:00 2001 From: Finn Plummer Date: Tue, 24 Jun 2025 16:21:24 -0700 Subject: [PATCH] [HLSL][RootSignature] Add `fdx-rootsignature-version` option to specify root signature version (#144813) This pr provides the ability to specify the root signature version as a compiler option and to retain this in the root signature decl. It also updates the methods to serialize the version when dumping the declaration and to output the version when generating the metadata. - Update `DXContainer.hI` to define the root signature versions - Update `Options.td` and `LangOpts.h` to define the `fdx-rootsignature-version` compiler option - Update `Options.td` to provide an alias `force-rootsig-ver` in clang-dxc - Update `Decl.[h|cpp]` and `SeamHLSL.cpp` so that `RootSignatureDecl` will retain its version type - Updates `CGHLSLRuntime.cpp` to generate the extra metadata field - Add tests to illustrate Resolves https://github.com/llvm/llvm-project/issues/126557. Note: this does not implement validation based on versioning. https://github.com/llvm/llvm-project/issues/129940 is required to retrieve the version and use it for validations. --- clang/include/clang/AST/Decl.h | 7 +++++++ clang/include/clang/Basic/LangOptions.h | 5 +++++ clang/include/clang/Driver/Options.td | 14 +++++++++++++ clang/lib/AST/Decl.cpp | 16 +++++++------- clang/lib/AST/TextNodeDumper.cpp | 10 +++++++++ clang/lib/CodeGen/CGHLSLRuntime.cpp | 19 +++++++++-------- clang/lib/Driver/ToolChains/Clang.cpp | 22 +++++++++++--------- clang/lib/Driver/ToolChains/HLSL.cpp | 7 +++++++ clang/lib/Frontend/CompilerInvocation.cpp | 4 ++++ clang/lib/Sema/SemaHLSL.cpp | 2 +- clang/test/AST/HLSL/RootSignatures-AST.hlsl | 8 +++++++ clang/test/Driver/dxc_hlsl-rootsig-ver.hlsl | 16 ++++++++++++++ llvm/include/llvm/BinaryFormat/DXContainer.h | 6 ++++++ 13 files changed, 109 insertions(+), 27 deletions(-) create mode 100644 clang/test/Driver/dxc_hlsl-rootsig-ver.hlsl diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0da940883b6f..c4202f1f3d07 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -41,6 +41,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/DXContainer.h" #include "llvm/Frontend/HLSL/HLSLRootSignature.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" @@ -5181,6 +5182,8 @@ class HLSLRootSignatureDecl final llvm::hlsl::rootsig::RootElement> { friend TrailingObjects; + llvm::dxbc::RootSignatureVersion Version; + unsigned NumElems; llvm::hlsl::rootsig::RootElement *getElems() { return getTrailingObjects(); } @@ -5190,16 +5193,20 @@ class HLSLRootSignatureDecl final } HLSLRootSignatureDecl(DeclContext *DC, SourceLocation Loc, IdentifierInfo *ID, + llvm::dxbc::RootSignatureVersion Version, unsigned NumElems); public: static HLSLRootSignatureDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation Loc, IdentifierInfo *ID, + llvm::dxbc::RootSignatureVersion Version, ArrayRef RootElements); static HLSLRootSignatureDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + llvm::dxbc::RootSignatureVersion getVersion() const { return Version; } + ArrayRef getRootElements() const { return {getElems(), NumElems}; } diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 491e8bee9fd5..b7c5ed994c99 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -24,6 +24,7 @@ #include "clang/Basic/Visibility.h" #include "llvm/ADT/FloatingPointMode.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/DXContainer.h" #include "llvm/TargetParser/Triple.h" #include #include @@ -623,6 +624,10 @@ public: // implementation on real-world examples. std::string OpenACCMacroOverride; + /// The HLSL root signature version for dxil. + llvm::dxbc::RootSignatureVersion HLSLRootSigVer = + llvm::dxbc::RootSignatureVersion::V1_1; + // Indicates if the wasm-opt binary must be ignored in the case of a // WebAssembly target. bool NoWasmOpt = false; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 26e953f7ac61..0822df2640d2 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -9312,6 +9312,20 @@ def fcgl : DXCFlag<"fcgl">, Alias; def enable_16bit_types : DXCFlag<"enable-16bit-types">, Alias, HelpText<"Enable 16-bit types and disable min precision types." "Available in HLSL 2018 and shader model 6.2.">; +def fdx_rootsignature_version : + Joined<["-"], "fdx-rootsignature-version=">, + Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Root Signature Version">, + Values<"rootsig_1_0,rootsig_1_1">, + NormalizedValuesScope<"llvm::dxbc::RootSignatureVersion">, + NormalizedValues<["V1_0", "V1_1"]>, + MarshallingInfoEnum, "V1_1">; +def dxc_rootsig_ver : + Separate<["/", "-"], "force-rootsig-ver">, + Alias, + Group, + Visibility<[DXCOption]>; def hlsl_entrypoint : Option<["-"], "hlsl-entry", KIND_SEPARATE>, Group, Visibility<[ClangOption, CC1Option]>, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 35c41859595d..bc88045c17f7 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5878,21 +5878,21 @@ bool HLSLBufferDecl::buffer_decls_empty() { // HLSLRootSignatureDecl Implementation //===----------------------------------------------------------------------===// -HLSLRootSignatureDecl::HLSLRootSignatureDecl(DeclContext *DC, - SourceLocation Loc, - IdentifierInfo *ID, - unsigned NumElems) +HLSLRootSignatureDecl::HLSLRootSignatureDecl( + DeclContext *DC, SourceLocation Loc, IdentifierInfo *ID, + llvm::dxbc::RootSignatureVersion Version, unsigned NumElems) : NamedDecl(Decl::Kind::HLSLRootSignature, DC, Loc, DeclarationName(ID)), - NumElems(NumElems) {} + Version(Version), NumElems(NumElems) {} HLSLRootSignatureDecl *HLSLRootSignatureDecl::Create( ASTContext &C, DeclContext *DC, SourceLocation Loc, IdentifierInfo *ID, + llvm::dxbc::RootSignatureVersion Version, ArrayRef RootElements) { HLSLRootSignatureDecl *RSDecl = new (C, DC, additionalSizeToAlloc( RootElements.size())) - HLSLRootSignatureDecl(DC, Loc, ID, RootElements.size()); + HLSLRootSignatureDecl(DC, Loc, ID, Version, RootElements.size()); auto *StoredElems = RSDecl->getElems(); llvm::uninitialized_copy(RootElements, StoredElems); return RSDecl; @@ -5901,7 +5901,9 @@ HLSLRootSignatureDecl *HLSLRootSignatureDecl::Create( HLSLRootSignatureDecl * HLSLRootSignatureDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { HLSLRootSignatureDecl *Result = new (C, ID) - HLSLRootSignatureDecl(nullptr, SourceLocation(), nullptr, /*NumElems=*/0); + HLSLRootSignatureDecl(nullptr, SourceLocation(), nullptr, + /*Version*/ llvm::dxbc::RootSignatureVersion::V1_1, + /*NumElems=*/0); return Result; } diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 1b84b8824047..bb860a8f7674 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -3041,6 +3041,16 @@ void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { void TextNodeDumper::VisitHLSLRootSignatureDecl( const HLSLRootSignatureDecl *D) { dumpName(D); + OS << " version: "; + switch (D->getVersion()) { + case llvm::dxbc::RootSignatureVersion::V1_0: + OS << "1.0"; + break; + case llvm::dxbc::RootSignatureVersion::V1_1: + OS << "1.1"; + break; + } + OS << ", "; llvm::hlsl::rootsig::dumpRootElements(OS, D->getRootElements()); } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 3103f1798e14..f2e992fb7fa6 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -66,17 +66,16 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { DXILValMD->addOperand(Val); } -void addRootSignature(ArrayRef Elements, +void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer, + ArrayRef Elements, llvm::Function *Fn, llvm::Module &M) { auto &Ctx = M.getContext(); - llvm::hlsl::rootsig::MetadataBuilder Builder(Ctx, Elements); - MDNode *RootSignature = Builder.BuildRootSignature(); + llvm::hlsl::rootsig::MetadataBuilder RSBuilder(Ctx, Elements); + MDNode *RootSignature = RSBuilder.BuildRootSignature(); - // TODO: We need to wire the root signature version up through the frontend - // rather than hardcoding it. - ConstantAsMetadata *Version = - ConstantAsMetadata::get(ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 2)); + ConstantAsMetadata *Version = ConstantAsMetadata::get(ConstantInt::get( + llvm::Type::getInt32Ty(Ctx), llvm::to_underlying(RootSigVer))); MDNode *MDVals = MDNode::get(Ctx, {ValueAsMetadata::get(Fn), RootSignature, Version}); @@ -471,9 +470,11 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, // Add and identify root signature to function, if applicable for (const Attr *Attr : FD->getAttrs()) { - if (const auto *RSAttr = dyn_cast(Attr)) - addRootSignature(RSAttr->getSignatureDecl()->getRootElements(), EntryFn, + if (const auto *RSAttr = dyn_cast(Attr)) { + auto *RSDecl = RSAttr->getSignatureDecl(); + addRootSignature(RSDecl->getVersion(), RSDecl->getRootElements(), EntryFn, M); + } } } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 25c65ab52fba..8a18865b899d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3827,16 +3827,18 @@ static void RenderOpenCLOptions(const ArgList &Args, ArgStringList &CmdArgs, static void RenderHLSLOptions(const ArgList &Args, ArgStringList &CmdArgs, types::ID InputType) { - const unsigned ForwardedArguments[] = {options::OPT_dxil_validator_version, - options::OPT_res_may_alias, - options::OPT_D, - options::OPT_I, - options::OPT_O, - options::OPT_emit_llvm, - options::OPT_emit_obj, - options::OPT_disable_llvm_passes, - options::OPT_fnative_half_type, - options::OPT_hlsl_entrypoint}; + const unsigned ForwardedArguments[] = { + options::OPT_dxil_validator_version, + options::OPT_res_may_alias, + options::OPT_D, + options::OPT_I, + options::OPT_O, + options::OPT_emit_llvm, + options::OPT_emit_obj, + options::OPT_disable_llvm_passes, + options::OPT_fnative_half_type, + options::OPT_hlsl_entrypoint, + options::OPT_fdx_rootsignature_version}; if (!types::isHLSL(InputType)) return; for (const auto &Arg : ForwardedArguments) diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp index be59cc895667..68c32ccdbb7c 100644 --- a/clang/lib/Driver/ToolChains/HLSL.cpp +++ b/clang/lib/Driver/ToolChains/HLSL.cpp @@ -295,6 +295,13 @@ HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, A->claim(); continue; } + if (A->getOption().getID() == options::OPT_dxc_rootsig_ver) { + DAL->AddJoinedArg(nullptr, + Opts.getOption(options::OPT_fdx_rootsignature_version), + A->getValue()); + A->claim(); + continue; + } if (A->getOption().getID() == options::OPT__SLASH_O) { StringRef OStr = A->getValue(); if (OStr == "d") { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5c52dc33ddf6..9e269ab244d4 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -636,6 +636,10 @@ static bool FixupInvocation(CompilerInvocation &Invocation, Diags.Report(diag::err_drv_argument_not_allowed_with) << "-hlsl-entry" << GetInputKindName(IK); + if (Args.hasArg(OPT_fdx_rootsignature_version) && !LangOpts.HLSL) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "-fdx-rootsignature-version" << GetInputKindName(IK); + if (Args.hasArg(OPT_fgpu_allow_device_init) && !LangOpts.HIP) Diags.Report(diag::warn_ignored_hip_only_option) << Args.getLastArg(OPT_fgpu_allow_device_init)->getAsString(Args); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 506da8a361ed..0974ccbf9267 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1067,7 +1067,7 @@ void SemaHLSL::ActOnFinishRootSignatureDecl( auto *SignatureDecl = HLSLRootSignatureDecl::Create( SemaRef.getASTContext(), /*DeclContext=*/SemaRef.CurContext, Loc, - DeclIdent, Elements); + DeclIdent, SemaRef.getLangOpts().HLSLRootSigVer, Elements); if (handleRootSignatureDecl(SignatureDecl, Loc)) return; diff --git a/clang/test/AST/HLSL/RootSignatures-AST.hlsl b/clang/test/AST/HLSL/RootSignatures-AST.hlsl index bc74522bf927..1e60b9367c14 100644 --- a/clang/test/AST/HLSL/RootSignatures-AST.hlsl +++ b/clang/test/AST/HLSL/RootSignatures-AST.hlsl @@ -1,5 +1,11 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -ast-dump \ // RUN: -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -ast-dump \ +// RUN: -fdx-rootsignature-version=rootsig_1_0 \ +// RUN: -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-V1_0 +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -ast-dump \ +// RUN: -fdx-rootsignature-version=rootsig_1_1 \ +// RUN: -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-V1_1 // This test ensures that the sample root signature is parsed without error and // the Attr AST Node is created succesfully. If an invalid root signature was @@ -23,6 +29,8 @@ "filter = FILTER_MIN_MAG_MIP_LINEAR )" // CHECK: -HLSLRootSignatureDecl 0x{{.*}} {{.*}} implicit [[SAMPLE_RS_DECL:__hlsl_rootsig_decl_\d*]] +// CHECK-V1_0: version: 1.0, +// CHECK-V1_1: version: 1.1, // CHECK-SAME: RootElements{ // CHECK-SAME: RootFlags(AllowInputAssemblerInputLayout | DenyVertexShaderRootAccess), // CHECK-SAME: RootCBV(b0, diff --git a/clang/test/Driver/dxc_hlsl-rootsig-ver.hlsl b/clang/test/Driver/dxc_hlsl-rootsig-ver.hlsl new file mode 100644 index 000000000000..c6aa1e94096c --- /dev/null +++ b/clang/test/Driver/dxc_hlsl-rootsig-ver.hlsl @@ -0,0 +1,16 @@ +// RUN: %clang_dxc -T cs_6_0 -fcgl %s | FileCheck %s --check-prefix=CHECK-V1_1 + +// RUN: %clang_dxc -T cs_6_0 -fcgl -force-rootsig-ver rootsig_1_0 %s | FileCheck %s --check-prefix=CHECK-V1_0 +// RUN: %clang_dxc -T cs_6_0 -fcgl -force-rootsig-ver rootsig_1_1 %s | FileCheck %s --check-prefix=CHECK-V1_1 + +// Test to demonstrate that we can specify the root-signature versions + +// CHECK: !dx.rootsignatures = !{![[#EMPTY_ENTRY:]]} +// CHECK: ![[#EMPTY_ENTRY]] = !{ptr @EmptyEntry, ![[#EMPTY:]], +// CHECK-V1_0: i32 1} +// CHECK-V1_1: i32 2} +// CHECK: ![[#EMPTY]] = !{} + +[shader("compute"), RootSignature("")] +[numthreads(1,1,1)] +void EmptyEntry() {} diff --git a/llvm/include/llvm/BinaryFormat/DXContainer.h b/llvm/include/llvm/BinaryFormat/DXContainer.h index 5ac04a6c5c25..45170f550faf 100644 --- a/llvm/include/llvm/BinaryFormat/DXContainer.h +++ b/llvm/include/llvm/BinaryFormat/DXContainer.h @@ -759,6 +759,12 @@ struct DescriptorRange { } // namespace v2 } // namespace RTS0 +// D3D_ROOT_SIGNATURE_VERSION +enum class RootSignatureVersion { + V1_0 = 0x1, + V1_1 = 0x2, +}; + } // namespace dxbc } // namespace llvm