Skip to content

[HLSL][RootSignature] Metadata generation of RootFlags, RootConstants, RootDescriptors #142010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 30, 2025

Conversation

inbelic
Copy link
Contributor

@inbelic inbelic commented May 29, 2025

Implements metadata generation of a Root Signature from its in-memory representation. It follows the same style as: #139633.

This pr handles RootFlags, RootConstants and RootDescriptors.

The metadata follows the format described here.

  • Implement BuildRoot[Flags|Constants|Descriptors] into HLSLRootSignature.h
  • Add sample testcases demonstrating functionality

Note: there is no validation of metadata nodes as the llvm::hlsl::rootsig::RootElement that generates it will have already been validated.

First part of #126586.

@llvmbot llvmbot added clang Clang issues not falling into any other category HLSL HLSL Language Support labels May 29, 2025
@llvmbot
Copy link
Member

llvmbot commented May 29, 2025

@llvm/pr-subscribers-hlsl

Author: Finn Plummer (inbelic)

Changes

Implements metadata generation of a Root Signature from its in-memory representation.

This pr handles RootFlags, RootConstants and RootDescriptors.

The metadata follows the format described here.

  • Implement BuildRoot[Flags|Constants|Descriptors] into HLSLRootSignature.h
  • Add sample testcases demonstrating functionality

Note: there is no validation of metadata nodes as the llvm::hlsl::rootsig::RootElement that generates it will have already been validated.

First part of #126586.


Full diff: https://github.com/llvm/llvm-project/pull/142010.diff

3 Files Affected:

  • (modified) clang/test/CodeGenHLSL/RootSignature.hlsl (+49-7)
  • (modified) llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h (+3)
  • (modified) llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp (+46)
diff --git a/clang/test/CodeGenHLSL/RootSignature.hlsl b/clang/test/CodeGenHLSL/RootSignature.hlsl
index 60e0dec175b8f..85f47727ce91f 100644
--- a/clang/test/CodeGenHLSL/RootSignature.hlsl
+++ b/clang/test/CodeGenHLSL/RootSignature.hlsl
@@ -1,16 +1,17 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -o - %s | FileCheck %s
 
-// CHECK: !dx.rootsignatures = !{![[#FIRST_ENTRY:]], ![[#SECOND_ENTRY:]]}
+// CHECK: !dx.rootsignatures = !{![[#EMPTY_ENTRY:]], ![[#DT_ENTRY:]],
+// CHECK-SAME: ![[#RF_ENTRY:]], ![[#RC_ENTRY:]], ![[#RD_ENTRY:]]}
 
-// CHECK: ![[#FIRST_ENTRY]] = !{ptr @FirstEntry, ![[#EMPTY:]]}
+// CHECK: ![[#EMPTY_ENTRY]] = !{ptr @EmptyEntry, ![[#EMPTY:]]}
 // CHECK: ![[#EMPTY]] = !{}
 
 [shader("compute"), RootSignature("")]
 [numthreads(1,1,1)]
-void FirstEntry() {}
+void EmptyEntry() {}
 
-// CHECK: ![[#SECOND_ENTRY]] = !{ptr @SecondEntry, ![[#SECOND_RS:]]}
-// CHECK: ![[#SECOND_RS]] = !{![[#TABLE:]]}
+// CHECK: ![[#DT_ENTRY]] = !{ptr @DescriptorTableEntry, ![[#DT_RS:]]}
+// CHECK: ![[#DT_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}
@@ -22,10 +23,51 @@ void FirstEntry() {}
   ")"
 [shader("compute"), RootSignature(SampleDescriptorTable)]
 [numthreads(1,1,1)]
