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

Reply via email to