KhronosGroup/SPIRV-LLVM

Support Vulkan?

Opened this issue · 25 comments

Not sure if Vulkan is currently supported or not, but it seems only OpenCL is?

Currently the translator only supports OpenCL.

After I'm done getting LDC to support OpenCL SPIR-V then if there is sufficient demand (principally within the D community, but other contributors are ofc welcome) I may consider making a fork of llvm off the latest version, updating this (OpenCL SPIR-V) to the latest llvm version and adding a Vulkan backend to it. Possibly also considering trying to integrate Microsoft's recently open-sourced DirectXShaderCompiler, although it's git history will definitely be bjorked. Will wait till development of it has matured a bit.

If I get around to doing that I will be changing a few things: most noticeably using llvm intrinsics for the OpenCL/Vulkan intrinsics rather than "itanium with extensions" C++ mangling, making it far easier to use with other tools.

@keryell Well, it doesn't have releases which makes doing CI difficult, see #201. It is also 105 commits behind KhronosGroup/SPIRV-LLVM 3.6.1 and is on LLVM 3.9.0 as opposed to the final release of that series 3.9.1. It also hasn't received any commits for 11 months.

Hence why I am considering doing it myself. Also I will probably put the lib/SPIRV and tools/llvm-spirv and tools/spirv-tool in their own submodules, to keep things separate and easily identifiable. Will do the same for MS's DirectXShaderCompiler.

Please be aware that the dialect of SPIR-V accepted by Vulkan is significantly different from the dialect accepted by OpenCL. For starters, see the validation rules in the SPIR-V spec for "Shader" vs. "Kernel". Also, check extra restrictions in SPIR-V environment spec in the Vulkan spec: https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#spirvenv

Isn't it just SPIR-V core + Vulkan specific capabilities + Vulkan specific operations (a.k.a. "standard library functions")?

Yes, but that's a big difference from SPIR-V for OpenCL. You're subtracting out a lot of assumptions made by SPIRV-LLVM assuming it's targeting OpenCL.

Examples:

  • In Vulkan all your control flow has to be "structured", with OpLoopMerge and OpSelectionMerge at key points in the control flow graph. If you've lost that original control flow intent from the source language, then you've changed semantics of the program. In OpenCL, you only have to ensure control flow is reducible. That's way simpler to maintain through the compiler flow.

  • Storage classes are completely different. Vulkan has about 10 valid storage classes, and OpenCL has 4. Vulkan's set is not a superset of OpenCL's.

  • OpenCL uses physical addressing, where all types automatically have a fixed size and alignment. Vulkan requires logical addressing. For objects in storage classes exposed to the host API (e.g. Uniform), layout annotations have to be applied.

  • Vulkan's LogicalAddressing model assumes that pointers don't alias unless proven otherwise. OpenCL is like C in that the default is the opposite: pointers are assumed to alias unless proven otherwise.

I urge you to consider all the details first before diving in by writing code.

@thewilsonator: we always appreciate contributions. :-)
On the SPIR-V side, the current LLVM library producing SPIR-V should be updated to a real SPIR-V LLVM back-end, if you are looking to some reorganization ideas...

My reorg ideas are

  • make git submodules for lib/spirv,tools/llvm-spirv and spirv-tool, principally to make keeping up with master easier, but also reduce the size if the code base since AFAIK other than include/llvm/Support/SPIRV.h and the relevant build script files nothing else is changed by this project other than the above listed directory.
  • make all of the (OpenCL) extensions intrinsics rather than "itanium with extensions" C++ magic mangling ( in llvm.spirv.opencl.*?). This will break clang/anything relying on this. It is of no concern to me, but surely will be for others. Will try to leverage .td tools for this. Should make doing the equivalent for Vulkan easier.
  • get a TargetMachine so we can run the dead simple optimisation passes, and fit in with the "normal" backend pipeline.
  • move all the OpenCL specific stuff to its own folder, do the same if/when adding vulkan support.

However I won't have any time to do this until August at the earliest.

@dneto0 thanks for the pointers.

Point 1 seems like a good candidate for an intrinsic function to mark the end.

Point 2 doesn't OpenCL have 5? Private, Local, Global, Constant and Generic? Also seems like a good candidate for addrspace qualification. edit: or is this const, volatile, restrict, ???, @?
Found them all valid SPIR-V storage classes are

  • UniformConstant:
  • Input:
  • Uniform:
  • Output:
  • Workgroup:
  • CrossWorkgroup:
  • Private:
  • Function:
  • Generic:
  • PushConstant:
  • AtomicCounter:
  • Image:
    Not obvious which belong to what though.

Point 3 I'm less sure of, but it seems that metadata should work here.

Point 4 It is the host language's problem to annotate everything with noalias.

@thewilsonator: About storage classes. Yes, OpenCL has 5 (I forgot Generic).

The validity rules are in two places:

  1. Inside the SPIR-V spec itself.
  2. Each client API that uses SPIR-V specifies additional rules in what it calls an "environment spec".
    https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#spirvenv

There's a matching one for OpenCL.

@keryell I have got a fork of llvm 5.0 with spir and spir64 proper targets (also made a Triple::spirl for logical spirv memory to ease Vulcan support if I get around to it) at https://github.com/thewilsonator/llvm/tree/compute
This is about the last point before I begin to change it use use intrinsics instead of mangled C++, and thus break compatibility with https://github.com/KhronosGroup/SPIR/tree/spirv-1.0 (although the upgrade from 3.6.1 to 5.0 will most definitely have broken things). Let me know if you (or others) want to collaborate on this, more eyes hands and bug reporters are always good.

