[SPIR-V] SPIR-V in LLVM

I note that there was a talk recently at EuroLLVM SPIR-V and LLVM about and so I want to get this message out soon so as to avoid duplicated effort.

I have an up to date backend for SPIR-V on an up to date fork (~2-3 weeks behind) of LLVM, transplanted and “modernised” from Khronos’ SPIRV-LLVM that I plan on integrating into LLVM trunk. While it is usable in it’s current form there are several issues.

SPIRV-LLVM’s SPIRV support is not a real backend and does not live in /lib/Target/SPIRV. This has been solved, SPIRV is now a proper target(s). I have not copied the tests across yet, and they will need to be updated in light of the changes made/being made below.
It only support’s the OpenCL “flavour” of SPIRV, but not the Vulkan. I plan on eventually supporting this but its priority is less than that of mainline integration.
Intrinsics (for both Core and OpenCL extension instructions, Vulkan to follow) are done through C++ "Itanium with extensions” mangling. I plan to convert these to a table-gen format and cull the associated mangling code…
Likewise the instruction format is currently done through a home-brew table format, which I am in the process of converting to table-gen. The core instructions are almost complete, but I haven’t started on the OpenCL instructions.
As noted in the talk the textual representation is different from the the reference implementation. I see no advantage of the current format over the reference implementation’s format, infact theirs is much easier to read, This is a low priority.
The instruction table-gen tables contain a significant number of critical non-instruction tables (there are no registers) and therefore a proper table-gen backend needs to be written to accomodate for this. Required.
There are some LLVM “infrastructure” things (TargetInfo, MCTargetDesc) that I have stubs in order to get LLVM to compile, I have no idea what I’m doing and those were mostly adapted from Sparc and RISCV. Due to the lack of a Dyld and the fact that SPIR-V is intermediate format, there is no point of a jit, Simple optimisations (CSE,DCE, some very simple inlining) would be nice to have, as would DebugInfo support.

The code is available at my GitHub:
SPIRV backend https://github.com/thewilsonator/llvm-target-spirv
LLVM https://github.com/thewilsonator/llvm/tree/compute
should you want to inspect/offer advice.

I am very busy until about July, but I just want to put this out here to gather interest / feedback / contributors.

Thanks,
Nicholas Wilson

Great job Nicholas!

Actually one of the topics in the OpenCL BoF during EuroLLVM is to discuss about whether we should put SPIRV as one of the LLVM backends. Some of the guys in ARM and Codeplay participate that BoF. Although there is no concrete conclusion, I think your work would be a valuable materials for folks in both Khronos and LLVM to determine whether put SPIRV under lib/Target.

Also, though I haven’t inspect your work in detail. I think you can take NVPTX and WebAssembly as reference instead of RISCV or SPARC, since the former two targets are both virtual targets, (unlimited amount of registers etc.), just like SPIRV.

Best Regards,
McClane

Great job Nicholas!

Thanks! I want to get this in so I don’t have to maintain a separate version for my work on LDC, the LLVM D Compiler.

Actually one of the topics in the OpenCL BoF during EuroLLVM is to discuss about whether we should put SPIRV as one of the LLVM backends. Some of the guys in ARM and Codeplay participate that BoF.

Great, any help (especially by those who actually know what they doing when it come to LLVM backends, i.e. not me :wink: ) is greatly appreciated. Co-ordination will be the key to getting this done in a reasonable timeframe.

Although there is no concrete conclusion, I think your work would be a valuable materials for folks in both Khronos and LLVM to determine whether put SPIRV under lib/Target.

I think SPIRV should be a proper target (and therefore live in $ROOT/lib/Target) for a number of reasons.

1) Logically it is a target: it produces binary code and is the last stage in the compilation pipeline.

2) it makes it easier for consumers (LDC, clang) to set up the build system / CMake as they only need specify they wish to target SPIRV rather than specify they want to link to libLLVMSPIRV.a with some CMake hackery. This was part of the reason for making it a target, as I was unable to get LDC’s build system to pull in the required library from Khronos’ SPIRV-LLVM.

3) it will integrate much better into consumers existing consumers compilation pipelines, as for LDC, there were, until I made it a target, some rather ugly hacks to fit it in with the rest of the codebase that was setup to just pass the IR to a backend.