-void SecondEntry() {}
+void DescriptorTableEntry() {}
+
+// CHECK: ![[#RF_ENTRY]] = !{ptr @RootFlagsEntry, ![[#RF_RS:]]}
+// CHECK: ![[#RF_RS]] = !{![[#ROOT_FLAGS:]]}
+// CHECK: ![[#ROOT_FLAGS]] = !{!"RootFlags", i32 2114}
+
+#define SampleRootFlags \
+  "RootFlags( " \
+  " Deny_Vertex_Shader_Root_Access | Allow_Stream_Output | " \
+  " sampler_heap_directly_indexed " \
+  ")"
+[shader("compute"), RootSignature(SampleRootFlags)]
+[numthreads(1,1,1)]
+void RootFlagsEntry() {}
+
+// CHECK: ![[#RC_ENTRY]] = !{ptr @RootConstantsEntry, ![[#RC_RS:]]}
+// CHECK: ![[#RC_RS]] = !{![[#ROOT_CONSTANTS:]]}
+// CHECK: ![[#ROOT_CONSTANTS]] = !{!"RootConstants", i32 5, i32 1, i32 2, i32 1}
+
+#define SampleRootConstants \
+  "RootConstants(" \
+  " space = 2, " \
+  " visibility = Shader_Visibility_Pixel, " \
+  " b1, num32BitConstants = 1 " \
+  ")"
+[shader("compute"), RootSignature(SampleRootConstants)]
+[numthreads(1,1,1)]
+void RootConstantsEntry() {}
+
+// CHECK: ![[#RD_ENTRY]] = !{ptr @RootDescriptorsEntry, ![[#RD_RS:]]}
+// CHECK: ![[#RD_RS]] = !{![[#ROOT_CBV:]], ![[#ROOT_UAV:]], ![[#ROOT_SRV:]]}
+// CHECK: ![[#ROOT_CBV]] = !{!"RootCBV", i32 0, i32 0, i32 0, i32 4}
+// CHECK: ![[#ROOT_UAV]] = !{!"RootUAV", i32 0, i32 0, i32 0, i32 2}
+// CHECK: ![[#ROOT_SRV]] = !{!"RootSRV", i32 0, i32 0, i32 0, i32 4}
+
+#define SampleRootDescriptors \
+  "CBV(b0), " \
+  "UAV(u0), " \
+  "SRV(t0)"
+[shader("compute"), RootSignature(SampleRootDescriptors)]
+[numthreads(1,1,1)]
+void RootDescriptorsEntry() {}
 
 // 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() {}
