-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[DirectX][docs] Architecture and design philosophy of DXIL support #78221
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
=============================================== | ||
Architecture and Design of DXIL Support in LLVM | ||
=============================================== | ||
|
||
.. contents:: | ||
:local: | ||
|
||
.. toctree:: | ||
:hidden: | ||
|
||
Introduction | ||
============ | ||
|
||
LLVM supports reading and writing the `DirectX Intermediate Language. | ||
<https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst>`_, | ||
or DXIL. DXIL is essentially LLVM 3.7 era bitcode with some | ||
restrictions and various semantically important operations and | ||
metadata. | ||
|
||
LLVM's implementation philosophy for DXIL support is to treat DXIL as | ||
merely a representation format as much as possible. When reading DXIL, | ||
we should translate everything to generic LLVM constructs when | ||
possible. Similarly, we should introduce DXIL-specific constructs as | ||
late as possible in the process of lowering to the format. | ||
|
||
There are three places to look for DXIL related code in LLVM: The | ||
`DirectX` backend, for writing DXIL; The `DXILUpgrade` pass, for | ||
reading; and in library code that is shared between writing and | ||
reading. We'll describe these in reverse order. | ||
|
||
Common Code for Reading and Writing | ||
=================================== | ||
|
||
There's quite a bit of logic that needs to be shared between reading | ||
and writing DXIL in order to avoid code duplication. While we don't | ||
have a hard and fast rule about where such code should live, there are | ||
generally three sensible places. Simple definitions of enums and | ||
values that must stay fixed to match DXIL's ABI can be found in | ||
`Support/DXILABI.h`, utilities to translate bidirectionally between | ||
DXIL and modern LLVM constructs live in `lib/Transforms/Utils`, and | ||
more analyses that are needed to derive or preserve information are | ||
implemented as typical `lib/Analysis` passes. | ||
|
||
The DXILUpgrade Pass | ||
==================== | ||
|
||
Translating DXIL to LLVM IR takes advantage of the fact that DXIL is | ||
compatible with LLVM 3.7 bitcode, and that modern LLVM is capable of | ||
"upgrading" older bitcode into modern IR. Simply relying on the | ||
bitcode upgrade process isn't sufficient though, since that leaves a | ||
number of DXIL specific constructs around. Thus, we have the | ||
`DXILUpgrade` pass to transform DXIL operations to LLVM operations and | ||
smooth over differences in metadata representation. We call this pass | ||
"upgrade" to reflect that it follows LLVM's standard bitcode upgrade | ||
process and simply finishes the job for DXIL constructs - while | ||
"reader" or "lifting" might also be reasonable names, they could be a | ||
bit misleading. | ||
|
||
The `DXILUpgrade` pass itself is fairly lightweight. It mostly relies | ||
on the utilities described in "Common Code" above in order to share | ||
logic with both the DirectX backend and with Clang's codegen of HLSL | ||
support as much as possible. | ||
|
||
The DirectX Backend | ||
=================== | ||
|
||
The DirectX backend lowers LLVM IR into DXIL. As we're transforming to | ||
an intermediate format rather than a specific ISA, this backend does | ||
not follow the instruction selection patterns you might be familiar | ||
with from other backends. There are two parts to lowering DXIL - a set | ||
of passes that mutate various constructs into a form that matches how | ||
DXIL represents those constructs, followed by a limited bitcode | ||
"downgrader pass". | ||
|
||
Before emitting DXIL, the DirectX backend needs to modify the LLVM IR | ||
such that external operations, types, and metadata is represented in | ||
the way that DXIL expects. For example, `DXILOpLowering` translates | ||
intrinsics into `dx.op` calls. These passes are essentially the | ||
inverse of the `DXILUpgrade` pass. It's best to do this downgrading | ||
process as IR to IR passes when possible, as that means that they can | ||
be easily tested with `opt` and `FileCheck` without the need for | ||
external tooling. | ||
|
||
The second part of DXIL emission is more or less an LLVM bitcode | ||
downgrader. We need to emit bitcode that matches the LLVM 3.7 | ||
representation. For this, we have `DXILWriter`, which is an alternate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just from a naming perspective it would be nice to have a DXILReader to match the DXILWriter, but it looks like we have DXILUpgrade instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The nuance here is that we don't actually implement a DXILReader at all. As per the explanation of DXILUpgrade earlier in the doc:
This is really the rationale for the somewhat unusual naming of DXILUpgrade, since it isn't the reader and it basically operates as an extension of LLVM's standard bitcode upgrade path. Let me know if there's some wording missing that could help clarify that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it makes sense that's what its doing, but that is also a bit of an implementation detail. I could also see us calling the Writer a "Downgrader" instead. In fact, that's what the doc says it does!
It's not a big deal, but I think having a symmetric name for reader/writer or upgrader/downgrader would be nice to quickly understand that the two operations are inverses and how you would do a roundtrip. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit reticent to use a symmetric name here since they really aren't inverses on their own.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added sentences to the DXILUpgrade section and to the paragraph about lowering/downgrading to try to clarify this |
||
version of LLVM's `BitcodeWriter`. At present, this is able to | ||
leverage LLVM's current bitcode libraries to do a lot of the work, but | ||
it's possible that at some point in the future it will need to be | ||
completely separate as modern LLVM bitcode evolves. | ||
|
||
Testing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if we want to call it out this early, but I can see value in eventually adding two additional testing methodologies:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with all of this but I couldn't come up with much concrete to say for now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just honestly can't see how we can have any confidence that the DXIL backend is working until there's functional tests using the generated code. Unit tests can only take us so far, but it won't be long before that isn't going to be sufficient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to in any way diminish the importance of execution testing (because it is very important), but I know that both @bogner, and myself have worked on backend implementations for hardware that didn't yet exist and the development and testing strategies that are common in LLVM have allowed us to do so with very high levels of confidence in the compiler implementation. For the purposes of this document I think it is valuable to focus on compiler unit testing and leave execution testing as a separate problem because it is a large and complicated problem that needs its own separate design. The approach we currently take in DXC is too limited for the goals of our Clang implementation so we'll really need to come up with something much more ambitious (although it certainly can draw inspiration and experience from what we did in DXC). |
||
======= | ||
|
||
A lot of DXIL testing can be done with typical IR to IR tests using | ||
`opt` and `FileCheck`, since a lot of the support is implemented in | ||
terms of IR level passes as described in the previous sections. You | ||
can see examples of this in `llvm/test/CodeGen/DirectX` as well as | ||
`llvm/test/Transforms/DXILUpgrade`, and this type of testing should be | ||
leveraged as much as possible. | ||
|
||
However, when it comes to testing the DXIL format itself, IR passes | ||
are insufficient for testing. For now, the best option we have | ||
available is using the DXC project's tools in order to round trip. | ||
These tests are currently found in `test/tools/dxil-dis` and are only | ||
available if the `LLVM_INCLUDE_DXIL_TESTS` cmake option is set. Note | ||
that we do not currently have the equivalent testing set up for the | ||
DXIL reading path. | ||
|
||
As soon as we are able, we will also want to round trip using the DXIL | ||
writing and reading paths in order to ensure self consistency and to | ||
get test coverage when `dxil-dis` isn't available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will the output of DXILUpgrade Pass look like?
Will it match the input for DirectX backend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will look like LLVM IR without DXIL specific features. Yes, it should be valid input for the DirectX backend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will DXILUpgrade Pass share some code with clang codeGen since they both generate input for DirectX backend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course. I added a short paragraph mentioning how this works to try to clarify.