4) if its a target then it will stay up to date with LLVM and not stagnate fall many releases behind.

There are probably some others I am missing.

Also, though I haven’t inspect your work in detail. I think you can take NVPTX and WebAssembly as reference instead of RISCV or SPARC, since the former two targets are both virtual targets, (unlimited amount of registers etc.), just like SPIRV.

Haha, you overestimate my exposure to the LLVM codebase by a large margin.
I used RISCV and SPARC because they are simple and I have very little idea what I’m doing, e.g. not knowing what files I need in order to make a backend (LLVMBuild.txt’s CMakeLists.txt,target info MCDesc etc.) no knowledge of the tablegen format (I’m still probably doin' it wrong), not because of architectural similarity.

I have taken some Inspiration from NVPTX simply because my work on LDC involves GPUs in general, but thank for the tip anyway.

Nic

I note that there was a talk recently at EuroLLVM SPIR-V and LLVM about and so I want to get this message out soon so as to avoid duplicated effort.

I have an up to date backend for SPIR-V on an up to date fork (~2-3 weeks behind) of LLVM, transplanted and “modernised” from Khronos’ SPIRV-LLVM that I plan on integrating into LLVM trunk. While it is usable in it’s current form there are several issues.

Thanks for taking the initiative to work on this. A well supported LLVM IR to SPIR-V
transformation layer is something that could be very useful.

SPIRV-LLVM’s SPIRV support is not a real backend and does not live in /lib/Target/SPIRV. This has been solved, SPIRV is now a proper target(s). I have not copied the tests across yet, and they will need to be updated in light of the changes made/being made below.
It only support’s the OpenCL “flavour” of SPIRV, but not the Vulkan. I plan on eventually supporting this but its priority is less than that of mainline integration.
Intrinsics (for both Core and OpenCL extension instructions, Vulkan to follow) are done through C++ "Itanium with extensions” mangling. I plan to convert these to a table-gen format and cull the associated mangling code..
Likewise the instruction format is currently done through a home-brew table format, which I am in the process of converting to table-gen. The core instructions are almost complete, but I haven’t started on the OpenCL instructions.
As noted in the talk the textual representation is different from the the reference implementation. I see no advantage of the current format over the reference implementation’s format, infact theirs is much easier to read, This is a low priority.
The instruction table-gen tables contain a significant number of critical non-instruction tables (there are no registers) and therefore a proper table-gen backend needs to be written to accomodate for this. Required.
There are some LLVM “infrastructure” things (TargetInfo, MCTargetDesc) that I have stubs in order to get LLVM to compile, I have no idea what I’m doing and those were mostly adapted from Sparc and RISCV. Due to the lack of a Dyld and the fact that SPIR-V is intermediate format, there is no point of a jit, Simple optimisations (CSE,DCE, some very simple inlining) would be nice to have, as would DebugInfo support.

The code is available at my GitHub:
SPIRV backend https://github.com/thewilsonator/llvm-target-spirv
LLVM https://github.com/thewilsonator/llvm/tree/compute
should you want to inspect/offer advice.

I am very busy until about July, but I just want to put this out here to gather interest / feedback / contributors.

First of all you may want to review the thread from a few years ago about
putting a SPIR-V target into LLVM:
http://llvm.1065342.n5.nabble.com/RFC-Proposal-for-Adding-SPIRV-Target-td82552.html

The fact that the SPIR-V target translates LLVM IR to SPIR-V directly and
does not use SelectionDAG/MachineInstrs or any of the standard lowering
mechanism is a strong case against having it in lib/Target. There are
two solutions for this: have the code live outside of lib/Target or as an
out-of-tree project(maybe as part of SPIRV-Tools[1]) or rewrite it to use
the standard lowering mechanism of LLVM.

In my opinion, doing LLVM IR->SPIR-V directly is a better option than
trying to convert it to a proper LLVM target. SPIR-V is too high level
to be able to gain any advantage from using LLVM's standard lowering
mechanism.

You will lose a lot of the type information going through the
SelectionDAG/MachineInstr layers, which is a major disadvantage. Also,
since almost everything is legal in SPIR-V, you won't be getting the
same kind of advantages from it as other targets.

SPIR-V and LLVM IR are actually fairly similar in terms of what level of the
compiler stack the were designed for, so I think doing a simple
LLVM IR -> SPIR-V conversion will be the easiest and give you the best
results in the the end.

Also, if you are able to integrate it into SPIR-V Tools you will be able
to re-use the existing SPIR-V in memory representation and the
reader/writer APIs.

I realize that having a separate library will make this more difficult to
integrate into clang, but there are other targets that use external tools
for linking/assembling so, you may be able to find a way to write a SPIR-V
driver for clang that called out to this external tool for the
LLVM IR -> SPIR-V phase.

-Tom

[1] https://github.com/KhronosGroup/SPIRV-Tools

> First of all you may want to review the thread from a few years ago about putting a SPIR-V target into LLVM: 
> [http://llvm.1065342.n5.nabble.com/RFC-Proposal-for-Adding-SPIRV-Target-td82552.html](http://llvm.1065342.n5.nabble.com/RFC-Proposal-for-Adding-SPIRV-Target-td82552.html)
Thanks I will take a look.
> The fact that the SPIR-V target translates LLVM IR to SPIR-V directly and
> does not use SelectionDAG/MachineInstrs or any of the standard lowering
> mechanism is a strong case against having it in lib/Target.  
Can you even use tablegen as a not target for generating binary format descriptions and intrinsics (serious question)? I think it will require a custom tablegen backend anyway though. One of the primary reasons for being a target is that we can have intrinsics that map directly to core and OpenCL/Vulkan extension instructions, as opposed to the mangling hacks used at the moment, which hurt use by anyone who can’t mangle C++ and windows users because it doesn’t make ANY sense to mangle some stuff as Itanuinm and some stuff as MS.
I am in the process of trying to make it “more traditional” where possible and it makes sense to do so, but I do not fully understand the backend pipeline and am just trying to express the intrinsics and binary format with tablegen at the moment. Regardless of the actual transformation pipeline used I think it should go in target for the advantages stated above, and the one that I missed was, it has its own target triples! Also IIRC the Mill CPUs will/(do?) only use one or two of the backend passes and I can’t imagine them not being considered targets.
>There are
> two solutions for this: have the code live outside of lib/Target or as an
> out-of-tree project(maybe as part of SPIRV-Tools[1]) or rewrite it to use
> the standard lowering mechanism of LLVM.
The first loses the advantages of being a target, the second loses that and the advantages of more eyes of LLVM devs AND staying in sync with the rest of the codebase, not to mention releases! And I can’t see any advantages gained from either of those two possibilities.

>In my opinion, doing LLVM IR->SPIR-V directly is a better option than
>trying to convert it to a proper LLVM target.  SPIR-V is too high level
>to be able to gain any advantage from using LLVM's standard lowering
>mechanism.
>
>You will lose a lot of the type information going through the
>SelectionDAG/MachineInstr layers, which is a major disadvantage.  Also,
>since almost everything is legal in SPIR-V, you won't be getting the
>same kind of advantages from it as other targets.
>
>SPIR-V and LLVM IR are actually fairly similar in terms of what level of the
>compiler stack the were designed for, so I think doing a simple
>LLVM IR -> SPIR-V conversion will be the easiest and give you the best
>results in the the end.
Perhaps, I do not understand the backend pipeline, but that is entirely orthogonal to targethood.

>Also, if you are able to integrate it into SPIR-V Tools you will be able
>to re-use the existing SPIR-V in memory representation and the
>reader/writer APIs.
The in memory representation is already there (inherited from SPIRV-LLVM) and will be refined once the tablegen work is complete, i.e. redundant code removed and integration with the tables. 
>I realize that having a separate library will make this more difficult to
>integrate into clang, but there are other targets that use external tools
>for linking/assembling so, you may be able to find a way to write a SPIR-V
>driver for clang that called out to this external tool for the
>LLVM IR -> SPIR-V phase.
An external library looks to get the worst of all worlds. I have no interest in clang, as my work is for LDC. The external library approach would require me writing a driver for LDC and then someone else writing a driver for clang, duplicating both code AND effort/bug fixes across multiple projects. There is already enough to do w.r.t metadata for the producer without having to alter their compilation pipeline as well. I plan to reduce the dependance on metadata as much as possible, but still. 

Nic

First of all you may want to review the thread from a few years ago about putting a SPIR-V target into LLVM:

http://llvm.1065342.n5.nabble.com/RFC-Proposal-for-Adding-SPIRV-Target-td82552.html

Thanks I will take a look.

The fact that the SPIR-V target translates LLVM IR to SPIR-V directly and
does not use SelectionDAG/MachineInstrs or any of the standard lowering
mechanism is a strong case against having it in lib/Target.

Can you even use tablegen as a not target for generating binary format descriptions and intrinsics (serious question)? I think it will require a custom tablegen backend anyway though. One of the primary reasons for being a target is that we can have intrinsics that map directly to core and OpenCL/Vulkan extension instructions, as opposed to the mangling hacks used at the moment, which hurt use by anyone who can’t mangle C++ and windows users because it doesn’t make ANY sense to mangle some stuff as Itanuinm and some stuff as MS.

You would probably need to write a new tablegen backend to generate
instruction tables that would be used outside of LLVM.

I am in the process of trying to make it “more traditional” where possible and it makes sense to do so, but I do not fully understand the backend pipeline and am just trying to express the intrinsics and binary format with tablegen at the moment. Regardless of the actual transformation pipeline used I think it should go in target for the advantages stated above, and the one that I missed was, it has its own target triples! Also IIRC the Mill CPUs will/(do?) only use one or two of the backend passes and I can’t imagine them not being considered targets.

A "more traditional" backend is more than just using tablegen to generate
the instruction definitions. It means implementing an instruction selector
and using SelectionDAG and MachineInstrs. What I'm saying is that
SPIR-V is not a good fit for the existing infrastructure, so I think
it would be good to consider alternatives before going to far down that path.

There are
two solutions for this: have the code live outside of lib/Target or as an
out-of-tree project(maybe as part of SPIRV-Tools[1]) or rewrite it to use
the standard lowering mechanism of LLVM.

The first loses the advantages of being a target, the second loses that and the advantages of more eyes of LLVM devs AND staying in sync with the rest of the codebase, not to mention releases! And I can’t see any advantages gained from either of those two possibilities. >In my opinion, doing LLVM IR->SPIR-V directly is a better option than >trying to convert it to a proper LLVM target. SPIR-V is too high level >to be able to gain any advantage from using LLVM's standard lowering >mechanism. > >You will lose a lot of the type information going through the >SelectionDAG/MachineInstr layers, which is a major disadvantage. Also, >since almost everything is legal in SPIR-V, you won't be getting the >same kind of advantages from it as other targets. > >SPIR-V and LLVM IR are actually fairly similar in terms of what level of the >compiler stack the were designed for, so I think doing a simple >LLVM IR -> SPIR-V conversion will be the easiest and give you the best >results in the the
end.

Perhaps, I do not understand the backend pipeline, but that is entirely orthogonal to targethood. >Also, if you are able to integrate it into SPIR-V Tools you will be able >to re-use the existing SPIR-V in memory representation and the >reader/writer APIs.

The in memory representation is already there (inherited from SPIRV-LLVM) and will be refined once the tablegen work is complete, i.e. redundant code removed and integration with the tables.

I realize that having a separate library will make this more difficult to >integrate into clang, but there are other targets that use external tools >for linking/assembling so, you may be able to find a way to write a SPIR-V >driver for clang that called out to this external tool for the >LLVM IR -> SPIR-V phase.

An external library looks to get the worst of all worlds. I have no interest in clang, as my work is for LDC. The external library approach would require me writing a driver for LDC and then someone else writing a driver for clang, duplicating both code AND effort/bug fixes across multiple projects. There is already enough to do w.r.t metadata for the producer without having to alter their compilation pipeline as well. I plan to reduce the dependance on metadata as much as possible, but still.

I think we may be using different definitions of driver in this case.
From the perspective of LDC, it doesn't seem like it would matter
much if LLVM IR -> SPIR-V were in the LLVM tree or outside of it.
In both cases you have a library that takes LLVM IR as input and
produces SPIR-V as output.

-Tom

You would probably need to write a new tablegen backend to generate
instruction tables that would be used outside of LLVM.

I think I need to write one anyway because I need to generate lots of tables other than the instruction table (there are no registers and therefore no register table), at least one for each subsection of section 3 of the spec and then some. Wether or not it is inside or outside of LLVM is irrelevant. What is, is that I’m not sure that the same can be said for intrinsics, which will enable the removal of the mangling hacks.

I am in the process of trying to make it “more traditional” where possible and it makes sense to do so, but I do not fully understand the backend pipeline and am just trying to express the intrinsics and binary format with tablegen at the moment. Regardless of the actual transformation pipeline used I think it should go in target for the advantages stated above, and the one that I missed was, it has its own target triples! Also IIRC the Mill CPUs will/(do?) only use one or two of the backend passes and I can’t imagine them not being considered targets.

A "more traditional" backend is more than just using tablegen to generate
the instruction definitions. It means implementing an instruction selector
and using SelectionDAG and MachineInstrs. What I'm saying is that
SPIR-V is not a good fit for the existing infrastructure, so I think
it would be good to consider alternatives before going to far down that path.

Indeed, tablegen is the first step. I intend to incorporate as much of the standard pipeline as makes sense (e.g. no register allocation because there are no registers). As I have said I know very little about the backend pipeline so I can’t comment a whole lot. Is SelectionDAG matching pieces of the IR and choosing instruction from them? Because I definitely want to do that, if it is a thing (the tablegen files are currently dag free and do not inherit from `Instruction` because I do not understand that process, I am merely transcribing the spec), any information is welcome.

I think we may be using different definitions of driver in this case.
From the perspective of LDC, it doesn't seem like it would matter
much if LLVM IR -> SPIR-V were in the LLVM tree or outside of it.
In both cases you have a library that takes LLVM IR as input and
produces SPIR-V as output.

-Tom

My point is that it is duplicated effort (i.e. one for LDC, one for clang and so on), and is redundant if it is a proper target. I am willing to put in the effort to allay concerns regarding the pipelining and legalisation, but I am steadfast in my opinion that it should be a target unless the benefits of not being a target can be shown to outweigh those of being a target.

Hi,

I actively working on LLVM project with SPIR (no V here), and would love to see SPIR-V get integrated into LLVM.

Does this SPIR-V “backend” support reading SPIR-V and generate LLVM IR in-memory representation?

Right, what I was trying to say is that there are more benefits from
having this not be a target than there is from having it be a target.
What you are proposing is a lot of work, and even if it does help to
avoid some duplicate work (which to be honest, I still don't quite
understand what duplication there would be if it weren't a target),
I don't think that is enough to justify the effort required.

-Tom

Hi,

I actively working on LLVM project with SPIR (no V here), and would love to see SPIR-V get integrated into LLVM.

Does this SPIR-V “backend” support reading SPIR-V and generate LLVM IR in-memory representation?

Yes it does.

Right, what I was trying to say is that there are more benefits from

having this not be a target than there is from having it be a target.

Please enumerate them, I have seen none posted so far . The implied “it is what all the the other backends do” w.r.t ISel/MC is at best(worst?) an implementation detail, and I’m still not quite sure why Chandler was so adamant about that. He seemed to imply that generating straight from the IR (as opposed to post legalisation?) introduces a direct dependance in the IR that the rest of LLVM would then be required to not break? I agree that the SPIRV backend should be insulated from changes the IR, although I’m not sure how to achieve that property. I’m also not sure how much, if at all, it would be susceptible to that to begin with. Deletions of instructions/attributes would obviously cause breakage and additions may cause unhandled and/or invalid combinations. I still don’t get the severity if this though, insight appreciated.

What you are proposing is a lot of work, and even if it does help to
avoid some duplicate work (which to be honest, I still don’t quite
understand what duplication there would be if it weren’t a target),
I don’t think that is enough to justify the effort required.

My point is that (modulo metadata, which I am still investigating better alternatives, and calling conventions) if SPIRV is a target, then a producer need not change their compilation pipeline at all to target SPIRV. There should be no effort required, it would come as a property of being a target. I think we are confusing each other again :frowning:

Leaving that aside for a moment, there are a number of advantages/requirements that, correct me if I’m wrong, would be impossible without a proper target.

  • Most critically: Intrinsics. I am almost certain that you would not accept the current mangling hacks, and if I am to support windows neither can I. Any solution would therefore need to be able to register intrinsics and I believe this is impossible without a target (and even if it is, it makes less sense than a target that doesn’t use ISel/MC). Not being able to use intrinsics is a complete deal breaker.

*Basic optimisations (basic CSE,DCE,inlining): requires a TargetLibraryInfoImpl(?) which I believe requires a target. While not strictly necessary it would improve the readability of the resulting IR/SPIRV. All of the more complex optimisations would be done “post ingestion” of the SPIRV and with a different target triple so are unaffected by any decision made. See my reply to Hongbin for an approach.

Right, what I was trying to say is that there are more benefits from

having this not be a target than there is from having it be a target.

Please enumerate them, I have seen none posted so far . The implied “it is what all the the other backends do” w.r.t ISel/MC is at best(worst?) an implementation detail, and I’m still not quite sure why Chandler was so adamant about that. He seemed to imply that generating straight from the IR (as opposed to post legalisation?) introduces a direct dependance in the IR that the rest of LLVM would then be required to not break? I agree that the SPIRV backend should be insulated from changes the IR, although I’m not sure how to achieve that property. I’m also not sure how much, if at all, it would be susceptible to that to begin with. Deletions of instructions/attributes would obviously cause breakage and additions may cause unhandled and/or invalid combinations. I still don’t get the severity if this though, insight appreciated.

So there are really two questions here:
1. Should targets be required to use SelectionDAG/GlobalISEL ?
2. Should SPIR-V use SelectionDAG/GlobalISel?

In my opinion, regardless of the answer to question #1, the answer
to question #2 is no, SPIR-V should not use SelectionDAG/GlobalISel.

I touched on this before in previous emails, but the main problem is that
SelectionDAG (and GlobalISel to a lesser extent) plus the whole MachineInstr
layer is a much lower-level representation than SPIR-V, so you will
need to do a lot of extra work and/or modifications to existing
infrastructure in order to get a working target, and even then
you may be limited to emitting poor quality SPIR-V that other
backends will have a hard time optimizing.

With all this work, what advantages are you getting? If the
only reason to do it this way is so you can use intrinsics,
or TargetLibraryInfo, or easier integration with other tools,
I think it would be better to try to save the effort and try
to solve those problems in some other way.

LLVM IR -> SPIR-V directly will give you better code, lower compile
times. It will be more simple and easier to maintain, and you will
be able to re-use existing SPIR-V parsers/writers that exist
in SPIRV-Tools.

This goes back to something I mentioned in my original email, but
I really think the best thing to do for this project right now is to
keep it separate from LLVM, clean up the code, and try to get people
using it. It's going to be much easier to get this upstream in LLVM or
even convince people that the answer to question #1 should be 'no' if we
have a code base that is mature, well supported, and has a healthy
userbase.

What you are proposing is a lot of work, and even if it does help to
avoid some duplicate work (which to be honest, I still don't quite
understand what duplication there would be if it weren't a target),
I don't think that is enough to justify the effort required.

My point is that (modulo metadata, which I am still investigating better alternatives, and calling conventions) if SPIRV is a target, then a producer need not change their compilation pipeline /at all/ to target SPIRV. There should be no effort required, it would come as a property of being a target. I think we are confusing each other again :frowning:

Leaving that aside for a moment, there are a number of advantages/requirements that, correct me if I’m wrong, would be impossible without a proper target.

* Most critically: Intrinsics. I am almost certain that you would not accept the current mangling hacks, and if I am to support windows neither can I. Any solution would therefore need to be able to register intrinsics and I believe this is impossible without a target (and even if it is, it makes less sense than a target that doesn’t use ISel/MC). Not being able to use intrinsics is a complete deal breaker.

You don't need to register intrinsics to be able to use them, and
it's also possible to register them without a backend, but this has not
been done before.

*Basic optimisations (basic CSE,DCE,inlining): requires a TargetLibraryInfoImpl(?) which I believe requires a target. While not strictly necessary it would improve the readability of the resulting IR/SPIRV. All of the more complex optimisations would be done “post ingestion” of the SPIRV and with a different target triple so are unaffected by any decision made. See my reply to Hongbin for an approach.

You don't need a target for this. TargeLibraryInfo is constructed
based on the triple.

-Tom

So there are really two questions here:

  1. Should targets be required to use SelectionDAG/GlobalISEL ?
  2. Should SPIR-V use SelectionDAG/GlobalISel?

In my opinion, regardless of the answer to question #1, the answer
to question #2 is no, SPIR-V should not use SelectionDAG/GlobalISel.

Yes I have come the same conclusion when I saw how much type information was lost.

I touched on this before in previous emails, but the main problem is that
SelectionDAG (and GlobalISel to a lesser extent) plus the whole MachineInstr
layer is a much lower-level representation than SPIR-V, so you will
need to do a lot of extra work and/or modifications to existing
infrastructure in order to get a working target, and even then
you may be limited to emitting poor quality SPIR-V that other
backends will have a hard time optimizing.

With all this work, what advantages are you getting? If the
only reason to do it this way is so you can use intrinsics,
or TargetLibraryInfo, or easier integration with other tools,
I think it would be better to try to save the effort and try
to solve those problems in some other way.

I didn’t really want to do that in the first place, but your responses and Chandler’s
reply in the previous RFC made it seem an absolute requirement.

LLVM IR → SPIR-V directly will give you better code, lower compile
times. It will be more simple and easier to maintain, and you will
be able to re-use existing SPIR-V parsers/writers that exist
in SPIRV-Tools.

Yes the current approach seems to be the place that has the most type info.
(aside the AST of the producer, but that is a non sequitur, because what
would we be building a backend for?).
The parser and writer already exist in the code base, as do the
interconversions between LLVM <->SPIR-V, so the only use would
be for the verifier for testing. I mean I could replace them, but that
is effort for nothing

This goes back to something I mentioned in my original email, but
I really think the best thing to do for this project right now is to
keep it separate from LLVM, clean up the code, and try to get people
using it. It’s going to be much easier to get this upstream in LLVM or
even convince people that the answer to question #1 should be ‘no’ if we
have a code base that is mature, well supported, and has a healthy
userbase.

The purpose of this thread is to notify the community that I am working on
this so that there ends up only one proposal and we don’t waste effort.

I’ll definitely polish and test thoroughly once I get intrinsics and the rest of
the tablegen stuff done.
I hope to get a large user base but this is sort of chicken and egg.
Keeping it separate is not a viable longterm solution and is the primary reason
why I want to upstream it eventually.

You don’t need to register intrinsics to be able to use them, and
it’s also possible to register them without a backend, but this has not
been done before.

“Hasn’t been bone” in an encouraging way, or not?
And you base them on what, the collection of triples?
(i.e. spirv: 32-bit OpenCL, spirv64: 64-bit OpenCL and possibly a Vulkan one
if/when I have an idea on what to do with the (lack of) layout)

Irrespectively my fork will stay a target for now.

You don’t need a target for this. TargeLibraryInfo is constructed
based on the triple.

Ah, OK, maybe it was that there wasn’t a triple independent one available
for the really simple operations when I was trying to enable it.

Thanks,
Nic

I'd like to add my weight to this statement from Tom Stellard:

This goes back to something I mentioned in my original email, but
I really think the best thing to do for this project right now is to
keep it separate from LLVM, clean up the code, and try to get people
using it. It's going to be much easier to get this upstream in LLVM or
even convince people that the answer to question #1 should be 'no' if we
have a code base that is mature, well supported, and has a healthy
userbase.

I think SPIR-V should go into tip LLVM eventually, I think it should not use SelectionDAG/GlobalISel because it doesn't make sense in my opinion. But the most important issue is getting LLVM based languages be able to target SPIR-V.

I think it would be highly beneficial if any SPIR-V target was used to make external targets in LLVM/Clang work much more nicely. Being blunt - external targets in LLVM suck, you're forced to patch the source code. If a SPIR-V target could be used to:

- Allow experimental targets to be discovered outwith the LLVM tree (EG. the target doesn't have to be in lib/Target)
- Move target triple information into the lib/Target/* folders (or at least allow a target to register a triple and information about the target in the target folder)

That'll get us a huge step towards being able to use LLVM with external targets, and also allow a bunch of developers to use a SPIR-V backend as if it was in tip LLVM, thus demonstrating its viability for upstream.

Cheers,
-Neil.

I agree with Neil and Tom's comments that going through the whole backend process of LLVM is too heavyweight and loses too much information. I'm very interested in seeing, and contributing to, development of the SPIR-V<->LLVM translation library and we should continue to discuss whether this can be upstreamed into LLVM in some way, and exactly what form it should take.

I think if we allow lib/Target backends to not use SelectionDAG, this would be a way to integrate it in.

Neil

This is completely skipping over one very important step which is currently part of ISel: legalization. The LLVM optimizers expect backends to support arbitrary-width integers, arbitrary-width vectors, and target-independent intrinsics which are lowered by legalization. SPIR-V does not have native support for these, therefore you need the legalization framework. And the legalization framework in LLVM is fundamentally tied to ISel.

-Eli

If type legalization really is required, I think we should at least take using SelectionDAG off the table. At worst I think this should directly jump to using global isel.

-Matt

While in practice most SPIR-V consumers are going to only accept i16/32/64, f/16/32/64 and pointers as the base types, I couldn’t actually find anything in the spec that says otherwise. This is particularly relevant for FPGAs that can support arbitrary integers (not sure about their consumers / synthesis tools though). Perhaps I should seek clarification from Khronos and maybe add that as a new capability.

(Given there are two Neil's on this thread, I'll use Codeplay Neil as an alias to differentiate!)

SPIR-V is constrained in the environment specs that consume it - https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0/doc/specs/vulkan/appendices/spirvenv.txt for Vulkan 1.0.

Vulkan does not support i8 types (currently), and has optional i16, i64, f64 types.

SPIR-V as a thing does not have these constraints though, so outwith an environment (we're losing the usefulness of SPIR-V without being able to feed it to a Vulkan/OpenCL driver) it can support arbitrary type widths.

Cheers,
- Codeplay Neil.

Great job Nicholas!

Indeed this is great news!

Thanks! I want to get this in so I don’t have to maintain a separate version for my work on LDC, the LLVM D Compiler.

I want to mirror this comment, the fact that if I want use SPIR-V I
need to versions of my compiler made me just drop the idea of adding
support for it.

Actually one of the topics in the OpenCL BoF during EuroLLVM is to discuss about whether we should put SPIRV as one of the LLVM backends. Some of the guys in ARM and Codeplay participate that BoF.

Great, any help (especially by those who actually know what they doing when it come to LLVM backends, i.e. not me :wink: ) is greatly appreciated. Co-ordination will be the key to getting this done in a reasonable timeframe.

Although there is no concrete conclusion, I think your work would be a valuable materials for folks in both Khronos and LLVM to determine whether put SPIRV under lib/Target.

I think SPIRV should be a proper target (and therefore live in $ROOT/lib/Target) for a number of reasons.

1) Logically it is a target: it produces binary code and is the last stage in the compilation pipeline.

2) it makes it easier for consumers (LDC, clang) to set up the build system / CMake as they only need specify they wish to target SPIRV rather than specify they want to link to libLLVMSPIRV.a with some CMake hackery. This was part of the reason for making it a target, as I was unable to get LDC’s build system to pull in the required library from Khronos’ SPIRV-LLVM.

3) it will integrate much better into consumers existing consumers compilation pipelines, as for LDC, there were, until I made it a target, some rather ugly hacks to fit it in with the rest of the codebase that was setup to just pass the IR to a backend.

4) if its a target then it will stay up to date with LLVM and not stagnate fall many releases behind.

There are probably some others I am missing.

While I can't really comment on where in the code it should live. But
with my frontend developer hat I'll like say it should be treated as
much as possible as a target, at least as long as I can use the C-API
to get the C binary from a module that I have generated I am happy.

Cheers, Jakob.