Skip to content

[DXIL] Remove incompatible metadata types when preparing DXIL. #136386

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 8 commits into from
Apr 28, 2025

Conversation

bob80905
Copy link
Contributor

@bob80905 bob80905 commented Apr 18, 2025

This PR introduces a Metadata Node Kind allowlist. The purpose is to prevent newer Metadata Node Kinds to be used and inserted into the outputted DXIL module. Only the metadata kinds that are accepted in the DXIL Validator are on the allowlist. The Github DXC validator doesn't support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata that isn't found in the list.

The accompanying test would add the llvm.loop.mustprogress metadata node kind, but thanks to the allowlist, filters it out, and so the whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist, and remain after the DXIL Prepare pass.

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

llvmbot commented Apr 18, 2025

@llvm/pr-subscribers-hlsl

@llvm/pr-subscribers-backend-directx

Author: Joshua Batista (bob80905)

Changes

This PR introduces a Metadata Node Kind whitelist. The purpose is to prevent newer Metadata Node Kinds to be used and inserted into the outputted DXIL module. The Github DXC validator doesn't support these newer Metadata Node Kinds, so we need to filter them out.
In Github DXC, lib\HLSL\DxilPreparePasses.cpp:814, there is a loop:

for (unsigned I = LLVMContext::MD_fpmath + 1;
     I <= LLVMContext::MD_dereferenceable_or_null; ++I) {
  IllegalMDSet.insert(I);
}

that loops up to and including the last metadata node kind that should be disallowed in the current context. I believe this code shows that there are no other metadata nodes to be allowed, setting aside the explicit list shown in lib\IR\LLVMContext.cpp:34
So, given that this is the world of acceptable metadata node kinds, we introduce this restrictive whitelist into LLVM and strip all metadata that isn't found in the list.

The accompanying test would add the loop.llvm.mustprogress metadata node kind, but thanks to the whitelist, filters it out, and so the whitelist is proven to work.


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

2 Files Affected:

  • (added) clang/test/CodeGenHLSL/metadata-stripping.hlsl (+13)
  • (modified) llvm/lib/Target/DirectX/DXILPrepare.cpp (+20)
