Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

joaosaffran
Copy link
Contributor

@joaosaffran joaosaffran commented Jul 8, 2025

DXC doesn't allow Textures/TypedBuffers to bind with root signature descriptors, this implements the same check.

Closes: #126647

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:DirectX HLSL HLSL Language Support labels Jul 8, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 8, 2025

@llvm/pr-subscribers-hlsl

Author: None (joaosaffran)

Changes

DXC doesn't allow Textures/TypedBuffers to bind with root signature descriptors, this implements the same check in clang.

Closes: #126647


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

2 Files Affected:

  • (added) clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl (+13)
  • (modified) llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp (+25)
diff --git a/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
new file mode 100644
index 0000000000000..9daf38e30dd67
--- /dev/null
+++ b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
@@ -0,0 +1,13 @@
+// RUN: not %clang_dxc -T cs_6_6 -E CSMain %s 2>&1 | FileCheck %s
+
+// CHECK: error: register srv (space=0, register=0) is bound to a texture or typed buffer.
+
+RWStructuredBuffer<int> Out : register(u0);
+Buffer<float> B : register(t0);
+// Compute Shader for UAV testing
+[numthreads(8, 8, 1)]
+[RootSignature("SRV(t0), UAV(u0)")]
+void CSMain(uint id : SV_GroupID)
+{
+    Out[0] = B[0];
+}
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index a52a04323514c..c34f31fbe35e5 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -85,6 +85,17 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
   }
 }
 
