Skip to content

Conversation

joaosaffran
Copy link
Contributor

As part of the Root Signature Spec, we need to validate if Root Signatures are not defining overlapping ranges.
Closes: #126645

joaosaffran and others added 5 commits August 5, 2025 15:02
refactoring

clean up

format

formating

fix import issues

formating

refactoring

init refactoring

adding validation

clean

implementing

finish implementing && fix tests

sync parent

sync parent

address comments

fix test

fix tests

format

address changes

add preserved

addressing comments

updating

format

adding tests

clean up

address comments

adding root constants

clean

moving code arround

clean

addressing comments

address comments

update code

cleanup

address comments from inbelic
@llvmbot llvmbot added backend:DirectX llvm:analysis Includes value tracking, cost tables and constant folding labels Aug 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 6, 2025

@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-backend-directx

Author: None (joaosaffran)

Changes

As part of the Root Signature Spec, we need to validate if Root Signatures are not defining overlapping ranges.
Closes: #126645


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

11 Files Affected:

  • (modified) llvm/include/llvm/Analysis/DXILResource.h (+14)
  • (modified) llvm/lib/Analysis/DXILResource.cpp (-14)
  • (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+2)
  • (modified) llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp (+174-3)
  • (modified) llvm/test/CodeGen/DirectX/llc-pipeline.ll (+1-1)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation-constants.ll (+22)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation-fail-cbuffer-range.ll (+15)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation-fail-descriptor-table-range.ll (+15)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation-fail-root-descriptor-range.ll (+15)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation-fail-static-sampler-range.ll (+13)
  • (added) llvm/test/CodeGen/DirectX/rootsignature-validation.ll (+39)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 93c6bfb057ef5..96d631698af0a 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -33,6 +33,20 @@ class DXILResourceTypeMap;
 
 namespace dxil {
 
+inline StringRef getResourceClassName(ResourceClass RC) {
+  switch (RC) {
+  case ResourceClass::SRV:
+    return "SRV";
+  case ResourceClass::UAV:
+    return "UAV";
+  case ResourceClass::CBuffer:
+    return "CBuffer";
+  case ResourceClass::Sampler:
+    return "Sampler";
+  }
+  llvm_unreachable("Unhandled ResourceClass");
+}
+
 // Returns the resource name from dx_resource_handlefrombinding or
 // dx_resource_handlefromimplicitbinding call
 LLVM_ABI StringRef getResourceNameFromBindingCall(CallInst *CI);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 629fa7cddb9d4..b476026fcc718 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -29,20 +29,6 @@
 using namespace llvm;
 using namespace dxil;
 
-static StringRef getResourceClassName(ResourceClass RC) {
-  switch (RC) {
-  case ResourceClass::SRV:
-    return "SRV";
-  case ResourceClass::UAV:
-    return "UAV";
-  case ResourceClass::CBuffer:
-    return "CBuffer";
-  case ResourceClass::Sampler:
-    return "Sampler";
-  }
-  llvm_unreachable("Unhandled ResourceClass");
-}
-
 static StringRef getResourceKindName(ResourceKind RK) {
   switch (RK) {
   case ResourceKind::Texture1D:
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 0ec15a629d0a2..db7719e129ad0 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -9,6 +9,7 @@
 #include "DXILOpLowering.h"
 #include "DXILConstants.h"
 #include "DXILOpBuilder.h"
+#include "DXILRootSignature.h"
 #include "DXILShaderFlags.h"
 #include "DirectX.h"
 #include "llvm/ADT/SmallVector.h"
@@ -945,6 +946,7 @@ class DXILOpLoweringLegacy : public ModulePass {
     AU.addPreserved<DXILResourceWrapperPass>();
     AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
     AU.addPreserved<ShaderFlagsAnalysisWrapper>();
+    AU.addPreserved<RootSignatureAnalysisWrapper>();
   }
 };
 char DXILOpLoweringLegacy::ID = 0;
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index 398dcbb8d1737..3da4cfbea23b8 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DXILPostOptimizationValidation.h"
+#include "DXILRootSignature.h"
 #include "DXILShaderFlags.h"
 #include "DirectX.h"
 #include "llvm/ADT/SmallString.h"
@@ -24,6 +25,35 @@ using namespace llvm;
 using namespace llvm::dxil;
 
 namespace {
+static ResourceClass RangeToResourceClass(uint32_t RangeType) {
+  using namespace dxbc;
+  switch (static_cast<DescriptorRangeType>(RangeType)) {
+  case DescriptorRangeType::SRV:
+    return ResourceClass::SRV;
+  case DescriptorRangeType::UAV:
+    return ResourceClass::UAV;
+  case DescriptorRangeType::CBV:
+    return ResourceClass::CBuffer;
+  case DescriptorRangeType::Sampler:
+    return ResourceClass::Sampler;
+  }
+}
+
+ResourceClass ParameterToResourceClass(uint32_t Type) {
+  using namespace dxbc;
+  switch (Type) {
+  case llvm::to_underlying(RootParameterType::Constants32Bit):
+    return ResourceClass::CBuffer;
+  case llvm::to_underlying(RootParameterType::SRV):
+    return ResourceClass::SRV;
+  case llvm::to_underlying(RootParameterType::UAV):
+    return ResourceClass::UAV;
+  case llvm::to_underlying(RootParameterType::CBV):
+    return ResourceClass::CBuffer;
+  default:
+    llvm_unreachable("Unknown RootParameterType");
+  }
+}
 
 static void reportInvalidDirection(Module &M, DXILResourceMap &DRM) {
   for (const auto &UAV : DRM.uavs()) {
@@ -84,8 +114,122 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
   }
 }
 
+static void reportOverlappingRegisters(
+    Module &M, const llvm::hlsl::BindingInfoBuilder::Binding &Reported,
+    const llvm::hlsl::BindingInfoBuilder::Binding &Overlaping) {
+  SmallString<128> Message;
+  raw_svector_ostream OS(Message);
+  OS << "register " << getResourceClassName(Reported.RC)
+     << " (space=" << Reported.Space << ", register=" << Reported.LowerBound
+     << ")" << " is overlapping with" << " register "
+     << getResourceClassName(Overlaping.RC) << " (space=" << Overlaping.Space
+     << ", register=" << Overlaping.LowerBound << ")"
+     << ", verify your root signature definition.";
+
+  M.getContext().diagnose(DiagnosticInfoGeneric(Message));
+}
+
+static dxbc::ShaderVisibility
+tripleToVisibility(llvm::Triple::EnvironmentType ET) {
+  assert((ET == Triple::Pixel || ET == Triple::Vertex ||
+          ET == Triple::Geometry || ET == Triple::Hull ||
+          ET == Triple::Domain || ET == Triple::Mesh ||
+          ET == Triple::Compute) &&
+         "Invalid Triple to shader stage conversion");
+
+  switch (ET) {
+  case Triple::Pixel:
+    return dxbc::ShaderVisibility::Pixel;
+  case Triple::Vertex:
+    return dxbc::ShaderVisibility::Vertex;
+  case Triple::Geometry:
+    return dxbc::ShaderVisibility::Geometry;
+  case Triple::Hull:
+    return dxbc::ShaderVisibility::Hull;
+  case Triple::Domain:
+    return dxbc::ShaderVisibility::Domain;
+  case Triple::Mesh:
+    return dxbc::ShaderVisibility::Mesh;
+  case Triple::Compute:
+    return dxbc::ShaderVisibility::All;
+  default:
+    llvm_unreachable("Invalid triple to shader stage conversion");
+  }
+}
+
+static void trackRootSigDescBinding(hlsl::BindingInfoBuilder &Builder,
+                                    const mcdxbc::RootSignatureDesc &RSD,
+                                    dxbc::ShaderVisibility Visibility) {
+  for (size_t I = 0; I < RSD.ParametersContainer.size(); I++) {
+    const auto &[Type, Loc] =
+        RSD.ParametersContainer.getTypeAndLocForParameter(I);
+
+    const auto &Header = RSD.ParametersContainer.getHeader(I);
+    if (Header.ShaderVisibility !=
+            llvm::to_underlying(dxbc::ShaderVisibility::All) &&
+        Header.ShaderVisibility != llvm::to_underlying(Visibility))
+      continue;
+
+    switch (Type) {
+    case llvm::to_underlying(dxbc::RootParameterType::Constants32Bit): {
+      dxbc::RTS0::v1::RootConstants Const =
+          RSD.ParametersContainer.getConstant(Loc);
+      Builder.trackBinding(dxil::ResourceClass::CBuffer, Const.RegisterSpace,
+                           Const.ShaderRegister,
+                           Const.ShaderRegister + Const.Num32BitValues, &Const);
+      break;
+    }
+
+    case llvm::to_underlying(dxbc::RootParameterType::SRV):
+    case llvm::to_underlying(dxbc::RootParameterType::UAV):
+    case llvm::to_underlying(dxbc::RootParameterType::CBV): {
+      dxbc::RTS0::v2::RootDescriptor Desc =
+          RSD.ParametersContainer.getRootDescriptor(Loc);
+      Builder.trackBinding(ParameterToResourceClass(Type), Desc.RegisterSpace,
+                           Desc.ShaderRegister, Desc.ShaderRegister, &Desc);
+
+      break;
+    }
+    case llvm::to_underlying(dxbc::RootParameterType::DescriptorTable): {
+      const mcdxbc::DescriptorTable &Table =
+          RSD.ParametersContainer.getDescriptorTable(Loc);
+
+      for (const dxbc::RTS0::v2::DescriptorRange &Range : Table.Ranges) {
+        Builder.trackBinding(RangeToResourceClass(Range.RangeType),
+                             Range.RegisterSpace, Range.BaseShaderRegister,
+                             Range.NumDescriptors == ~0U
+                                 ? Range.NumDescriptors
+                                 : Range.BaseShaderRegister +
+                                       Range.NumDescriptors,
+                             &Range);
+      }
+      break;
+    }
+    }
+  }
+
+  for (auto &S : RSD.StaticSamplers) {
+    Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,
+                         S.ShaderRegister, S.ShaderRegister, &S);
+  }
+}
+
+std::optional<mcdxbc::RootSignatureDesc>
+getRootSignature(RootSignatureBindingInfo &RSBI,
+                 dxil::ModuleMetadataInfo &MMI) {
+  if (MMI.EntryPropertyVec.size() == 0)
+    return std::nullopt;
+  std::optional<mcdxbc::RootSignatureDesc> RootSigDesc =
+      RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
+  if (!RootSigDesc)
+    return std::nullopt;
+  return RootSigDesc;
+}
+
 static void reportErrors(Module &M, DXILResourceMap &DRM,
-                         DXILResourceBindingInfo &DRBI) {
+                         DXILResourceBindingInfo &DRBI,
+                         RootSignatureBindingInfo &RSBI,
+                         dxil::ModuleMetadataInfo &MMI) {
   if (DRM.hasInvalidCounterDirection())
     reportInvalidDirection(M, DRM);
 
@@ -94,6 +238,20 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,
 
   assert(!DRBI.hasImplicitBinding() && "implicit bindings should be handled in "
                                        "DXILResourceImplicitBinding pass");
+
+  if (auto RSD = getRootSignature(RSBI, MMI)) {
+
+    hlsl::BindingInfoBuilder Builder;
+    dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
+    trackRootSigDescBinding(Builder, *RSD, Visibility);
+    hlsl::BindingInfo Info = Builder.calculateBindingInfo(
+        [&M](const llvm::hlsl::BindingInfoBuilder &Builder,
+             const llvm::hlsl::BindingInfoBuilder::Binding &ReportedBinding) {
+          const llvm::hlsl::BindingInfoBuilder::Binding &Overlaping =
+              Builder.findOverlapping(ReportedBinding);
+          reportOverlappingRegisters(M, ReportedBinding, Overlaping);
+        });
+  }
 }
 } // namespace
 
@@ -101,7 +259,10 @@ PreservedAnalyses
 DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {
   DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
   DXILResourceBindingInfo &DRBI = MAM.getResult<DXILResourceBindingAnalysis>(M);
-  reportErrors(M, DRM, DRBI);
+  RootSignatureBindingInfo &RSBI = MAM.getResult<RootSignatureAnalysis>(M);
+  ModuleMetadataInfo &MMI = MAM.getResult<DXILMetadataAnalysis>(M);
+
+  reportErrors(M, DRM, DRBI, RSBI, MMI);
   return PreservedAnalyses::all();
 }
 
@@ -113,7 +274,12 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
         getAnalysis<DXILResourceWrapperPass>().getResourceMap();
     DXILResourceBindingInfo &DRBI =
         getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
-    reportErrors(M, DRM, DRBI);
+    RootSignatureBindingInfo &RSBI =
+        getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
+    dxil::ModuleMetadataInfo &MMI =
+        getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
+
+    reportErrors(M, DRM, DRBI, RSBI, MMI);
     return false;
   }
   StringRef getPassName() const override {
@@ -125,10 +291,13 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
   void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
     AU.addRequired<DXILResourceWrapperPass>();
     AU.addRequired<DXILResourceBindingWrapperPass>();
+    AU.addRequired<DXILMetadataAnalysisWrapperPass>();
+    AU.addRequired<RootSignatureAnalysisWrapper>();
     AU.addPreserved<DXILResourceWrapperPass>();
     AU.addPreserved<DXILResourceBindingWrapperPass>();
     AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
     AU.addPreserved<ShaderFlagsAnalysisWrapper>();
+    AU.addPreserved<RootSignatureAnalysisWrapper>();
   }
 };
 char DXILPostOptimizationValidationLegacy::ID = 0;
