Setting version, environment, and extensions for SPIR-V target

The current SPIR-V target in LLVM only supports specifying spirv32/spirv64 in triple with the pointer size [1] but no other details. In this RFC I would like to discuss how other important target settings should be provided to the tools. While the position here is summarized from the OpenCL community we hope that it applies to other areas that are using or interested in using SPIR-V, and if it does not fit broad use cases it would be valuable to discuss and agree on a coherent strategy.

While the SPIR-V target doesn’t fall into a category of conventional LLVM targets it seems that extending the triple components makes sense and might be the best route forward considering that this avoids adding extra flags to tools that use LLVM because they can just inherit LLVM’s triple functionality. It might also be slightly more intuitive to the end users as they do not need to learn new flags.

We therefore would like to suggest the following:

1. SPIR-V version can be specified in sub-architecture component of the target triple. Following the example given in [2] we could expose the interface for setting the version using sub-architecture field in the following format, example:

-triple=spirv64v1.0 for SPIR-V 64-bit version 1.0.

Defaults: When the version is not set explicitly it is being deduced from the functionality being compiled. This is how SPIR-V generation tools have been working up to now at least for OpenCL. However LLVM tools traditionally just set some fixed default value in such cases. It might be good to discuss further what would be more valuable to the end users?

2. SPIR-V environment can be set using the system component of the target triple following examples provided in [2]. However this is currently somewhat redundant as high-level languages mostly target a specific SPIR-V environment either OpenCL or Vulkan but not both. However this flexibility might be beneficial in a longer term, example:

-triple=spirv64-unknown-opencl for SPIR-V 64-bit conforming to the OpenCL environment specification with generic/unknown vendor.

Defaults: Currently OpenCL is a default environment and the only one supported. It might be useful to make this deducible from the high-level language default environment. However clang infrastructure is not very flexible towards the target setting being affected by the language. That means requiring components being set explicitly might be the cheapest option from the implementation/maintenance perspective and more consistent with the LLVM tooling flow too.

3. SPIR-V extensions for OpenCL are currently mainly aligned with the OpenCL kernel language extensions. Therefore they can be implicitly set through -cl-ext flag [3]. This flag has not been exposed in the clang driver yet as this seemed like a fairly low level feature for the end user to be setting manually. For the coarse grain settings of extensions we could use vendor component of the triple, example

-triple=spirv64-arm for 64-bit SPIR-V compatible with Arm device family.

Defaults: unknown/generic SPIR-V with all supported extensions set. This is how SPIR-V is produced currently for OpenCL by default in clang.

Note that for the finer-grained control of extensions some new flags might need to be added in the long term. It would be good to gather some feedback to this in particular to get more clear picture of the requirements.

Overall, it would be good to reach an agreement on how to move forward with the SPIR-V target evolution to make sure that the interface is consistent by all languages and tools in the LLVM project.

[1] llvm-project/Triple.cpp at main · llvm/llvm-project · GitHub
[2] Cross-compilation using Clang — Clang 15.0.0git documentation
[3] OpenCL Support — Clang 15.0.0git documentation

1 Like

Thank you for the proposals. Based on my experience of contributing to DXC SPIR-V code gen, the proposals mostly sound good to me. One thing I want to clarify is the proposal about the SPIR-V extensions.

Background:

  • We have extensions specific to hardware vendors e.g., SPV_NV_ray_tracing.
  • We also have extensions not specific to hardware vendors e.g., SPV_KHR_ray_query
  • For HLSL, we can let the compiler deduce the required extensions based on the given HLSL code e.g., [[vk::shader_record_nv]] ConstantBuffer<S> srb; // use VK + NV specific attribute.
  • DXC provides a flag -fspv-extension= to allow users to explicitly enable extensions.

I want to clarify the expected behavior when we specify the vendor e.g., -triple=spirv64-arm. I think what the compiler has to do is

  1. Deduce all the required extensions based on the input shader.
  2. If we can use one of multiple extensions for a feature, choose the vendor specific one.
  3. If the user explicitly specifies some extensions via flags, enable them.

If you agree that the above is the expected behavior, we have to describe it in a document.

I would expect generic, cross-vendor extensions to be chosen with higher priority than vendor-specific ones. So, if there is only an ARM and an AMD extension for a given feature, and the triple is spirv64-arm, then choose the ARM extension. But if there is also an EXT or KHR extension for the feature, then extension should be used even if the triplpe is spirv64-arm or spirv64-amd.

Thanks! This is very insightful!

It seems the main difference b/w OpenCL and HLSL extension handling is that OpenCL provides a flag to set extensions of high-level language (via -cl-ext) and HLSL has a flag to set SPIR-V extensions (via -fspv-extensions). It is possible that in the future OpenCL will also need to set SPIR-V extensions then it would be good to use the same flag (I imagine it is planned to be added at some point).

FYI I have started this review that adds SPIR-V documentation (https://reviews.llvm.org/D124776) for the target triple. Would it be a good place for extensions too? Otherwise, we could also add info into clang’s UsersManual or just link SPIR-V page there… Particularly how the extensions are deduced could be valuable to document or perhaps even align… I am not sure how it’s currently done in SPIR-V generators for OpenCL so I will defer this answer to someone more experienced.