Skip to content

[DirectX] only allow intrinsics defined in DXIL.td #128613

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 4 commits into from
Feb 25, 2025

Conversation

farzonl
Copy link
Member

@farzonl farzonl commented Feb 25, 2025

Fixes #128071
The current behavior lets intrinsics that don't map to a DXILOP slip through. Nothing catches this until we hit the DXIL validator. This change fails earlier so we don't encode invalid llvm intrinsics that can slip through because of clang builtins like __builtin_reduce_and
example:
https://hlsl.godbolt.org/z/13rPj18vn

Fixes llvm#128071
The current behavior lets intrinsics that don't map to a DXILOP
slip through. Nothing catches this until we hit the DXIL validator.
This change fails earlier so we don't encode invalid llvm intrinsics
that can slip through because of clang builtins like
`__builtin_reduce_and`
example:
https://hlsl.godbolt.org/z/13rPj18vn
@farzonl farzonl self-assigned this Feb 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 25, 2025

@llvm/pr-subscribers-backend-directx

Author: Farzon Lotfi (farzonl)

Changes

Fixes #128071
The current behavior lets intrinsics that don't map to a DXILOP slip through. Nothing catches this until we hit the DXIL validator. This change fails earlier so we don't encode invalid llvm intrinsics that can slip through because of clang builtins like __builtin_reduce_and
example:
https://hlsl.godbolt.org/z/13rPj18vn


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

