[clang] a4eb0db - [HLSL][RootSignature] Add metadata generation for descriptor tables (#139633)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 15 14:54:05 PDT 2025
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
More information about the cfe-commits
mailing list