diff --git a/clang/test/CodeGenHLSL/metadata-stripping.hlsl b/clang/test/CodeGenHLSL/metadata-stripping.hlsl
new file mode 100644
index 0000000000000..0178f5efe4c01
--- /dev/null
+++ b/clang/test/CodeGenHLSL/metadata-stripping.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang --driver-mode=dxc -T cs_6_0 -Fo x.dxil %s | FileCheck %s
+// CHECK-NOT: llvm.loop.mustprogress
+
+StructuredBuffer<uint4> X : register(t0);
+StructuredBuffer<float4> In : register(t1);
+RWStructuredBuffer<float4> Out : register(u0);
+
+[numthreads(1, 1, 1)]
+void main(uint3 dispatch_thread_id : SV_DispatchThreadID) {
+  for (uint I = 0; I < X[dispatch_thread_id].x; ++I) {
+    Out[dispatch_thread_id] = In[dispatch_thread_id];
+  }
+}
diff --git a/llvm/lib/Target/DirectX/DXILPrepare.cpp b/llvm/lib/Target/DirectX/DXILPrepare.cpp
index de97de209048b..c54a5ac5c1227 100644
--- a/llvm/lib/Target/DirectX/DXILPrepare.cpp
+++ b/llvm/lib/Target/DirectX/DXILPrepare.cpp
@@ -189,6 +189,26 @@ class DXILPrepareModule : public ModulePass {
       for (auto &BB : F) {
         IRBuilder<> Builder(&BB);
         for (auto &I : make_early_inc_range(BB)) {
+
+          // TODO: Audit this list - is it enough? Too much?
+          static unsigned DXILCompatibleMDs[] = {
+              LLVMContext::MD_dbg,
+              LLVMContext::MD_tbaa,
+              LLVMContext::MD_prof,
+              LLVMContext::MD_fpmath,
+              LLVMContext::MD_range,
+              LLVMContext::MD_tbaa_struct,
+              LLVMContext::MD_invariant_load,
+              LLVMContext::MD_alias_scope,
+              LLVMContext::MD_noalias,
+              LLVMContext::MD_nontemporal,
+              LLVMContext::MD_mem_parallel_loop_access,
+              LLVMContext::MD_nonnull,
+              LLVMContext::MD_dereferenceable,
+              LLVMContext::MD_dereferenceable_or_null,
+          };
+          I.dropUnknownNonDebugMetadata(DXILCompatibleMDs);
+
           if (I.getOpcode() == Instruction::FNeg) {
             Builder.SetInsertPoint(&I);
             Value *In = I.getOperand(0);

@llvmbot
Copy link
Member

llvmbot commented Apr 18, 2025

@llvm/pr-subscribers-clang

Author: Joshua Batista (bob80905)

Changes

This PR introduces a Metadata Node Kind whitelist. The purpose is to prevent newer Metadata Node Kinds to be used and inserted into the outputted DXIL module. The Github DXC validator doesn't support these newer Metadata Node Kinds, so we need to filter them out.
In Github DXC, lib\HLSL\DxilPreparePasses.cpp:814, there is a loop:

for (unsigned I = LLVMContext::MD_fpmath + 1;
     I &lt;= LLVMContext::MD_dereferenceable_or_null; ++I) {
  IllegalMDSet.insert(I);
}

that loops up to and including the last metadata node kind that should be disallowed in the current context. I believe this code shows that there are no other metadata nodes to be allowed, setting aside the explicit list shown in lib\IR\LLVMContext.cpp:34
So, given that this is the world of acceptable metadata node kinds, we introduce this restrictive whitelist into LLVM and strip all metadata that isn't found in the list.

The accompanying test would add the loop.llvm.mustprogress metadata node kind, but thanks to the whitelist, filters it out, and so the whitelist is proven to work.


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

2 Files Affected:

  • (added) clang/test/CodeGenHLSL/metadata-stripping.hlsl (+13)
  • (modified) llvm/lib/Target/DirectX/DXILPrepare.cpp (+20)
diff --git a/clang/test/CodeGenHLSL/metadata-stripping.hlsl b/clang/test/CodeGenHLSL/metadata-stripping.hlsl
new file mode 100644
index 0000000000000..0178f5efe4c01
--- /dev/null
+++ b/clang/test/CodeGenHLSL/metadata-stripping.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang --driver-mode=dxc -T cs_6_0 -Fo x.dxil %s | FileCheck %s
+// CHECK-NOT: llvm.loop.mustprogress
+
+StructuredBuffer<uint4> X : register(t0);
+StructuredBuffer<float4> In : register(t1);
+RWStructuredBuffer<float4> Out : register(u0);
+
+[numthreads(1, 1, 1)]
+void main(uint3 dispatch_thread_id : SV_DispatchThreadID) {
+  for (uint I = 0; I < X[dispatch_thread_id].x; ++I) {
+    Out[dispatch_thread_id] = In[dispatch_thread_id];
+  }
+}
diff --git a/llvm/lib/Target/DirectX/DXILPrepare.cpp b/llvm/lib/Target/DirectX/DXILPrepare.cpp
index de97de209048b..c54a5ac5c1227 100644
--- a/llvm/lib/Target/DirectX/DXILPrepare.cpp
+++ b/llvm/lib/Target/DirectX/DXILPrepare.cpp
@@ -189,6 +189,26 @@ class DXILPrepareModule : public ModulePass {
       for (auto &BB : F) {
         IRBuilder<> Builder(&BB);
         for (auto &I : make_early_inc_range(BB)) {
+
+          // TODO: Audit this list - is it enough? Too much?
+          static unsigned DXILCompatibleMDs[] = {
+              LLVMContext::MD_dbg,
+              LLVMContext::MD_tbaa,
+              LLVMContext::MD_prof,
+              LLVMContext::MD_fpmath,
+              LLVMContext::MD_range,
+              LLVMContext::MD_tbaa_struct,
+              LLVMContext::MD_invariant_load,
+              LLVMContext::MD_alias_scope,
+              LLVMContext::MD_noalias,
+              LLVMContext::MD_nontemporal,
+              LLVMContext::MD_mem_parallel_loop_access,
+              LLVMContext::MD_nonnull,
+              LLVMContext::MD_dereferenceable,
+              LLVMContext::MD_dereferenceable_or_null,
+          };
+          I.dropUnknownNonDebugMetadata(DXILCompatibleMDs);
+
           if (I.getOpcode() == Instruction::FNeg) {
             Builder.SetInsertPoint(&I);
             Value *In = I.getOperand(0);

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.

This looks pretty good, but the test needs to also cover the positive cases. We should show that dx.nonuniform and MD_range, for example, are not removed. This can probably be done in the same test file unless you think it's worthwhile to split it up.

@bob80905
Copy link
Contributor Author

Created a new issue to improve range metadata validation here:
#137407

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.

Description / commit message needs to be updated to match how this ended up, but otherwise this is looking pretty good. A couple of minor comments inline.

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

@@ -189,6 +201,9 @@ class DXILPrepareModule : public ModulePass {
for (auto &BB : F) {
IRBuilder<> Builder(&BB);
for (auto &I : make_early_inc_range(BB)) {

I.dropUnknownNonDebugMetadata(DXILCompatibleMDs);

if (I.getOpcode() == Instruction::FNeg) {
Copy link
Member

Choose a reason for hiding this comment

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

DXILPrepare has a bunch of overlap with the legalization pass. Seems like MetaData might not be able to move because legalization is a function level pass and this is a module level one, but at a minimum it seems like FNeg -> FSub should move.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree. I think DXILPrepare should do things like translate attributes and metadata, insert no-op bitcasts to appease the bitcode writer, and that sort of thing. Replacing ops with their equivalents should squarely be a part of legalization, not here.

I've filed #137685

@bob80905 bob80905 merged commit c8b3d79 into llvm:main Apr 28, 2025
12 checks passed
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…136386)

This PR introduces a Metadata Node Kind allowlist. The purpose is to
prevent newer Metadata Node Kinds to be used and inserted into the
outputted DXIL module. Only the metadata kinds that are accepted in the
DXIL Validator are on the allowlist. The Github DXC validator doesn't
support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata
that isn't found in the list.

The accompanying test would add the `llvm.loop.mustprogress` metadata
node kind, but thanks to the allowlist, filters it out, and so the
whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist,
and remain after the DXIL Prepare pass.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…136386)

This PR introduces a Metadata Node Kind allowlist. The purpose is to
prevent newer Metadata Node Kinds to be used and inserted into the
outputted DXIL module. Only the metadata kinds that are accepted in the
DXIL Validator are on the allowlist. The Github DXC validator doesn't
support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata
that isn't found in the list.

The accompanying test would add the `llvm.loop.mustprogress` metadata
node kind, but thanks to the allowlist, filters it out, and so the
whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist,
and remain after the DXIL Prepare pass.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…136386)

This PR introduces a Metadata Node Kind allowlist. The purpose is to
prevent newer Metadata Node Kinds to be used and inserted into the
outputted DXIL module. Only the metadata kinds that are accepted in the
DXIL Validator are on the allowlist. The Github DXC validator doesn't
support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata
that isn't found in the list.

The accompanying test would add the `llvm.loop.mustprogress` metadata
node kind, but thanks to the allowlist, filters it out, and so the
whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist,
and remain after the DXIL Prepare pass.
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
…136386)

This PR introduces a Metadata Node Kind allowlist. The purpose is to
prevent newer Metadata Node Kinds to be used and inserted into the
outputted DXIL module. Only the metadata kinds that are accepted in the
DXIL Validator are on the allowlist. The Github DXC validator doesn't
support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata
that isn't found in the list.

The accompanying test would add the `llvm.loop.mustprogress` metadata
node kind, but thanks to the allowlist, filters it out, and so the
whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist,
and remain after the DXIL Prepare pass.
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
…136386)

This PR introduces a Metadata Node Kind allowlist. The purpose is to
prevent newer Metadata Node Kinds to be used and inserted into the
outputted DXIL module. Only the metadata kinds that are accepted in the
DXIL Validator are on the allowlist. The Github DXC validator doesn't
support these newer Metadata Node Kinds, so we need to filter them out.

We introduce this restrictive allowlist into LLVM and strip all metadata
that isn't found in the list.

The accompanying test would add the `llvm.loop.mustprogress` metadata
node kind, but thanks to the allowlist, filters it out, and so the
whitelist is proven to work.
The test also has two separate metadata kinds that are on the allowlist,
and remain after the DXIL Prepare pass.
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
Status: Closed
Development

Successfully merging this pull request may close these issues.

[DirectX] Strip loop metadata that will trip up the validator when generating DXIL
4 participants