[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.
This commit is contained in:
Finn Plummer
2025-06-24 16:21:24 -07:00
committed by GitHub
parent 52fbefb281
commit e93a0d0d1e
13 changed files with 109 additions and 27 deletions

View File

@@ -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<llvm::hlsl::rootsig::RootElement> RootElements);
static HLSLRootSignatureDecl *CreateDeserialized(ASTContext &C,
GlobalDeclID ID);
llvm::dxbc::RootSignatureVersion getVersion() const { return Version; }
ArrayRef<llvm::hlsl::rootsig::RootElement> getRootElements() const {
return {getElems(), NumElems};
}

View File

@@ -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 <optional>
#include <string>
@@ -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;

View File

@@ -9312,6 +9312,20 @@ def fcgl : DXCFlag<"fcgl">, Alias<emit_pristine_llvm>;
def enable_16bit_types : DXCFlag<"enable-16bit-types">, Alias<fnative_half_type>,
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<dxc_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<LangOpts<"HLSLRootSigVer">, "V1_1">;
def dxc_rootsig_ver :
Separate<["/", "-"], "force-rootsig-ver">,
Alias<fdx_rootsignature_version>,
Group<dxc_Group>,
Visibility<[DXCOption]>;
def hlsl_entrypoint : Option<["-"], "hlsl-entry", KIND_SEPARATE>,
Group<dxc_Group>,
Visibility<[ClangOption, CC1Option]>,

View File

@@ -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<llvm::hlsl::rootsig::RootElement> RootElements) {
HLSLRootSignatureDecl *RSDecl =
new (C, DC,
additionalSizeToAlloc<llvm::hlsl::rootsig::RootElement>(
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;
}

View File

@@ -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());
}

View File

@@ -66,17 +66,16 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) {
DXILValMD->addOperand(Val);
}
void addRootSignature(ArrayRef<llvm::hlsl::rootsig::RootElement> Elements,
void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
ArrayRef<llvm::hlsl::rootsig::RootElement> 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<RootSignatureAttr>(Attr))
addRootSignature(RSAttr->getSignatureDecl()->getRootElements(), EntryFn,
if (const auto *RSAttr = dyn_cast<RootSignatureAttr>(Attr)) {
auto *RSDecl = RSAttr->getSignatureDecl();
addRootSignature(RSDecl->getVersion(), RSDecl->getRootElements(), EntryFn,
M);
}
}
}

View File

@@ -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)

View File

@@ -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") {

View File

@@ -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);

View File

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

View File

@@ -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,

View File

@@ -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() {}

View File

@@ -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