+void NoRSEntry() {}
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
index fd0abc9479469..d020dc413f0af 100644
--- a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
@@ -188,6 +188,9 @@ class MetadataBuilder {
 
 private:
   /// Define the various builders for the different metadata types
+  MDNode *BuildRootFlags(const RootFlags &Flags);
+  MDNode *BuildRootConstants(const RootConstants &Constants);
+  MDNode *BuildRootDescriptor(const RootDescriptor &Descriptor);
   MDNode *BuildDescriptorTable(const DescriptorTable &Table);
   MDNode *BuildDescriptorTableClause(const DescriptorTableClause &Clause);
 
diff --git a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
index ec0d130a6767c..cd606e61fcebf 100644
--- a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
+++ b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
@@ -171,6 +171,12 @@ void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements) {
 MDNode *MetadataBuilder::BuildRootSignature() {
   for (const RootElement &Element : Elements) {
     MDNode *ElementMD = nullptr;
+    if (const auto &Flags = std::get_if<RootFlags>(&Element))
+      ElementMD = BuildRootFlags(*Flags);
+    if (const auto &Constants = std::get_if<RootConstants>(&Element))
+      ElementMD = BuildRootConstants(*Constants);
+    if (const auto &Descriptor = std::get_if<RootDescriptor>(&Element))
+      ElementMD = BuildRootDescriptor(*Descriptor);
     if (const auto &Clause = std::get_if<DescriptorTableClause>(&Element))
       ElementMD = BuildDescriptorTableClause(*Clause);
     if (const auto &Table = std::get_if<DescriptorTable>(&Element))
@@ -187,6 +193,46 @@ MDNode *MetadataBuilder::BuildRootSignature() {
   return MDNode::get(Ctx, GeneratedMetadata);
 }
 
+MDNode *MetadataBuilder::BuildRootFlags(const RootFlags &Flags) {
+  IRBuilder<> Builder(Ctx);
+  return MDNode::get(Ctx, {
+                              MDString::get(Ctx, "RootFlags"),
+                              ConstantAsMetadata::get(
+                                  Builder.getInt32(llvm::to_underlying(Flags))),
+                          });
+}
+
+MDNode *MetadataBuilder::BuildRootConstants(const RootConstants &Constants) {
+  IRBuilder<> Builder(Ctx);
+  return MDNode::get(
+      Ctx, {
+               MDString::get(Ctx, "RootConstants"),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Constants.Visibility))),
+               ConstantAsMetadata::get(Builder.getInt32(Constants.Reg.Number)),
+               ConstantAsMetadata::get(Builder.getInt32(Constants.Space)),
+               ConstantAsMetadata::get(Builder.getInt32(
+                   Constants.Num32BitConstants)),
+           });
+}
+
+MDNode *MetadataBuilder::BuildRootDescriptor(const RootDescriptor &Descriptor) {
+  IRBuilder<> Builder(Ctx);
+  std::string Name;
+  llvm::raw_string_ostream OS(Name);
+  OS << "Root" << ClauseType(llvm::to_underlying(Descriptor.Type));
+  return MDNode::get(
+      Ctx, {
+               MDString::get(Ctx, OS.str()),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Descriptor.Visibility))),
+               ConstantAsMetadata::get(Builder.getInt32(Descriptor.Reg.Number)),
+               ConstantAsMetadata::get(Builder.getInt32(Descriptor.Space)),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Descriptor.Flags))),
+           });
+}
+
 MDNode *MetadataBuilder::BuildDescriptorTable(const DescriptorTable &Table) {
   IRBuilder<> Builder(Ctx);
   SmallVector<Metadata *> TableOperands;

@llvmbot
Copy link
Member

llvmbot commented May 29, 2025

@llvm/pr-subscribers-clang

Author: Finn Plummer (inbelic)

Changes

Implements metadata generation of a Root Signature from its in-memory representation.

This pr handles RootFlags, RootConstants and RootDescriptors.

The metadata follows the format described here.

  • Implement BuildRoot[Flags|Constants|Descriptors] into HLSLRootSignature.h
  • Add sample testcases demonstrating functionality

Note: there is no validation of metadata nodes as the llvm::hlsl::rootsig::RootElement that generates it will have already been validated.

First part of #126586.


Full diff: https://github.com/llvm/llvm-project/pull/142010.diff

3 Files Affected:

  • (modified) clang/test/CodeGenHLSL/RootSignature.hlsl (+49-7)
  • (modified) llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h (+3)
  • (modified) llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp (+46)
diff --git a/clang/test/CodeGenHLSL/RootSignature.hlsl b/clang/test/CodeGenHLSL/RootSignature.hlsl
index 60e0dec175b8f..85f47727ce91f 100644
--- a/clang/test/CodeGenHLSL/RootSignature.hlsl
+++ b/clang/test/CodeGenHLSL/RootSignature.hlsl
@@ -1,16 +1,17 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -o - %s | FileCheck %s
 
-// CHECK: !dx.rootsignatures = !{![[#FIRST_ENTRY:]], ![[#SECOND_ENTRY:]]}
+// CHECK: !dx.rootsignatures = !{![[#EMPTY_ENTRY:]], ![[#DT_ENTRY:]],
+// CHECK-SAME: ![[#RF_ENTRY:]], ![[#RC_ENTRY:]], ![[#RD_ENTRY:]]}
 
-// CHECK: ![[#FIRST_ENTRY]] = !{ptr @FirstEntry, ![[#EMPTY:]]}
+// CHECK: ![[#EMPTY_ENTRY]] = !{ptr @EmptyEntry, ![[#EMPTY:]]}
 // CHECK: ![[#EMPTY]] = !{}
 
 [shader("compute"), RootSignature("")]
 [numthreads(1,1,1)]
-void FirstEntry() {}
+void EmptyEntry() {}
 
-// CHECK: ![[#SECOND_ENTRY]] = !{ptr @SecondEntry, ![[#SECOND_RS:]]}
-// CHECK: ![[#SECOND_RS]] = !{![[#TABLE:]]}
+// CHECK: ![[#DT_ENTRY]] = !{ptr @DescriptorTableEntry, ![[#DT_RS:]]}
+// CHECK: ![[#DT_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}
@@ -22,10 +23,51 @@ void FirstEntry() {}
   ")"
 [shader("compute"), RootSignature(SampleDescriptorTable)]
 [numthreads(1,1,1)]
-void SecondEntry() {}
+void DescriptorTableEntry() {}
+
+// CHECK: ![[#RF_ENTRY]] = !{ptr @RootFlagsEntry, ![[#RF_RS:]]}
+// CHECK: ![[#RF_RS]] = !{![[#ROOT_FLAGS:]]}
+// CHECK: ![[#ROOT_FLAGS]] = !{!"RootFlags", i32 2114}
+
+#define SampleRootFlags \
+  "RootFlags( " \
+  " Deny_Vertex_Shader_Root_Access | Allow_Stream_Output | " \
+  " sampler_heap_directly_indexed " \
+  ")"
+[shader("compute"), RootSignature(SampleRootFlags)]
+[numthreads(1,1,1)]
+void RootFlagsEntry() {}
+
+// CHECK: ![[#RC_ENTRY]] = !{ptr @RootConstantsEntry, ![[#RC_RS:]]}
+// CHECK: ![[#RC_RS]] = !{![[#ROOT_CONSTANTS:]]}
+// CHECK: ![[#ROOT_CONSTANTS]] = !{!"RootConstants", i32 5, i32 1, i32 2, i32 1}
+
+#define SampleRootConstants \
+  "RootConstants(" \
+  " space = 2, " \
+  " visibility = Shader_Visibility_Pixel, " \
+  " b1, num32BitConstants = 1 " \
+  ")"
+[shader("compute"), RootSignature(SampleRootConstants)]
+[numthreads(1,1,1)]
+void RootConstantsEntry() {}
+
+// CHECK: ![[#RD_ENTRY]] = !{ptr @RootDescriptorsEntry, ![[#RD_RS:]]}
+// CHECK: ![[#RD_RS]] = !{![[#ROOT_CBV:]], ![[#ROOT_UAV:]], ![[#ROOT_SRV:]]}
+// CHECK: ![[#ROOT_CBV]] = !{!"RootCBV", i32 0, i32 0, i32 0, i32 4}
+// CHECK: ![[#ROOT_UAV]] = !{!"RootUAV", i32 0, i32 0, i32 0, i32 2}
+// CHECK: ![[#ROOT_SRV]] = !{!"RootSRV", i32 0, i32 0, i32 0, i32 4}
+
+#define SampleRootDescriptors \
+  "CBV(b0), " \
+  "UAV(u0), " \
+  "SRV(t0)"
+[shader("compute"), RootSignature(SampleRootDescriptors)]
+[numthreads(1,1,1)]
+void RootDescriptorsEntry() {}
 
 // 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() {}
+void NoRSEntry() {}
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
index fd0abc9479469..d020dc413f0af 100644
--- a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
@@ -188,6 +188,9 @@ class MetadataBuilder {
 
 private:
   /// Define the various builders for the different metadata types
+  MDNode *BuildRootFlags(const RootFlags &Flags);
+  MDNode *BuildRootConstants(const RootConstants &Constants);
+  MDNode *BuildRootDescriptor(const RootDescriptor &Descriptor);
   MDNode *BuildDescriptorTable(const DescriptorTable &Table);
   MDNode *BuildDescriptorTableClause(const DescriptorTableClause &Clause);
 
diff --git a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
index ec0d130a6767c..cd606e61fcebf 100644
--- a/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
+++ b/llvm/lib/Frontend/HLSL/HLSLRootSignature.cpp
@@ -171,6 +171,12 @@ void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements) {
 MDNode *MetadataBuilder::BuildRootSignature() {
   for (const RootElement &Element : Elements) {
     MDNode *ElementMD = nullptr;
+    if (const auto &Flags = std::get_if<RootFlags>(&Element))
+      ElementMD = BuildRootFlags(*Flags);
+    if (const auto &Constants = std::get_if<RootConstants>(&Element))
+      ElementMD = BuildRootConstants(*Constants);
+    if (const auto &Descriptor = std::get_if<RootDescriptor>(&Element))
+      ElementMD = BuildRootDescriptor(*Descriptor);
     if (const auto &Clause = std::get_if<DescriptorTableClause>(&Element))
       ElementMD = BuildDescriptorTableClause(*Clause);
     if (const auto &Table = std::get_if<DescriptorTable>(&Element))
@@ -187,6 +193,46 @@ MDNode *MetadataBuilder::BuildRootSignature() {
   return MDNode::get(Ctx, GeneratedMetadata);
 }
 
+MDNode *MetadataBuilder::BuildRootFlags(const RootFlags &Flags) {
+  IRBuilder<> Builder(Ctx);
+  return MDNode::get(Ctx, {
+                              MDString::get(Ctx, "RootFlags"),
+                              ConstantAsMetadata::get(
+                                  Builder.getInt32(llvm::to_underlying(Flags))),
+                          });
+}
+
+MDNode *MetadataBuilder::BuildRootConstants(const RootConstants &Constants) {
+  IRBuilder<> Builder(Ctx);
+  return MDNode::get(
+      Ctx, {
+               MDString::get(Ctx, "RootConstants"),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Constants.Visibility))),
+               ConstantAsMetadata::get(Builder.getInt32(Constants.Reg.Number)),
+               ConstantAsMetadata::get(Builder.getInt32(Constants.Space)),
+               ConstantAsMetadata::get(Builder.getInt32(
+                   Constants.Num32BitConstants)),
+           });
+}
+
+MDNode *MetadataBuilder::BuildRootDescriptor(const RootDescriptor &Descriptor) {
+  IRBuilder<> Builder(Ctx);
+  std::string Name;
+  llvm::raw_string_ostream OS(Name);
+  OS << "Root" << ClauseType(llvm::to_underlying(Descriptor.Type));
+  return MDNode::get(
+      Ctx, {
+               MDString::get(Ctx, OS.str()),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Descriptor.Visibility))),
+               ConstantAsMetadata::get(Builder.getInt32(Descriptor.Reg.Number)),
+               ConstantAsMetadata::get(Builder.getInt32(Descriptor.Space)),
+               ConstantAsMetadata::get(
+                   Builder.getInt32(llvm::to_underlying(Descriptor.Flags))),
+           });
+}
+
 MDNode *MetadataBuilder::BuildDescriptorTable(const DescriptorTable &Table) {
   IRBuilder<> Builder(Ctx);
   SmallVector<Metadata *> TableOperands;

@inbelic inbelic linked an issue May 29, 2025 that may be closed by this pull request
2 tasks
Copy link

github-actions bot commented May 29, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Comment on lines 221 to 223
std::string Name;
llvm::raw_string_ostream OS(Name);
OS << "Root" << ClauseType(llvm::to_underlying(Descriptor.Type));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll be slightly more efficient to use SmallString<16> and raw_svector_ostream here to avoid a heap allocation. Also I find it a bit clearer to use Name directly below instead of OS.str().

Note also that this does actually point out a bit of an inefficiency with how we've implemented only operator<< for these simple enum stringifying functions. If we had a function like getClauseTypeName returning static StringRef for these we could avoid this work entirely, and the operator<< could still exist for where it's useful and simply call this function in its implementation. It might be worth coming back to this in a follow up change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I have a note for a clean-up to do define the stringifying functions for the enums. Further, the pattern used here was propagated from descriptor tables, so I will update the other uses accordingly.

@inbelic inbelic merged commit 9cdd33d into llvm:main May 30, 2025
7 checks passed
@inbelic inbelic deleted the inbelic/rs-gen-md branch June 2, 2025 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[HLSL] Generate remaining Root Signature element Metadata from AST
4 participants