KhronosGroup/SPIRV-LLVM

Capability Addresses is not allowed by Vulkan 1.0 specification

Closed this issue · 4 comments

Using LunarG's VulkanSDK=1.1.73.0, LLVM=spirv-3.6.1 and clang=spirv-1.0, I generated a SPIR-V binary from an OpenCL kernel and the disassembly looks like this:

; SPIR-V
; Version: 1.0
; Generator: Khronos LLVM/SPIR-V Translator; 14
; Bound: 18
; Schema: 0
               OpCapability Addresses
               OpCapability Linkage
               OpCapability Kernel
          %1 = OpExtInstImport "OpenCL.std"
               OpMemoryModel Physical32 OpenCL
               OpEntryPoint Kernel %9 "CopyBuffer"
               OpSource OpenCL_C 102000
               OpName %__spirv_BuiltInGlobalInvocationId "__spirv_BuiltInGlobalInvocationId"
               OpName %src "src"
               OpName %dst "dst"
               OpName %entry "entry"
               OpName %call "call"
               OpName %arrayidx "arrayidx"
               OpName %arrayidx1 "arrayidx1"
               OpDecorate %__spirv_BuiltInGlobalInvocationId BuiltIn GlobalInvocationId
               OpDecorate %__spirv_BuiltInGlobalInvocationId Constant
               OpDecorate %__spirv_BuiltInGlobalInvocationId LinkageAttributes "__spirv_BuiltInGlobalInvocationId" Import
       %uint = OpTypeInt 32 0
     %v3uint = OpTypeVector %uint 3
%_ptr_UniformConstant_v3uint = OpTypePointer UniformConstant %v3uint
       %void = OpTypeVoid
%_ptr_CrossWorkgroup_uint = OpTypePointer CrossWorkgroup %uint
          %8 = OpTypeFunction %void %_ptr_CrossWorkgroup_uint %_ptr_CrossWorkgroup_uint
%__spirv_BuiltInGlobalInvocationId = OpVariable %_ptr_UniformConstant_v3uint UniformConstant
          %9 = OpFunction %void None %8
        %src = OpFunctionParameter %_ptr_CrossWorkgroup_uint
        %dst = OpFunctionParameter %_ptr_CrossWorkgroup_uint
      %entry = OpLabel
         %13 = OpLoad %v3uint %__spirv_BuiltInGlobalInvocationId
       %call = OpCompositeExtract %uint %13 0
   %arrayidx = OpInBoundsPtrAccessChain %_ptr_CrossWorkgroup_uint %src %call
         %16 = OpLoad %uint %arrayidx Aligned 4
  %arrayidx1 = OpInBoundsPtrAccessChain %_ptr_CrossWorkgroup_uint %dst %call
               OpStore %arrayidx1 %16 Aligned 4
               OpReturn
               OpFunctionEnd

But trying to create a shader module using vkShaderCreateModule(), this shows up:

Validation(ERROR): msg_code: 5: Object: VK_NULL_HANDLE (Type = 0) | SPIR-V module not valid: Capability Addresses is not allowed by Vulkan 1.0 specification (or requires extension)

This seems strange as I can see Addresses listed under the Capabilities column in SPIR-V 1.0 (https://www.khronos.org/registry/spir-v/specs/1.0/SPIRV.html#Capability).

The vkEnumerateInstanceVersion() returns 1.1.73 so I am definitely above Vulkan 1.0 version as well.

Also, the function returns a VK_ERROR_INITIALIZATION_FAILED code which doesn't seem to be inline with possible return return codes listed here : https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCreateShaderModule.html.
This seems more of an issue with AMD's Vulkan implementation though.

Vulkan, OpenCL and OpenGL consume slightly different variants of SPIR-V: the SPIR-V specification defines the core SPIR-V, and then Vulkan, OpenCL and OpenGL specifications each have different restrictions on which features of SPIR-V they allow. For example, in OpenCL, physical pointers are allowed, so the memory model has to be OpMemoryModel Physical32 OpenCL (or Physical64), whereas this is not the case in Vulkan (and OpenGL), where there, you have to define it as OpMemoryModel Logical GLSL450.

For example, if you want to see which subset of SPIR-V is allowed in OpenCL, you should have a look at https://www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_Env.html.

So you can’t generate an OpenCL SPIR-V and give it to Vulkan, nor vice-versa. However, you can compile OpenCL kernels to Vulkan SPIR-V using https://github.com/google/clspv.

Thanks a lot for clearing that up. I had my doubts regarding running OpenCL kernels on Vulkan but thought I'd give it a try anyways. It would be nice if the SPIR-V spec was more explicit about this stuff.

Going to try clspv now, thanks for the tip. Hopefully, this SPIR-V variability will be addressed by extensions in the future.

It is mentioned in Section 2.1 of the SPIR-V specification, that client API specifications might have specific restrictions:

An execution environment is allowed to reject modules declaring capabilities it does not support. (See client API specifications for environment-specific rules.)

but I agree that it’s easy to miss it, and it’s mainly talking about capabilities.

Hopefully, this SPIR-V variability will be addressed by extensions in the future.

I’m not entirely sure, but I think this variability was part of the design for SPIR-V, to have a single core intermediate language, that can then be extended to suit various programming languages.
So for example, most of the math functions like cos and such, or instructions that are language specific like mad24 for OpenCL, are defined in specific extended instruction sets, with both OpenCL and OpenGL/Vukan having one.

You can see in your SPIR-V module, that the OpenCL extended instruction set is imported, %1 = OpExtInstImport "OpenCL.std", though it doesn’t look like it is being used.

My comment wasn't about differences between languages in general but more for OpenCL and Vulkan compatibility. All signs lead to them being merged in the future with kernel differences being settled at SPIR-V using extensions. Khronos has been pretty vocal about it lately.