2 Files Affected:

  • (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+5-2)
  • (added) llvm/test/CodeGen/DirectX/unknown_intrinsic.ll (+11)
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 83cc4b18824c7..a98107a71c219 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -770,8 +770,11 @@ class OpLowerer {
         continue;
       Intrinsic::ID ID = F.getIntrinsicID();
       switch (ID) {
-      default:
-        continue;
+      default: {
+        DiagnosticInfoUnsupported Diag(F, "Unknown intrinsic?");
+        M.getContext().diagnose(Diag);
+        break;
+      }
 #define DXIL_OP_INTRINSIC(OpCode, Intrin, ...)                                 \
   case Intrin:                                                                 \
     HasErrors |= replaceFunctionWithOp(                                        \
diff --git a/llvm/test/CodeGen/DirectX/unknown_intrinsic.ll b/llvm/test/CodeGen/DirectX/unknown_intrinsic.ll
new file mode 100644
index 0000000000000..efd0c5637077e
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/unknown_intrinsic.ll
@@ -0,0 +1,11 @@
+; RUN: not opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; CHECK: error: <unknown>:0:0: in function llvm.vector.reduce.and.v4i32 i32 (<4 x i32>): Unknown intrinsic?
+define i32 @fn_and(<4 x i32> %0) local_unnamed_addr #0 {
+  %2 = tail call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> %0)
+  ret i32 %2
+}
+
+declare i32 @llvm.vector.reduce.and.v4i32(<4 x i32>)
+
+attributes #0 = { convergent norecurse nounwind "hlsl.export"}

@farzonl farzonl marked this pull request as draft February 25, 2025 01:39
… up some testcases so passes happen in right order and intrinsics have the right declarations
@farzonl farzonl force-pushed the allow-only-legal-intrinsics branch from bfd8361 to 3aacbd7 Compare February 25, 2025 02:27
@farzonl farzonl marked this pull request as ready for review February 25, 2025 02:27
@farzonl
Copy link
Member Author

farzonl commented Feb 25, 2025

Need to investigate a tools/dxil-dis/debug-info.ll failure before merging.

continue;
default: {
DiagnosticInfoUnsupported Diag(F, "Unknown intrinsic?");
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably include the name of the intrinsic in the error message here. It'd also be nice to have a debug location, but I suspect there isn't really anywhere we can grab a reasonable one from so I guess we'll do without.

Copy link
Member Author

Choose a reason for hiding this comment

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

The intrinsic name seems to already be encoded this is the error string i see function llvm.vector.reduce.and.v4i32 i32 (<4 x i32>): Unknown intrinsic?

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps "Unsupported intrinsic for DXIL lowering", but this seems informative enough.

@@ -0,0 +1,11 @@
; RUN: not opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

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

-scalarizer doesn't seem necessary here.

continue;
default: {
DiagnosticInfoUnsupported Diag(F, "Unknown intrinsic?");
M.getContext().diagnose(Diag);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also set HasErrors here? It doesn't actually matter, since we won't leave the casthandles in an inconsistent state and we don't key anything else off of that, but it seems weird not to

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll add it for consistency. We don't seem to be able to do multiple Diagnostic errors per test case. Ie I can only invoke M.getContext().diagnose(...) once. So setting HasErrors seemed pointless.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's because you're using opt, which has the default error handler that just exits the program. If you want a test case with multiple errors you need to use llc (or clang, but that wouldn't be appropriate for this kind of test)

@bogner
Copy link
Contributor

bogner commented Feb 25, 2025

Need to investigate a tools/dxil-dis/debug-info.ll failure before merging.

I think we need to let llvm.dbg.value through. It's a bit annoying that we seem to be emitting bitcode when we have errors here, but that seems like an issue that's unrelated to this change.

continue;
default: {
DiagnosticInfoUnsupported Diag(F, "Unknown intrinsic?");
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps "Unsupported intrinsic for DXIL lowering", but this seems informative enough.

continue;
default: {
DiagnosticInfoUnsupported Diag(F, "Unknown intrinsic?");
M.getContext().diagnose(Diag);
Copy link
Contributor

Choose a reason for hiding this comment

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

That's because you're using opt, which has the default error handler that just exits the program. If you want a test case with multiple errors you need to use llc (or clang, but that wouldn't be appropriate for this kind of test)

declare <3 x i16> @llvm.dx.uclamp.v3i32(<3 x i16>, <3 x i32>, <3 x i16>)
declare <3 x i16> @llvm.dx.uclamp.v3i16(<3 x i16>, <3 x i16>, <3 x i16>)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did this change?

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 just to make the declaration match the function that is actually used.
I think this is an NFC. We could remove the declarations and the test would still be fine.
I don't see harm in including this change.

Copy link
Member Author

@farzonl farzonl Feb 25, 2025

Choose a reason for hiding this comment

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

Well one its a typo. but it broke when I changed to error by default.There was no uclamp.v3i32 usage only a declare. There was a uclamp.v3i16 usage but no declare.

If we had a use then after the DXILIntrinsicExpansion pass ran it would have done cleanup for the decalres aswell. As is though this intrinsic declaration lives on and triggered our error because there is n uclamp lowering to a dxil op.

Copy link
Contributor

@Icohedron Icohedron left a comment

Choose a reason for hiding this comment

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

Looks ok to me

@farzonl farzonl merged commit 0be3f13 into llvm:main Feb 25, 2025
12 checks passed
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 14, 2025
fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic printer was called
but when using clang the diagnostic message was called.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 14, 2025
fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic printer was called
but when using clang the diagnostic message was called.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 14, 2025
fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic printer was called
but when using clang the diagnostic message was called.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 14, 2025
fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic printer was called
but when using clang the diagnostic message was called.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 18, 2025
…pLowering

fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do this.

What we found was when using `opt` the diagnostic print function  was called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging information like function name and function type when LLVMContext was only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
   just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
   line change. Downside is
   when using opt you see the function name twice. But makes the
   clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
   message directly. Downside is a bunch of innacurate information shows
   up in the message if you don't  specify
   `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
   keep a map of these so we can pass the calling function to
   `DiagnosticInfoUnsupported` instead of the intrinsic.
   This would only be useful if we had debug info so we could
   distinguish different uses of the intrinsic. We would also need to
   change from iterating on every function to doing somethin like a
   LazyCallGraph.

5. pick a different means of doing a Diagnostic error, because other
   uses of`DiagnosticInfoUnsupported` error when we are in the body of a
   function not when we see one being used like in the intrinsic case.

This PR went with option 2. Its low code change that also only impacts
the DirectX backend.
farzonl added a commit to farzonl/llvm-project that referenced this pull request Apr 18, 2025
…pLowering

fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do this.

What we found was when using `opt` the diagnostic print function  was called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging information like function name and function type when LLVMContext was only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
   just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
   line change. Downside is
   when using opt you see the function name twice. But makes the
   clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
   message directly. Downside is a bunch of innacurate information shows
   up in the message if you don't  specify
   `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
   keep a map of these so we can pass the calling function to
   `DiagnosticInfoUnsupported` instead of the intrinsic.
   This would only be useful if we had debug info so we could
   distinguish different uses of the intrinsic. We would also need to
   change from iterating on every function to doing somethin like a
   LazyCallGraph.

5. pick a different means of doing a Diagnostic error, because other
   uses of`DiagnosticInfoUnsupported` error when we are in the body of a
   function not when we see one being used like in the intrinsic case.

This PR went with option 2. Its low code change that also only impacts
the DirectX backend.
farzonl added a commit that referenced this pull request Apr 21, 2025
…pLowering (#136234)

fixes #135654

In #128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic print function was
called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
line change. Downside is when using opt you see the function name twice.
But makes the clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
message directly. Downside is a bunch of innacurate information shows up
in the message if you don't specify `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
keep a map of these so we can pass the calling function to
`DiagnosticInfoUnsupported` instead of the intrinsic. This would only be
useful if we had debug info so we could distinguish different uses of
the intrinsic by line\col number. We would also need to change from
iterating on every function to doing something like a LazyCallGraph
which is a nonstarter.

5. pick a different means of doing a Diagnostic error, because other
uses of `DiagnosticInfoUnsupported` error when we are in the body of a
function not when we see one being used like in the intrinsic case.

This PR went with a combo of option 2 & 5. Its low code change that also
only impacts the DirectX backend.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…pLowering (llvm#136234)

fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic print function was
called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
line change. Downside is when using opt you see the function name twice.
But makes the clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
message directly. Downside is a bunch of innacurate information shows up
in the message if you don't specify `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
keep a map of these so we can pass the calling function to
`DiagnosticInfoUnsupported` instead of the intrinsic. This would only be
useful if we had debug info so we could distinguish different uses of
the intrinsic by line\col number. We would also need to change from
iterating on every function to doing something like a LazyCallGraph
which is a nonstarter.

5. pick a different means of doing a Diagnostic error, because other
uses of `DiagnosticInfoUnsupported` error when we are in the body of a
function not when we see one being used like in the intrinsic case.

This PR went with a combo of option 2 & 5. Its low code change that also
only impacts the DirectX backend.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…pLowering (llvm#136234)

fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic print function was
called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
line change. Downside is when using opt you see the function name twice.
But makes the clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
message directly. Downside is a bunch of innacurate information shows up
in the message if you don't specify `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
keep a map of these so we can pass the calling function to
`DiagnosticInfoUnsupported` instead of the intrinsic. This would only be
useful if we had debug info so we could distinguish different uses of
the intrinsic by line\col number. We would also need to change from
iterating on every function to doing something like a LazyCallGraph
which is a nonstarter.

5. pick a different means of doing a Diagnostic error, because other
uses of `DiagnosticInfoUnsupported` error when we are in the body of a
function not when we see one being used like in the intrinsic case.

This PR went with a combo of option 2 & 5. Its low code change that also
only impacts the DirectX backend.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…pLowering (llvm#136234)

fixes llvm#135654

In llvm#128613 we added safe guards to prevent the lowering of just any
intrinsic in the backend. We used `DiagnosticInfoUnsupported` to do
this.

What we found was when using `opt` the diagnostic print function was
called but when using clang the diagnostic message was used.

Printing message in the clang version means we miss valuable debugging
information like function name and function type when LLVMContext was
only needed to call `getBestLocationFromDebugLoc`.

There are a few potential fixes

1. Write a custom DiagnosticInfoUnsupported so we can change the Message
just for DirectX. Too heavy handed so rejected.

2. Add the function name to the Message in DirectX code. Very simple one
line change. Downside is when using opt you see the function name twice.
But makes the clang-dxc bugs more actionable.

3. change CodeGenAction.cpp to always use the print function and not the
message directly. Downside is a bunch of innacurate information shows up
in the message if you don't specify `-debug-info-kind=standalone`.

4. add some book keeping to know which function called the intrinsic
keep a map of these so we can pass the calling function to
`DiagnosticInfoUnsupported` instead of the intrinsic. This would only be
useful if we had debug info so we could distinguish different uses of
the intrinsic by line\col number. We would also need to change from
iterating on every function to doing something like a LazyCallGraph
which is a nonstarter.

5. pick a different means of doing a Diagnostic error, because other
uses of `DiagnosticInfoUnsupported` error when we are in the body of a
function not when we see one being used like in the intrinsic case.

This PR went with a combo of option 2 & 5. Its low code change that also
only impacts the DirectX backend.
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[DirectX] Error if unknown intrinsic makes it to DXILOpLowering.cpp
5 participants