@@ -139,6 +308,8 @@ INITIALIZE_PASS_BEGIN(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
 INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)
 INITIALIZE_PASS_END(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
                     "DXIL Post Optimization Validation", false, false)
 
diff --git a/llvm/test/CodeGen/DirectX/llc-pipeline.ll b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
index 151603a7161c5..c31d2194a8de5 100644
--- a/llvm/test/CodeGen/DirectX/llc-pipeline.ll
+++ b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
@@ -33,9 +33,9 @@
 ; CHECK-NEXT:   DXIL Module Metadata analysis
 ; CHECK-NEXT:   DXIL Shader Flag Analysis
 ; CHECK-NEXT:   DXIL Translate Metadata
+; CHECK-NEXT:   DXIL Root Signature Analysis
 ; CHECK-NEXT:   DXIL Post Optimization Validation
 ; CHECK-NEXT:   DXIL Op Lowering
-; CHECK-NEXT:   DXIL Root Signature Analysis
 ; CHECK-NEXT:   DXIL Prepare Module
 
 ; CHECK-ASM-NEXT: DXIL Metadata Pretty Printer
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation-constants.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation-constants.ll
new file mode 100644
index 0000000000000..3c33f1dc6bdf0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation-constants.ll
@@ -0,0 +1,22 @@
+; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 
+; expected-no-diagnostics
+; Root Signature(RootConstants(num32BitConstants=4, b2))
+
+%__cblayout_CB = type <{ float }>
+
+@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
+
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+; cbuffer CB : register(b2, space0) {
+;  float a;
+; }
+  %CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, i1 false, ptr nonnull @CB.str)
+  ret void
+}
+
+!dx.rootsignatures = !{!0}
+
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2}
+!2 = !{!"RootConstants", i32 0, i32 2, i32 0, i32 4}
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-cbuffer-range.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-cbuffer-range.ll
new file mode 100644
index 0000000000000..47d2a5b8d5aab
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-cbuffer-range.ll
@@ -0,0 +1,15 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
+; CHECK: error: register CBuffer (space=0, register=2) is overlapping with register CBuffer (space=0, register=0), verify your root signature definition.
+
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+  ret void
+}
+
+; RootConstants(num32BitConstants=4, b2), DescriptorTable(CBV(b0, numDescriptors=3))
+!dx.rootsignatures = !{!0}
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2, !3}
+!2 = !{!"RootConstants", i32 0, i32 2, i32 0, i32 4}
+!3 = !{!"DescriptorTable", i32 0, !4}
+!4 = !{!"CBV", i32 3, i32 0, i32 0, i32 -1, i32 4}
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-descriptor-table-range.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-descriptor-table-range.ll
new file mode 100644
index 0000000000000..dbc75ac86db5f
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-descriptor-table-range.ll
@@ -0,0 +1,15 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
+; CHECK: error: register UAV (space=10, register=4294967295) is overlapping with register UAV (space=10, register=4294967295), verify your root signature definition.
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+  ret void
+}
+
+; DescriptorTable(UAV(u0, numDescriptors=unbounded), visibility = SHADER_VISIBILITY_HULL), DescriptorTable(UAV(u2, numDescriptors=4))
+!dx.rootsignatures = !{!0}
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2, !4}
+!2 = !{!"DescriptorTable", i32 0, !3}
+!3 = !{!"UAV", i32 -1, i32 -1, i32 10, i32 -1, i32 2}
+!4 = !{!"DescriptorTable", i32 0, !5}
+!5 = !{ !"UAV", i32 -1, i32 -1, i32 10, i32 5, i32 2 }
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-root-descriptor-range.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-root-descriptor-range.ll
new file mode 100644
index 0000000000000..9f70cc3f062a9
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-root-descriptor-range.ll
@@ -0,0 +1,15 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
+; CHECK: error: register UAV (space=1, register=3) is overlapping with register UAV (space=1, register=0), verify your root signature definition.
+
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+  ret void
+}
+
+; DescriptorTable(UAV(u0, numDescriptors=unbounded), visibility = SHADER_VISIBILITY_HULL), UAV(u3, space=1)
+!dx.rootsignatures = !{!0}
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2, !4}
+!2 = !{!"RootUAV", i32 0, i32 3, i32 1, i32 4}
+!4 = !{!"DescriptorTable", i32 0, !5}
+!5 = !{!"UAV", i32 3, i32 0, i32 1, i32 -1, i32 2}
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-static-sampler-range.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-static-sampler-range.ll
new file mode 100644
index 0000000000000..340b0aed6d16b
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation-fail-static-sampler-range.ll
@@ -0,0 +1,13 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
+; CHECK: error: register Sampler (space=0, register=42) is overlapping with register Sampler (space=0, register=42), verify your root signature definition.
+
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+  ret void
+}
+
+!dx.rootsignatures = !{!0}
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2, !3}
+!2 = !{ !"StaticSampler", i32 5, i32 4, i32 5, i32 3, float 0x3FF7CCCCC0000000, i32 10, i32 2, i32 1, float -1.270000e+02, float 1.220000e+02, i32 42, i32 0, i32 0 }
+!3 = !{ !"StaticSampler", i32 4, i32 2, i32 3, i32 5, float 0x3FF6CCCCC0000000, i32 9, i32 3, i32 2, float -1.280000e+02, float 1.280000e+02, i32 42, i32 0, i32 0 }
diff --git a/llvm/test/CodeGen/DirectX/rootsignature-validation.ll b/llvm/test/CodeGen/DirectX/rootsignature-validation.ll
new file mode 100644
index 0000000000000..750679bf743c5
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/rootsignature-validation.ll
@@ -0,0 +1,39 @@
+; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 
+; expected-no-diagnostics
+
+
+; Root Signature(
+;   CBV(b3, space=1, visibility=SHADER_VISIBILITY_ALL)
+;   DescriptorTable(SRV(t0, space=0, numDescriptors=1), visibility=SHADER_VISIBILITY_ALL)
+;   DescriptorTable(Sampler(s0, numDescriptors=2), visibility=SHADER_VISIBILITY_VERTEX)
+;   DescriptorTable(UAV(u0, numDescriptors=unbounded), visibility=SHADER_VISIBILITY_ALL)
+
+%__cblayout_CB = type <{ float }>
+
+@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
+@Smp.str = private unnamed_addr constant [4 x i8] c"Smp\00", align 1
+@SB.str = private unnamed_addr constant [3 x i8] c"SB\00", align 1
+@RWB.str = private unnamed_addr constant [4 x i8] c"RWB\00", align 1
+
+define void @CSMain() "hlsl.shader"="compute" {
+entry:
+
+  %CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 1, i32 3, i32 1, i32 0, i1 false, ptr nonnull @CB.str)
+  %Sampler = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 3, i32 1, i32 0, i1 false, ptr nonnull @Smp.str)
+  %SB = tail call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false, ptr nonnull @SB.str)
+  %RWB =  tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false, ptr nonnull @RWB.str)
+
+  ret void
+}
+
+!dx.rootsignatures = !{!0}
+
+!0 = !{ptr @CSMain, !1, i32 2}
+!1 = !{!2, !3, !5, !7}
+!2 = !{!"RootCBV", i32 0, i32 3, i32 1, i32 4}
+!3 = !{!"DescriptorTable", i32 0, !4}
+!4 = !{!"SRV", i32 1, i32 0, i32 0, i32 -1, i32 0}
+!5 = !{!"DescriptorTable", i32 0, !6}
+!6 = !{!"Sampler", i32 5, i32 3, i32 2, i32 -1, i32 0}
+!7 = !{!"DescriptorTable", i32 0, !8}
+!8 = !{!"UAV", i32 -1, i32 0, i32 0, i32 -1, i32 2}