I am OK with that. Thanks for the efforts.

@thewilsonator For now I am busy on my SYCL device compiler with 3.9, SPIR and SPIR-V.
So, along with other projects too, I do not have the bandwidth for more... :-(
But at some point I hope there will be some convergence in future versions. :-)
Thanks for the work.

Oh well. I'm sure it will be there when you decide you need to update your llvm version.
Good luck!

Sorry to ask here, but this issue has been open for a while and seems to serve like a forum (given the project lacks an actual forum).

I see the default branch is 3.6, but there is a 3.8 branch which is listed as active. The 3.8 branch does not pass continuous integration, though.

  • What branch should we work on?
  • Should I raise issues for the failing tests on the 3.8 branch? How can I assign myself to a ticket I create?

I need to work on fixing bugs on an LLVM related project for gaining familiarity with it and this seems a good place for me to pick up bugs.

Thanks in advance.

Ok, I think I figured out this question. The project is based on LLVM 3.6 and that is what the 3.6 branch contains. There was an attempt to upgrade to LLVM 3.8.1 but the tests are not passing with the new version, which is the reason why we are stuck on the 3.6 branch.

Someone has to figure out why the tests are failing with the upgraded version. I could try to look at that then. Tell me if there is anything else you would like me to focus on. For any suggestions, feel free to contact me (abel.bernabeu@gmail.com).

Just an update to all: there is reasonable interest from the LLVM people for upstreaming my work (see http://lists.llvm.org/pipermail/llvm-dev/2017-May/112538.html), which is an updated version of this, as well as some interest from the D community to support Vulkan as part of that.

I can agree on most of your suggestions.

However, suggesting that this project should be an LLVM backend is understimating the main attractive of the khronos branch: that it is a bidirectional translator.

It can translate back and forth between LLVM IR and SPIRV because the library keeps the mapping bijective, and that property should be retained for the project to gain traction.

For someone who has a pre-existing frontend and a pre-existing backend, the library allows to use SPIRV as a standard program exchange format.


            llvm-spirv           llvm-spirv (reversing)
            +-----^-----+        +-----^-----+
            |           |        |           |
   source -> ll -> spirv          spirv-> ll -> binary
  |            |                         |           |
  +-----v------+                         +-----v-----+
  Existing frontend                      Existing backend

Is there such a thing as an abstraction of a bidirectional LLVM IR translator on the LLVM framework? I do not think so, but I could be wrong. Having a SPIRV backend is just half of the whole picture.

Ideally you want the concept of bidirectional translator abstracted on an LLVM interface, and then we would stop having SPIRV-LLVM as a library.

that it is a bidirectional translator

It is that at the moment and it will remain one. In fact I hope to improve it by integrating it (not quite the right word) with the other backends i.e. translate the intrinsics.
There are some discussions/reservations about the exact implementation but ...

project to gain traction

The last functional commit to this repo was at the start of December...

For someone who has a pre-existing frontend

Thats precisely why I am doing this, see my first post.

and a pre-existing backend, the library allows to use SPIRV as a standard program exchange format

My changes integrate far better with the LLVM machinery and so opens this up to more backends, all that would be required would be to translate the intrinsics. No to mention with stay up to date with LLVM instead of falling 2 years and counting behind.

Ideally you want the concept of bidirectional translator abstracted on an LLVM interface, and then we would stop having SPIRV-LLVM as a library.

That's what llvm-spirv is for, to do the reverse translation. The intrinsic translation will have to be added to it but that requires someone with knowledge of the other backends, who are most likely to be found in the LLVM project itself.

Ok, if we still can translate back and forth I guess your fork deserves a try. I would never object to people forking open source stuff :)

Am not an expert in LLVM and I have only started spending o couple hours a week on getting the 3.8 branch on a better shape than it is now. My modest short term goal is getting the continuous integration functional by fixing the current failures. From what I have seen so far some of the issues are due to the tests needing an update but others need fixing the translator.

Apart from that, I did try to manually merge the functional changes from the 3.6 branch that where missing in my working copy of 3.8. The merged tree builds and seems to work, which tells me that, once the continuous integration works, we could start cherry picking from 3.6 into 3.8 trying to keep the history of who made what change.

Neither am I :)

My plan is to:

  1. finish moving to an intrinsics/tablegen format. This requires writing a backend for tablegen, and will enabling removing all of the mangling code and custom table stuff.
  2. port the tests across from this repo, update them to account for (1) and fix any breakages.
  3. make llvm-spirv usable.
  4. change the assembly format to match the reference implementation and update the tests accordingly.
  5. assuming I haven't forgotten anything upstream it to LLVM.

I currently have to translate a large codebase from C++11 to Vulkan compute.

I am wondering whether the more sensible approach would be to rather have a transpiler from C++ to SPIRV for Vulkan

The frontend could then:

  1. Handle C++ name resolution
  2. Prevent any call to a mutating function
  3. Prevent access of pointers
  4. Prevent passing of references to calls

The backend would have to:

  1. Handle (naive) name mangling scheme for GLSL

That of course is a oversimplification.

@abergmeier-dsfishlabs I think you would have better luck scripting clang to do the translations of C++ to Vulkan Compute source for you, rather than go via SPIR-V.

If you do decide to go the SPIR-V route https://github.com/google/clspv may be of interest.

C++ to Vulkan Spir-V compute shaders is something that I want as well.
There is plenty of CUDA source that would justify that. AMD stuff as well.

Vulkan was supposed to bring compute and graphics together no?