Currently we have logic in the (experimental) DirectX backend which transform clang’s models of HLSL constructs into the equivalent representations in DXIL. For example, we transform hlsl.shader function attributes to metadata in DXILMetadata.cpp. However, when we read DXIL in to LLVM, we don’t have a very good place for the transformation in the opposite direction.
I would like to introduce a new library in lib/Transforms/DXIL where we can consolidate logic to transform in both directions. This would be a place for passes transforming between DXIL constructs and LLVM’s representations and the supporting logic that can be shared between those.
A few examples of things that would find a home here:
Translating DXIL metadata to shader function attributes and resource metadata
Translating resource metadata and function attributes into a format appropriate for DXIL
Mapping between DXIL operations and LLVM constructs / intrinsics
Handling DXIL’s metadata representation of shader models vs LLVM’s use of triples
Feedback welcome. Does anyone have concerns with adding another small library within Transforms? Is this the best place for this?
While the reader part makes sense in “Frontend”, the writer bit is really “Backend” (it is in fact, currently in “Target”). Since the goal is to share code between the reader and writer, if we were to use “Frontend” we’d end up with some pieces being somewhere odd.
So in the backend these will be used for lowering LLVM IR to DXIL by transforming modern LLVM constructs to the way they’re represented in the format - as you point out, that part’s fairly obvious.
Outside of the backend, we need these for transforming in the other direction - taking DXIL constructs and transforming them to how we represent the information in LLVM. If you’re not familiar, DXIL is essentially an old LLVM bitcode format (3.7-ish?), and uses various pieces of metadata, function calls to “DXIL Ops”, and that sort of thing to represent shader-specific things. If we treat DXIL as an input to LLVM we already recognize it as older bitcode and it goes through the upgrader path and we get a valid module, but it’s a module with some strange functions and funny metadata that generic LLVM passes don’t necessarily know what to do with.
So the short answer is that we’d use these transform utilities to replace the DXIL constructs with LLVM ones, and then we have the ability to read DXIL in and get a module as if we’d started from HLSL or something and were on the path to generating that same DXIL.
So that’s how it works mechanically, but you might be asking “Why do we need to read DXIL in at all?”. There are two things I want this for. First, this should provide a path for reading in DXIL and feeding it to a backend - this would need some more infrastructure, but should be useful as a path for GPU codegen. The second and more immediate thing is testing - if we can read DXIL it gives us a few nice options for validating the DirectX backend that we can’t really access today, like round trip testing or using existing DXIL to drive feature development of the DirectX backend before we have all of clang’s HLSL handling in place.
To add one more point: there are a number implementations of reading DXIL either into proprietary formats or reading DXIL into LLVM and translating it to a different target.
GPU vendor drivers read DXIL and retarget it to the device ISA. Some vendor drivers are LLVM-based, so they could directly benefit from this code being public and reusable.
Thanks for the detailed explanation! This all sounds reasonable to me, and I don’t see a problem with lifting this functionality out of the DXIL backend.
Whether Transform/Utils is the best place for this depends on how this reverse-transform is going to be applied. Would this be an actual transformation pass that gets injected in the optimization pipeline (how / by what?) or more of an ad-hoc utility that is applied directly after bitcode reading (how / by what?), or something else?
I was thinking that we’d do this by creating a transformation pass and insert it into clang’s optimization pipeline based on LangOpts telling us that we’re handling DXIL - somewhere in clang’s lib/CodeGen/BackendUtil, though I haven’t looked at exactly where it’ll fit yet. Having this as a pass will also mean that we can write tests using opt rather than having to do something complicated.