Copy link

github-actions bot commented Aug 6, 2025

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
Please turn off Keep my email addresses private setting in your account.
See LLVM Developer Policy and LLVM Discourse for more information.

@inbelic inbelic self-requested a review August 6, 2025 15:26
Comment on lines 202 to 203
: Range.BaseShaderRegister +
Range.NumDescriptors,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
: Range.BaseShaderRegister +
Range.NumDescriptors,
: Range.BaseShaderRegister +
Range.NumDescriptors - 1,

I believe this is an inclusive range. We should add a test case for this case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably more readable to introduce a UpperBound variable rather than bury the ternary operator in the function call as well.

RSD.ParametersContainer.getConstant(Loc);
Builder.trackBinding(dxil::ResourceClass::CBuffer, Const.RegisterSpace,
Const.ShaderRegister,
Const.ShaderRegister + Const.Num32BitValues, &Const);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Const.ShaderRegister + Const.Num32BitValues, &Const);
Const.ShaderRegister, &Const);

This is what we do in the frontend, and iirc, I did check the behaviour with DXC. In either case, can you double-check what the correct behaviour is. But I believe it will always have a size of 1.

Comment on lines 211 to 214
for (auto &S : RSD.StaticSamplers) {
Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,
S.ShaderRegister, S.ShaderRegister, &S);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Suggested change
for (auto &S : RSD.StaticSamplers) {
Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,
S.ShaderRegister, S.ShaderRegister, &S);
}
for (auto &S : RSD.StaticSamplers)
Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,
S.ShaderRegister, S.ShaderRegister, &S);

Copy link
Contributor

@bogner bogner left a comment

Choose a reason for hiding this comment

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

We probably need some more tests here, but it looks like it's moving in the right direction.

Please run the offload-test-suite for this kind of change - trying it locally I see a lot of failures. Most of them look like they're the same issue as #152250, but even after cherry picking that change there are a number of failures.

AU.addPreserved<DXILResourceWrapperPass>();
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
AU.addPreserved<ShaderFlagsAnalysisWrapper>();
AU.addPreserved<RootSignatureAnalysisWrapper>();
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll need to preserve the root signature analysis in the new-PM version of the pass as well (that is, in DXILOpLowering::run)


namespace dxil {

inline StringRef getResourceClassName(ResourceClass RC) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't an appropriate place for this function - the enum is defined in Support/DXILABI.h and this is just an arbitrary user of that that happens to also be a dependency of where you're using this functionality. Also, it's not a "great" practice to have a bunch of string literals in an inline function in a header - these will end up as duplicate data in every translation unit and we have to rely on the linker to clean it all up later.

I guess it would probably be best to add a DXILABI.cpp in Support and out-of-line the resource class enum stringify-ing function there.

Comment on lines 28 to 30
static ResourceClass RangeToResourceClass(uint32_t RangeType) {
using namespace dxbc;
switch (static_cast<DescriptorRangeType>(RangeType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's weird for this function to take a uint32_t, and this function isn't following llvm naming conventions. We can solve both of these by creating a strongly typed conversion function that can be overloaded, like static ResourceClass toResourceClass(dxbc::DescriptorRangeType).

using namespace llvm;
using namespace llvm::dxil;

namespace {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is already there, but static functions don't need to be in an anonymous namespace (only classes and structs do). I guess we could remove this particular anonymous namespace completely.

}
}

ResourceClass ParameterToResourceClass(uint32_t Type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly, toResourceClass(dxbc::RootParameterType) would be better.

Comment on lines 173 to 174
switch (Type) {
case llvm::to_underlying(dxbc::RootParameterType::Constants32Bit): {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly here - Using the underlying type for the switch is needlessly confusing and defeats covered enum warnings. Why not:

    dxbc::RootParameterType ParamType =
        static_cast<dxbc::RootParameterType>(ParamInfo.Header.ParameterType);
    switch (ParamType) {
    case dxbc::RootParameterType::Constants32Bit: {
    // ...

RSD.ParametersContainer.getConstant(Loc);
Builder.trackBinding(dxil::ResourceClass::CBuffer, Const.RegisterSpace,
Const.ShaderRegister,
Const.ShaderRegister + Const.Num32BitValues, &Const);
Copy link
Contributor

Choose a reason for hiding this comment

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

&Const is a reference to a local here - this is undefined behaviour. If we need a cookie to deduplicate these bindings we'll need something that has a sufficient lifetime. Same with the following calls to trackBinding

Comment on lines 202 to 203
: Range.BaseShaderRegister +
Range.NumDescriptors,
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably more readable to introduce a UpperBound variable rather than bury the ternary operator in the function call as well.

}
}

for (auto &S : RSD.StaticSamplers) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's best to only use auto when the type is blatantly obvious like a cast, or sometimes in cases like iterators where the type name is long enough to obscure what's happening. Please spell out the type here. Also, should this be const?

Comment on lines 222 to 223
std::optional<mcdxbc::RootSignatureDesc> RootSigDesc =
RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit surprised / worried that this API is returning a std::optional<mcdxbc::RootSignatureDesc> and not a std::optional<mcdxbc::RootSignatureDesc &> - the root signature description object doesn't look cheap to copy (it has multiple vectors in it!) and here we are copying it twice...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

std::optional don't seem to support reference, used a pointer instead.

const unsigned MinWaveSize = 4;
const unsigned MaxWaveSize = 128;

StringRef getResourceClassName(ResourceClass RC);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to use the recently landed function defined here in tangent with enumToStringRef: #152213

Copy link
Contributor

Choose a reason for hiding this comment

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

Bump

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check llvm/lib/Support/DXILABI.cpp, getResourceClassName calls enumToStringRef

Copy link
Contributor

@inbelic inbelic Aug 12, 2025

Choose a reason for hiding this comment

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

Ah okay, I think we could probably just do that inline? Then we don't need to define DXILABI.cpp at all, since this file is defined to just contain "definitions of constants and enums" to be stable in the DXIL

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please check #152229 (comment), bogner explains why we created DXILABI.cpp.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, in that case I think we need to move the getResourceClasses from here to DXILABI. Otherwise, we have a circular dependency between DXILABI and BinaryFormat/DXContainer.h.

Either that or inline this function where it is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The circular dependency shouldn't be an issue, since we use the #ifndef in all files.

I will fix this, but in a follow-up PR, since this change is already out of scope for this patch.

Copy link
Contributor Author

@joaosaffran joaosaffran Aug 13, 2025

Choose a reason for hiding this comment

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

Here is the PR fixing it: #153490

@@ -0,0 +1,14 @@
; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
; CHECK: error: register UAV (space=1, register=3) is overlapping with register UAV (space=1, register=0), verify your root signature definition.
Copy link
Contributor

Choose a reason for hiding this comment

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

This error message would seemingly indicate that they shouldn't have overlapped? Maybe something like:
resource UAV(space=1, registers=[3,3]) is overlapping with resource UAV (space=1,registers=[0,3]), ...?

@joaosaffran joaosaffran requested a review from inbelic August 12, 2025 17:35
Copy link

github-actions bot commented Aug 12, 2025

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

const unsigned MinWaveSize = 4;
const unsigned MaxWaveSize = 128;

StringRef getResourceClassName(ResourceClass RC);
Copy link
Contributor

Choose a reason for hiding this comment

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

Bump

dxil::ModuleMetadataInfo &MMI) {
if (MMI.EntryPropertyVec.size() == 0)
return nullptr;
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
assert(MMI.EntryPropertyVec.size() == 1 && ...)
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);

Is that true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, there are a lot of tests that compile without an entry point, mainly scenarios for libraries.

Copy link
Contributor

Choose a reason for hiding this comment

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

But if it isn't 0, is it then always 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, HLSL can have multiple entry point, for multiple shader stages.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case, what makes it always be the first one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For non library shader compilation, only one entry point is expected. If we have more than one entry point, we are compiling a library shader. Library don't compile root signature to dxcontainer.

Copy link
Contributor

Choose a reason for hiding this comment

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

We really need to clean up the places that make this assumption and grab the first entry of the entry point vector explicitly, either by wrapping it with an API with an explicit name or by more explicitly handling library profiles. That said, this is a pretty ubiquitous problem already I don't think we need to fix it in this PR.

@@ -0,0 +1,14 @@

Copy link
Contributor

Choose a reason for hiding this comment

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

This needs a file header.


#include "llvm/Support/DXILABI.h"
#include "llvm/BinaryFormat/DXContainer.h"
#include "llvm/Support/ErrorHandling.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is used

Comment on lines 8 to 10
namespace llvm {
namespace dxil {
StringRef getResourceClassName(dxil::ResourceClass RC) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please avoid namespace blocks like this in the cpp file and just qualify the names:

StringRef dxil::getResourceClassName(dxil::ResourceClass RC) {

Some rationale here: https://llvm.org/docs/CodingStandards.html#use-namespace-qualifiers-to-implement-previously-declared-functions

return enumToStringRef(RC, dxbc::getResourceClasses());
}
} // namespace dxil
} // namespace llvm No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing newline at the end of the file.

case DescriptorRangeType::Sampler:
return ResourceClass::Sampler;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll need an llvm_unreachable() outside of the switch block or MSVC will get upset that not all control paths return a value.

case dxbc::RootParameterType::DescriptorTable:
break;
}
llvm_unreachable("Unconvertible RootParameterType");
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be better to have a different llvm_unreachable() with a message like "DescriptorTable is not convertible to ResourceClass" in the DescriptorTable case, and then this one is truly "Unknown RootParameterType"? That case could be hit because of programmer-error (we call this function without satisfying its invariants) whereas a value outside of the enum range should be truly unreachable.


hlsl::BindingInfoBuilder Builder;
dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
SmallVector<char> IDs;
Copy link
Contributor

Choose a reason for hiding this comment

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

Are duplicates possible? Do we need to unique them at all? If not, then we probably don't need IDs at all - we can just pass nullptr as the cookie if we're not going to do anything with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Duplicates are possible, if we have a root signature like: SRV(t0), DescriptorTable(SRV(t0)).

Copy link
Contributor

Choose a reason for hiding this comment

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

Is using the IDs vector more standard than just &ParamInfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if there is a standard, since this isn't used in a lot of places. But I tried to follow a similar pattern as this test:

Copy link
Contributor

Choose a reason for hiding this comment

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

Using ParamInfo would allow you to access &ParamInfo in the error reporting function for more detail, which is useful in general though it looks like you didn't need it.

However, there are two reasons just using &ParamInfo would still be better:

  • It's already a unique ID with sufficient lifetime, so adding a separate object just to use its address seems inefficient
  • Using a SmallVector<char> here that we fill as we go is unsafe/incorrect - if the vector is resized (because it gets too big for the current storage) the addresses of the objects in it will be invalidated, so we'll be comparing invalid pointers and hitting UB here.

dxil::ModuleMetadataInfo &MMI) {
if (MMI.EntryPropertyVec.size() == 0)
return nullptr;
return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);
Copy link
Contributor

Choose a reason for hiding this comment

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

We really need to clean up the places that make this assumption and grab the first entry of the entry point vector explicitly, either by wrapping it with an API with an explicit name or by more explicitly handling library profiles. That said, this is a pretty ubiquitous problem already I don't think we need to fix it in this PR.

@joaosaffran joaosaffran requested review from bogner and inbelic August 13, 2025 18:33
Copy link
Contributor

@inbelic inbelic left a comment

Choose a reason for hiding this comment

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

LGTM, just a collection of nits.

Comment on lines 1 to 2
//===-- DXILABI.cpp - ABI Sensitive Values for DXIL ---------------*- C++
//-*-===//
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: formatting


hlsl::BindingInfoBuilder Builder;
dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
SmallVector<char> IDs;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is using the IDs vector more standard than just &ParamInfo?

!1 = !{!2, !3}
!2 = !{ !"StaticSampler", i32 5, i32 4, i32 5, i32 3, float 0x3FF7CCCCC0000000, i32 10, i32 2, i32 1, float -1.270000e+02, float 1.220000e+02, i32 42, i32 0, i32 0 }
!3 = !{!"DescriptorTable", i32 0, !4}
!4 = !{!"Sampler", i32 1, i32 42, i32 0, i32 -1, i32 0} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: file ending

const unsigned MinWaveSize = 4;
const unsigned MaxWaveSize = 128;

StringRef getResourceClassName(ResourceClass RC);
Copy link
Contributor

Choose a reason for hiding this comment

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

I see, in that case I think we need to move the getResourceClasses from here to DXILABI. Otherwise, we have a circular dependency between DXILABI and BinaryFormat/DXContainer.h.

Either that or inline this function where it is used.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this really adds much value by itself, maybe we can move it to the rootsignature-validation.ll to show it doesn't overlap with a CBV range there.

@@ -0,0 +1,20 @@
//===-- DXILABI.cpp - ABI Sensitive Values for DXIL --------------*- C++-*-===//
Copy link
Contributor

Choose a reason for hiding this comment

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

The *- C++ -* tag isn't necessary in .cpp files, and in fact, it looks like we've dropped the recommendation to put it in these headers at all a little while ago: e2c3d16

Comment on lines 9 to 10
// This file implements functions that can be reused accross different stages
// dxil generation.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence isn't very clear. Why not use the description from DXILABI.h here instead?

//
//===----------------------------------------------------------------------===//

#include "llvm/BinaryFormat/DXContainer.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Support can't depend on BinaryFormat

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you removed the actual dependency but forgot to remove the include here

return ResourceClass::CBuffer;
case dxbc::RootParameterType::DescriptorTable:
llvm_unreachable("DescriptorTable is not convertible to ResourceClass");
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

break isn't necessary after unreachable


hlsl::BindingInfoBuilder Builder;
dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
SmallVector<char> IDs;
Copy link
Contributor

Choose a reason for hiding this comment

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

Using ParamInfo would allow you to access &ParamInfo in the error reporting function for more detail, which is useful in general though it looks like you didn't need it.

However, there are two reasons just using &ParamInfo would still be better:

  • It's already a unique ID with sufficient lifetime, so adding a separate object just to use its address seems inefficient
  • Using a SmallVector<char> here that we fill as we go is unsafe/incorrect - if the vector is resized (because it gets too big for the current storage) the addresses of the objects in it will be invalidated, so we'll be comparing invalid pointers and hitting UB here.

@@ -0,0 +1,28 @@
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1
Copy link
Contributor

Choose a reason for hiding this comment

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

Why pipe stderr to stdout here? The 2>&1 is more likely to make things more confusing than help.

@joaosaffran joaosaffran requested review from bogner and inbelic August 14, 2025 18:37
Copy link
Contributor

@bogner bogner left a comment

Choose a reason for hiding this comment

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

Looks good. A couple of small comments left.

Before committing, could you make sure that the way we comment/document the root signatures in the tests are consistent? Most have the signature in a comment just before the !dx.rootsignatures metadata, but some like llvm/test/CodeGen/DirectX/rootsignature-validation-fail-root-descriptor-range.ll are missing the comment and llvm/test/CodeGen/DirectX/rootsignature-validation.ll has the comment but it's in a different spot in the file

const unsigned MinWaveSize = 4;
const unsigned MaxWaveSize = 128;

LLVM_ABI ArrayRef<EnumEntry<llvm::dxil::ResourceClass>> getResourceClasses();
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to fully qualify this

Suggested change
LLVM_ABI ArrayRef<EnumEntry<llvm::dxil::ResourceClass>> getResourceClasses();
LLVM_ABI ArrayRef<EnumEntry<ResourceClass>> getResourceClasses();

//
//===----------------------------------------------------------------------===//

#include "llvm/BinaryFormat/DXContainer.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you removed the actual dependency but forgot to remove the include here

@@ -0,0 +1,28 @@
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s
; expected-no-diagnostics
Copy link
Contributor

Choose a reason for hiding this comment

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

The expected-no-diagnostics syntax is for clang -verify. Might be clearer to just write it out in a sentence like "We have a valid root signature, this should compile successfully"

@joaosaffran joaosaffran merged commit d56fa96 into llvm:main Aug 14, 2025
10 checks passed
#define LLVM_SUPPORT_DXILABI_H

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ScopedPrinter.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

This PR makes the clang build 2% slower, probably by adding these includes. ScopedPrinter.h is very heavy, and DXILABI.h is included everywhere in clang. Please refactor things to remove these includes.

See https://llvm-compile-time-tracker.com/compare_clang.php?from=ca9ddd54b77020322a94a8032276b46cc1046522&to=d56fa965243bcdc115b8f262ce79cf2547500c6d&stat=instructions%3Au&sortBy=interestingness.

Copy link
Contributor Author

@joaosaffran joaosaffran Aug 15, 2025

Choose a reason for hiding this comment

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

I've filed an issue #153827, to keep track of this. Will fix it as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:DirectX llvm:analysis Includes value tracking, cost tables and constant folding llvm:support