+static void
+reportInvalidHandleTyBoundInRs(Module &M, Twine Type,
+                               ResourceInfo::ResourceBinding Binding) {
+  SmallString<128> Message;
+  raw_svector_ostream OS(Message);
+  OS << "register " << Type << " (space=" << Binding.Space
+     << ", register=" << Binding.LowerBound << ")"
+     << " is bound to a texture or typed buffer.";
+  M.getContext().diagnose(DiagnosticInfoGeneric(Message));
+}
+
 static void reportRegNotBound(Module &M, Twine Type,
                               ResourceInfo::ResourceBinding Binding) {
   SmallString<128> Message;
@@ -163,12 +174,26 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,
       ResourceInfo::ResourceBinding Binding = SRV.getBinding();
       if (!Validation.checkTRegBinding(Binding))
         reportRegNotBound(M, "srv", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(SRV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &UAV : DRM.uavs()) {
       ResourceInfo::ResourceBinding Binding = UAV.getBinding();
       if (!Validation.checkURegBinding(Binding))
         reportRegNotBound(M, "uav", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(UAV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &Sampler : DRM.samplers()) {

@llvmbot
Copy link
Member

llvmbot commented Jul 8, 2025

@llvm/pr-subscribers-backend-directx

Author: None (joaosaffran)

Changes

DXC doesn't allow Textures/TypedBuffers to bind with root signature descriptors, this implements the same check in clang.

Closes: #126647


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

2 Files Affected:

  • (added) clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl (+13)
  • (modified) llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp (+25)
diff --git a/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
new file mode 100644
index 0000000000000..9daf38e30dd67
--- /dev/null
+++ b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
@@ -0,0 +1,13 @@
+// RUN: not %clang_dxc -T cs_6_6 -E CSMain %s 2>&1 | FileCheck %s
+
+// CHECK: error: register srv (space=0, register=0) is bound to a texture or typed buffer.
+
+RWStructuredBuffer<int> Out : register(u0);
+Buffer<float> B : register(t0);
+// Compute Shader for UAV testing
+[numthreads(8, 8, 1)]
+[RootSignature("SRV(t0), UAV(u0)")]
+void CSMain(uint id : SV_GroupID)
+{
+    Out[0] = B[0];
+}
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index a52a04323514c..c34f31fbe35e5 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -85,6 +85,17 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
   }
 }
 
+static void
+reportInvalidHandleTyBoundInRs(Module &M, Twine Type,
+                               ResourceInfo::ResourceBinding Binding) {
+  SmallString<128> Message;
+  raw_svector_ostream OS(Message);
+  OS << "register " << Type << " (space=" << Binding.Space
+     << ", register=" << Binding.LowerBound << ")"
+     << " is bound to a texture or typed buffer.";
+  M.getContext().diagnose(DiagnosticInfoGeneric(Message));
+}
+
 static void reportRegNotBound(Module &M, Twine Type,
                               ResourceInfo::ResourceBinding Binding) {
   SmallString<128> Message;
@@ -163,12 +174,26 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,
       ResourceInfo::ResourceBinding Binding = SRV.getBinding();
       if (!Validation.checkTRegBinding(Binding))
         reportRegNotBound(M, "srv", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(SRV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &UAV : DRM.uavs()) {
       ResourceInfo::ResourceBinding Binding = UAV.getBinding();
       if (!Validation.checkURegBinding(Binding))
         reportRegNotBound(M, "uav", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(UAV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &Sampler : DRM.samplers()) {

@llvmbot
Copy link
Member

llvmbot commented Jul 8, 2025

@llvm/pr-subscribers-clang

Author: None (joaosaffran)

Changes

DXC doesn't allow Textures/TypedBuffers to bind with root signature descriptors, this implements the same check in clang.

Closes: #126647


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

2 Files Affected:

  • (added) clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl (+13)
  • (modified) llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp (+25)
diff --git a/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
new file mode 100644
index 0000000000000..9daf38e30dd67
--- /dev/null
+++ b/clang/test/SemaHLSL/RootSignature-Validation-Textures.hlsl
@@ -0,0 +1,13 @@
+// RUN: not %clang_dxc -T cs_6_6 -E CSMain %s 2>&1 | FileCheck %s
+
+// CHECK: error: register srv (space=0, register=0) is bound to a texture or typed buffer.
+
+RWStructuredBuffer<int> Out : register(u0);
+Buffer<float> B : register(t0);
+// Compute Shader for UAV testing
+[numthreads(8, 8, 1)]
+[RootSignature("SRV(t0), UAV(u0)")]
+void CSMain(uint id : SV_GroupID)
+{
+    Out[0] = B[0];
+}
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index a52a04323514c..c34f31fbe35e5 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -85,6 +85,17 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
   }
 }
 
+static void
+reportInvalidHandleTyBoundInRs(Module &M, Twine Type,
+                               ResourceInfo::ResourceBinding Binding) {
+  SmallString<128> Message;
+  raw_svector_ostream OS(Message);
+  OS << "register " << Type << " (space=" << Binding.Space
+     << ", register=" << Binding.LowerBound << ")"
+     << " is bound to a texture or typed buffer.";
+  M.getContext().diagnose(DiagnosticInfoGeneric(Message));
+}
+
 static void reportRegNotBound(Module &M, Twine Type,
                               ResourceInfo::ResourceBinding Binding) {
   SmallString<128> Message;
@@ -163,12 +174,26 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,
       ResourceInfo::ResourceBinding Binding = SRV.getBinding();
       if (!Validation.checkTRegBinding(Binding))
         reportRegNotBound(M, "srv", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(SRV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &UAV : DRM.uavs()) {
       ResourceInfo::ResourceBinding Binding = UAV.getBinding();
       if (!Validation.checkURegBinding(Binding))
         reportRegNotBound(M, "uav", Binding);
+      else {
+        const auto *Handle =
+            dyn_cast_or_null<RawBufferExtType>(UAV.getHandleTy());
+
+        if (!Handle)
+          reportInvalidHandleTyBoundInRs(M, "srv", Binding);
+      }
     }
 
     for (const ResourceInfo &Sampler : DRM.samplers()) {

@joaosaffran joaosaffran marked this pull request as draft August 28, 2025 21:11
@joaosaffran joaosaffran changed the base branch from users/joaosaffran/146785 to main August 29, 2025 00:00
Comment on lines 136 to 139
std::optional<const Binding *> findBoundReg(dxil::ResourceClass RC,
uint32_t Space,
uint32_t LowerBound,
uint32_t UpperBound) const {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Formatting done to fit within 80 columns

StringRef RCName = getResourceClassName(RC);
OS << RCName << " at register " << Binding.LowerBound << " and space "
<< Binding.Space << " is bound to a texture or typed buffer. " << RCName
<< " root descriptors can only be Raw or Structured buffers.";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Last sentence added to match DXC diagnostic message

Comment on lines 258 to 268
const auto *ParamInfo =
static_cast<const mcdxbc::RootParameterInfo *>((*Reg)->Cookie);

if (RC != ResourceClass::SRV && RC != ResourceClass::UAV)
continue;

if (ParamInfo->Type == dxbc::RootParameterType::DescriptorTable)
continue;

if (RK != ResourceKind::RawBuffer && RK != ResourceKind::StructuredBuffer)
reportInvalidHandleTyError(M, RC, Binding);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using continues instead of long bool expression, as recommended in llvm coding guides: https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code

@@ -0,0 +1,17 @@
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s
; "This is a valid root signature with a texture/typed buffer resource"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is using descriptor table instead of root descriptors, that is why it is allowed, as far as my understanding of DXC goes

@@ -0,0 +1,17 @@
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1
; This is a valid root signature with a texture/typed buffer resource
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as previous valid test

@joaosaffran joaosaffran requested a review from inbelic August 30, 2025 01:26
@joaosaffran joaosaffran marked this pull request as ready for review August 30, 2025 01:26
@@ -133,17 +133,21 @@ class BoundRegs {
public:
BoundRegs(SmallVector<Binding> &&Bindings) : Bindings(std::move(Bindings)) {}

bool isBound(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
uint32_t UpperBound) const {
std::optional<const Binding *> findBoundReg(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.

std::optional<> of a pointer type is weird - pointers are already nullable. Better to just return the pointer directly and use nullptr for not found.

@@ -118,6 +118,17 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
"true, yet no overlapping binding was found");
}

static void reportInvalidHandleTyError(Module &M, ResourceClass RC,
ResourceInfo::ResourceBinding Binding) {
SmallString<128> Message;
Copy link
Contributor

Choose a reason for hiding this comment

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

The message here is always longer than 128 characters, so this SmallString is never able to use its "small" storage. We should bump this to something like 160 and avoid an unnecessary heap allocation


define void @CSMain() "hlsl.shader"="compute" {
entry:
%TB = tail call target("dx.Texture", float, 1, 0, 0, 4) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @TB.str)
Copy link
Contributor

Choose a reason for hiding this comment

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

You can skip the overload type here and let the IR parser add it implicitly

Suggested change
%TB = tail call target("dx.Texture", float, 1, 0, 0, 4) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @TB.str)
%TB = tail call target("dx.Texture", float, 1, 0, 0, 4) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr nonnull @TB.str)

Similarly in the other tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

It might be better to name this test "valid" rather than "validation" like rootsignature-valid-textures.ll. I think it's a bit clearer.

@joaosaffran joaosaffran requested a review from bogner September 10, 2025 18:48
Copy link
Contributor

Choose a reason for hiding this comment

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

Should rename this test to match the new name on rootsignature-valid-textures.ll

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, missed this one

@joaosaffran joaosaffran merged commit 9179d3f into llvm:main Sep 10, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:DirectX 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] Implement: Textures/TypedBuffers cannot be bound to root descriptors.
6 participants