Author: Finn Plummer Date: 2025-05-15T14:54:00-07:00 New Revision: a4eb0db062b646907a2c19d54f8240fe4bdd98ce
URL: https://github.com/llvm/llvm-project/commit/a4eb0db062b646907a2c19d54f8240fe4bdd98ce DIFF: https://github.com/llvm/llvm-project/commit/a4eb0db062b646907a2c19d54f8240fe4bdd98ce.diff LOG: [HLSL][RootSignature] Add metadata generation for descriptor tables (#139633) - prereq: Modify `RootSignatureAttr` to hold a reference to the owned declaration - Define and implement `MetadataBuilder` in `HLSLRootSignature` - Integrate and invoke the builder in `CGHLSLRuntime.cpp` to generate the Root Signature for any associated entry functions - Add tests to demonstrate functionality in `RootSignature.hlsl` Resolves https://github.com/llvm/llvm-project/issues/126584 Note: this is essentially just https://github.com/llvm/llvm-project/pull/125131 rebased onto the new approach of constructing a root signature decl, instead of holding the elements in `AdditionalMembers`. Added: clang/test/CodeGenHLSL/RootSignature.hlsl Modified: clang/include/clang/Basic/Attr.td clang/lib/CodeGen/CGHLSLRuntime.cpp clang/lib/Sema/SemaHLSL.cpp llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ccd13a4cca4dd..dda62fdf03586 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4739,7 +4739,8 @@ def Error : InheritableAttr { def RootSignature : Attr { /// [RootSignature(Signature)] let Spellings = [Microsoft<"RootSignature">]; - let Args = [IdentifierArgument<"Signature">]; + let Args = [IdentifierArgument<"SignatureIdent">, + DeclArgument<HLSLRootSignature, "SignatureDecl", 0, /*fake=*/1>]; let Subjects = SubjectList<[Function], ErrorDiag, "'function'">; let LangOpts = [HLSL]; diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 0eb4bb062e02e..5bc71a9b4dd1c 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -68,6 +68,20 @@ void addDxilValVersion(StringRef ValVersionStr, llvm::Module &M) { DXILValMD->addOperand(Val); } +void addRootSignature(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(); + MDNode *FnPairing = + MDNode::get(Ctx, {ValueAsMetadata::get(Fn), RootSignature}); + + StringRef RootSignatureValKey = "dx.rootsignatures"; + auto *RootSignatureValMD = M.getOrInsertNamedMetadata(RootSignatureValKey); + RootSignatureValMD->addOperand(FnPairing); +} + } // namespace llvm::Type * @@ -423,6 +437,13 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD, // FIXME: Handle codegen for return type semantics. // See: https://github.com/llvm/llvm-project/issues/57875 B.CreateRetVoid(); + + // 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, + M); + } } void CGHLSLRuntime::setHLSLFunctionAttributes(const FunctionDecl *FD, diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 16f3986179aea..e6daa67fcee95 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -959,7 +959,7 @@ void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) { IdentifierInfo *Ident = AL.getArgAsIdent(0)->getIdentifierInfo(); if (auto *RS = D->getAttr<RootSignatureAttr>()) { - if (RS->getSignature() != Ident) { + if (RS->getSignatureIdent() != Ident) { Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << RS; return; } @@ -970,10 +970,11 @@ void SemaHLSL::handleRootSignatureAttr(Decl *D, const ParsedAttr &AL) { LookupResult R(SemaRef, Ident, SourceLocation(), Sema::LookupOrdinaryName); if (SemaRef.LookupQualifiedName(R, D->getDeclContext())) - if (isa<HLSLRootSignatureDecl>(R.getFoundDecl())) { + if (auto *SignatureDecl = + dyn_cast<HLSLRootSignatureDecl>(R.getFoundDecl())) { // Perform validation of constructs here - D->addAttr(::new (getASTContext()) - RootSignatureAttr(getASTContext(), AL, Ident)); + D->addAttr(::new (getASTContext()) RootSignatureAttr( + getASTContext(), AL, Ident, SignatureDecl)); } } diff --git a/clang/test/CodeGenHLSL/RootSignature.hlsl b/clang/test/CodeGenHLSL/RootSignature.hlsl new file mode 100644 index 0000000000000..60e0dec175b8f --- /dev/null +++ b/clang/test/CodeGenHLSL/RootSignature.hlsl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -o - %s | FileCheck %s + +// CHECK: !dx.rootsignatures = !{![[#FIRST_ENTRY:]], ![[#SECOND_ENTRY:]]} + +// CHECK: ![[#FIRST_ENTRY]] = !{ptr @FirstEntry, ![[#EMPTY:]]} +// CHECK: ![[#EMPTY]] = !{} + +[shader("compute"), RootSignature("")] +[numthreads(1,1,1)] +void FirstEntry() {} + +// CHECK: ![[#SECOND_ENTRY]] = !{ptr @SecondEntry, ![[#SECOND_RS:]]} +// CHECK: ![[#SECOND_RS]] = !{![[#TABLE:]]} +// CHECK: ![[#TABLE]] = !{!"DescriptorTable", i32 0, ![[#CBV:]], ![[#SRV:]]} +// CHECK: ![[#CBV]] = !{!"CBV", i32 1, i32 0, i32 0, i32 -1, i32 4} +// CHECK: ![[#SRV]] = !{!"SRV", i32 4, i32 42, i32 3, i32 32, i32 0} + +#define SampleDescriptorTable \ + "DescriptorTable( " \ + " CBV(b0), " \ + " SRV(t42, space = 3, offset = 32, numDescriptors = 4, flags = 0) " \ + ")" +[shader("compute"), RootSignature(SampleDescriptorTable)] +[numthreads(1,1,1)] +void SecondEntry() {} + +// Sanity test to ensure no root is added for this function as there is only +// two entries in !dx.roosignatures +[shader("compute")] +[numthreads(1,1,1)] +void ThirdEntry() {} diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h index 37f3d9ad61d3e..9fdb40db9c23d 100644 --- a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h +++ b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h @@ -20,6 +20,10 @@ #include <variant> namespace llvm { +class LLVMContext; +class MDNode; +class Metadata; + namespace hlsl { namespace rootsig { @@ -84,7 +88,9 @@ struct RootConstants { // Models the end of a descriptor table and stores its visibility struct DescriptorTable { ShaderVisibility Visibility = ShaderVisibility::All; - uint32_t NumClauses = 0; // The number of clauses in the table + // Denotes that the previous NumClauses in the RootElement array + // are the clauses in the table. + uint32_t NumClauses = 0; void dump(raw_ostream &OS) const; }; @@ -119,12 +125,47 @@ struct DescriptorTableClause { void dump(raw_ostream &OS) const; }; -// Models RootElement : RootConstants | DescriptorTable | DescriptorTableClause +/// Models RootElement : RootFlags | RootConstants | DescriptorTable +/// | DescriptorTableClause +/// +/// A Root Signature is modeled in-memory by an array of RootElements. These +/// aim to map closely to their DSL grammar reprsentation defined in the spec. +/// +/// Each optional parameter has its default value defined in the struct, and, +/// each mandatory parameter does not have a default initialization. +/// +/// For the variants RootFlags, RootConstants and DescriptorTableClause: each +/// data member maps directly to a parameter in the grammar. +/// +/// The DescriptorTable is modelled by having its Clauses as the previous +/// RootElements in the array, and it holds a data member for the Visibility +/// parameter. using RootElement = std::variant<RootFlags, RootConstants, DescriptorTable, DescriptorTableClause>; void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements); +class MetadataBuilder { +public: + MetadataBuilder(llvm::LLVMContext &Ctx, ArrayRef<RootElement> Elements) + : Ctx(Ctx), Elements(Elements) {} + + /// Iterates through the elements and dispatches onto the correct Build method + /// + /// Accumulates the root signature and returns the Metadata node that is just + /// a list of all the elements + MDNode *BuildRootSignature(); + +private: + /// Define the various builders for the diff erent metadata types + MDNode *BuildDescriptorTable(const DescriptorTable &Table); + MDNode *BuildDescriptorTableClause(const DescriptorTableClause &Clause); + + llvm::LLVMContext &Ctx; + ArrayRef<RootElement> Elements; + SmallVector<Metadata *> GeneratedMetadata; +}; + } // namespace rootsig } // namespace hlsl } // namespace llvm diff --git a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp index cd3c6f8dde8be..abf076944b273 100644 --- a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp +++ b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp @@ -12,6 +12,9 @@ #include "llvm/Frontend/HLSL/HLSLRootSignature.h" #include "llvm/ADT/bit.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" namespace llvm { namespace hlsl { @@ -160,6 +163,65 @@ void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements) { OS << "}"; } +MDNode *MetadataBuilder::BuildRootSignature() { + for (const RootElement &Element : Elements) { + MDNode *ElementMD = nullptr; + if (const auto &Clause = std::get_if<DescriptorTableClause>(&Element)) + ElementMD = BuildDescriptorTableClause(*Clause); + if (const auto &Table = std::get_if<DescriptorTable>(&Element)) + ElementMD = BuildDescriptorTable(*Table); + + // FIXME(#126586): remove once all RootElemnt variants are handled in a + // visit or otherwise + assert(ElementMD != nullptr && + "Constructed an unhandled root element type."); + + GeneratedMetadata.push_back(ElementMD); + } + + return MDNode::get(Ctx, GeneratedMetadata); +} + +MDNode *MetadataBuilder::BuildDescriptorTable(const DescriptorTable &Table) { + IRBuilder<> Builder(Ctx); + SmallVector<Metadata *> TableOperands; + // Set the mandatory arguments + TableOperands.push_back(MDString::get(Ctx, "DescriptorTable")); + TableOperands.push_back(ConstantAsMetadata::get( + Builder.getInt32(llvm::to_underlying(Table.Visibility)))); + + // Remaining operands are references to the table's clauses. The in-memory + // representation of the Root Elements created from parsing will ensure that + // the previous N elements are the clauses for this table. + assert(Table.NumClauses <= GeneratedMetadata.size() && + "Table expected all owned clauses to be generated already"); + // So, add a refence to each clause to our operands + TableOperands.append(GeneratedMetadata.end() - Table.NumClauses, + GeneratedMetadata.end()); + // Then, remove those clauses from the general list of Root Elements + GeneratedMetadata.pop_back_n(Table.NumClauses); + + return MDNode::get(Ctx, TableOperands); +} + +MDNode *MetadataBuilder::BuildDescriptorTableClause( + const DescriptorTableClause &Clause) { + IRBuilder<> Builder(Ctx); + std::string Name; + llvm::raw_string_ostream OS(Name); + OS << Clause.Type; + return MDNode::get( + Ctx, { + MDString::get(Ctx, OS.str()), + ConstantAsMetadata::get(Builder.getInt32(Clause.NumDescriptors)), + ConstantAsMetadata::get(Builder.getInt32(Clause.Reg.Number)), + ConstantAsMetadata::get(Builder.getInt32(Clause.Space)), + ConstantAsMetadata::get(Builder.getInt32(Clause.Offset)), + ConstantAsMetadata::get( + Builder.getInt32(llvm::to_underlying(Clause.Flags))), + }); +} + } // namespace rootsig } // namespace hlsl } // namespace llvm _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits