SPIR-V 目标用户指南¶
简介¶
SPIR-V 目标提供针对 SPIR-V 官方规范 中描述的 SPIR-V 二进制格式的代码生成。
用法¶
SPIR-V 后端可以通过 LLVM 的静态编译器 (llc) 或 Clang 调用,允许开发人员将 LLVM 中间语言 (IL) 文件或 OpenCL 内核源代码直接编译为 SPIR-V。本节概述了使用各种命令来利用 SPIR-V 后端实现不同目的的用法。
静态编译器命令¶
基本 SPIR-V 编译 命令:llc -mtriple=spirv32-unknown-unknown input.ll -o output.spvt 说明:此命令将 LLVM IL 文件 (input.ll) 编译为适用于 32 位架构的 SPIR-V 二进制文件 (output.spvt)。
带有扩展和优化的编译 命令:llc -O1 -mtriple=spirv64-unknown-unknown –spirv-ext=+SPV_INTEL_arbitrary_precision_integers input.ll -o output.spvt 说明:将 LLVM IL 文件编译为 SPIR-V,并进行 (-O1) 优化,面向 64 位架构。它启用了 SPV_INTEL_arbitrary_precision_integers 扩展。
使用实验性 NonSemantic.Shader.DebugInfo.100 支持进行编译 命令:llc –spv-emit-nonsemantic-debug-info –spirv-ext=+SPV_KHR_non_semantic_info input.ll -o output.spvt 说明:将 LLVM IL 文件编译为 SPIR-V,并添加额外的 NonSemantic.Shader.DebugInfo.100 指令。它启用了所需的 SPV_KHR_non_semantic_info 扩展。
SPIR-V 二进制文件生成 命令:llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj input.ll -o output.spvt 说明:从 LLVM 模块生成 SPIR-V 对象文件 (output.spvt),面向 64 位 SPIR-V 架构,不进行任何优化。
Clang 命令¶
SPIR-V 生成 命令:clang –target=spirv64 input.cl 说明:直接从 OpenCL 内核源文件 (input.cl) 生成 SPIR-V 文件。
编译器选项¶
目标三元组¶
对于交叉编译到 SPIR-V,使用选项
-target <Architecture><Subarchitecture>-<Vendor>-<OS>-<Environment>
指定目标三元组
¶ 架构
描述
spirv32
指针宽度为 32 位的 SPIR-V。
spirv64
指针宽度为 64 位的 SPIR-V。
spirv
具有逻辑内存布局的 SPIR-V。
¶ 子架构
描述
<空>
后端根据输入推断的 SPIR-V 版本。
v1.0
SPIR-V 版本 1.0。
v1.1
SPIR-V 版本 1.1。
v1.2
SPIR-V 版本 1.2。
v1.3
SPIR-V 版本 1.3。
v1.4
SPIR-V 版本 1.4。
v1.5
SPIR-V 版本 1.5。
v1.6
SPIR-V 版本 1.6。
¶ 供应商
描述
<空>/
unknown
没有任何供应商特定设置的通用 SPIR-V 目标。
amd
AMDGCN SPIR-V 目标,支持目标特定的内置函数和 ASM,旨在由 AMDGCN 工具链使用。
¶ 操作系统
描述
<空>/
unknown
默认为 OpenCL 运行时。
vulkan
Vulkan 着色器运行时。
vulkan1.2
Vulkan 1.2 运行时,对应于 SPIR-V 1.5。
vulkan1.3
Vulkan 1.3 运行时,对应于 SPIR-V 1.6。
amdhsa
AMDHSA 运行时,旨在用于 HSA 兼容运行时,对应于 SPIR-V 1.6。
¶ 环境
描述
<空>/
unknown
OpenCL 环境或后端根据输入推断的环境。
示例
-target spirv64v1.0
可用于编译指针宽度为 64 位的 SPIR-V 版本 1.0。
-target spirv64-amd-amdhsa
可用于编译指针宽度为 64 位的 AMDGCN 风格的 SPIR-V。
扩展¶
SPIR-V 后端支持各种 扩展,这些扩展可以启用或增强核心 SPIR-V 规范之外的功能。可以使用 -spirv-extensions
选项后跟要启用的扩展名称来启用这些扩展。以下是支持的 SPIR-V 扩展列表,按扩展名称的字母顺序排序
扩展名称 |
描述 |
---|---|
|
将 SPV_EXT_shader_atomic_float_add 扩展扩展到支持对内存中的 16 位浮点数进行原子加法。 |
|
添加对浮点数的原子加法指令。 |
|
添加对浮点数的原子最小值和最大值指令。 |
|
允许生成任意宽度的整数类型。 |
|
添加在单精度 32 位浮点数和 16 位 bfloat16 值之间进行转换的指令。 |
|
允许将缓存控制信息应用于内存访问指令。 |
|
允许转换函数指针。 |
|
允许使用内联汇编。 |
|
添加可以应用于全局(模块范围)变量的装饰。 |
|
添加可以应用于全局(模块范围)变量的装饰,以帮助 FPGA 设备的代码生成。 |
|
为 Function Control 掩码添加 OptNoneINTEL 值,指示请求不优化函数。 |
|
允许子组中的工作项共享数据,无需使用本地内存和工作组屏障,并利用专用硬件从图像或缓冲区加载和存储数据块。 |
|
引入了两个新的存储类,它们是 CrossWorkgroup 存储类的子类,提供可启用优化的其他信息。 |
|
允许分配在编译时元素数量未知的本地数组。 |
|
启用位指令供 SPIR-V 模块使用,而无需 Shader 功能。 |
|
向编译器提供其他信息,类似于 llvm.assume 和 llvm.expect 内联函数。 |
|
提供新的执行模式,通过覆盖实现的舍入模式、非规格化数、带符号零和无穷大的默认行为来控制浮点计算。 |
|
允许使用 LinkOnceODR 链接类型,该类型允许在发生链接时将函数或全局变量与具有相同名称的其他函数或全局变量合并。 |
|
添加装饰以指示给定指令不会导致整数溢出。 |
|
添加 cl_khr_kernel_clock 扩展,该扩展增加了内核对计算单元提供的时钟值进行采样的能力。 |
|
添加一个新的指令,该指令能够在子组内的调用之间旋转值。 |
|
允许支持在统一控制流中进行其他组操作。 |
|
增加了声明对模块没有语义影响且可以安全地从模块中删除的扩展指令集的能力。 |
要启用多个扩展,请用空格分隔它们列出。例如,要启用对浮点数和任意精度整数进行原子操作的支持,请使用
-spirv-ext=+SPV_EXT_shader_atomic_float_add,+SPV_INTEL_arbitrary_precision_integers
要启用所有扩展,请使用以下选项:-spirv-ext=all
要启用所有扩展,除了指定的扩展,请指定 all
后跟不允许的扩展列表。例如:-spirv-ext=all,-SPV_INTEL_arbitrary_precision_integers
LLVM IR 中的 SPIR-V 表示¶
SPIR-V 旨在与各种中间表示 (IR)(包括 LLVM IR)无缝集成,从而为其大多数实体提供简单的映射。SPIR-V 后端的开发遵循与 Khronos Group SPIR-V LLVM 翻译器 兼容的原则。因此,SPIR-V 后端接受的输入表示与 LLVM 中的 SPIR-V 表示文档 中详细说明的表示紧密一致。本文档以及后续部分阐明了要点,并重点关注此后端处理的 LLVM IR 与其他工具使用的约定之间的任何差异。
特殊类型¶
SPIR-V 指定了几种不透明类型。这些类型使用目标扩展类型表示,并按如下方式表示
¶ SPIR-V 类型
LLVM 类型名称
LLVM 类型参数
OpTypeImage
spirv.Image
采样类型,维度,深度,数组化,MS,采样,图像格式,访问限定符
OpTypeSampler
spirv.Sampler
(无)
OpTypeSampledImage
spirv.SampledImage
采样类型,维度,深度,数组化,MS,采样,图像格式,访问限定符
OpTypeEvent
spirv.Event
(无)
OpTypeDeviceEvent
spirv.DeviceEvent
(无)
OpTypeReserveId
spirv.ReserveId
(无)
OpTypeQueue
spirv.Queue
(无)
OpTypePipe
spirv.Pipe
访问限定符
OpTypePipeStorage
spirv.PipeStorage
(无)
所有整数参数都采用与其在相应的 SPIR-V 指令中相同的取值。例如,OpenCL 类型image2d_depth_ro_t
将在 SPIR-V IR 中表示为target("spirv.Image", void, 1, 1, 0, 0, 0, 0, 0)
,其维度参数为1
表示 2D。采样图像类型包括其底层图像类型的参数,因此先前类型的采样图像具有表示形式target("spirv.SampledImage, void, 1, 1, 0, 0, 0, 0, 0)
。
目标内联函数¶
SPIR-V 后端采用多个 LLVM IR 内联函数,这些内联函数有助于各种低级操作,这些操作对于生成正确且高效的 SPIR-V 代码至关重要。这些内联函数涵盖了从类型分配和内存管理到控制流和原子操作的一系列功能。以下是 SPIR-V 后端中使用的选定内联函数的详细表格,以及它们的描述和参数详细信息。
内联函数 ID |
返回值类型 |
参数类型 |
描述 |
---|---|---|---|
int_spv_assign_type |
无 |
[类型,元数据] |
将类型与元数据关联,这对于在 SPIR-V 结构中维护类型信息至关重要。不会直接发出,但内部支持类型系统。 |
int_spv_assign_ptr_type |
无 |
[类型,元数据,整数] |
类似于int_spv_assign_type,但用于指针类型,并带有指定存储类的附加整数。支持 SPIR-V 详细的指针类型系统。不会直接发出。 |
int_spv_assign_name |
无 |
[类型,可变参数] |
为类型或值分配名称,增强 SPIR-V 代码的可读性和可调试性。不会直接发出,但用于元数据丰富。 |
int_spv_assign_decoration |
无 |
[类型,元数据] |
通过将值与元数据关联来为值分配装饰。不会直接发出,但用于支持 LLVM IR 中的 SPIR-V 表示。 |
int_spv_track_constant |
类型 |
[类型,元数据] |
跟踪 SPIR-V 模块中的常量。对于优化和减少冗余至关重要。仅为内部使用而发出。 |
int_spv_init_global |
无 |
[类型,类型] |
初始化全局变量,这是确保 SPIR-V 中正确管理全局状态的必要步骤。仅为内部使用而发出。 |
int_spv_unref_global |
无 |
[类型] |
通过将全局变量标记为未引用来管理全局变量的生命周期,从而能够对全局变量使用进行优化。仅为内部使用而发出。 |
int_spv_gep |
指针 |
[布尔值,类型,可变参数] |
计算聚合类型子元素的地址。对于访问数组元素和结构体字段至关重要。以通用方式支持有条件地寻址元素。 |
int_spv_load |
32 位整数 |
[指针,16 位整数,8 位整数] |
从内存位置加载值。附加整数指定内存访问和对齐详细信息,对于确保正确的内存操作至关重要。 |
int_spv_store |
无 |
[类型,指针,16 位整数,8 位整数] |
将值存储到内存位置。与int_spv_load类似,它包括内存访问和对齐的规范,对于内存操作至关重要。 |
int_spv_extractv |
类型 |
[32 位整数,可变参数] |
从向量中提取值,允许在 SPIR-V 中进行向量运算。启用向量组件的操作。 |
int_spv_insertv |
32 位整数 |
[32 位整数,类型,可变参数] |
将值插入向量。作为int_spv_extractv的补充,它有助于构建和操作向量。 |
int_spv_extractelt |
类型 |
[类型,任何整数] |
根据索引从聚合类型中提取元素。对于数组和向量上的操作至关重要。 |
int_spv_insertelt |
类型 |
[类型,类型,任何整数] |
在指定索引处将元素插入聚合类型。允许构建和修改数组和向量。 |
int_spv_const_composite |
类型 |
[可变参数] |
从给定元素构造复合类型。从单个组件创建数组、结构体和向量是关键。 |
int_spv_bitcast |
类型 |
[类型] |
在类型之间执行按位转换。对于不更改位表示的类型转换至关重要。 |
int_spv_ptrcast |
类型 |
[类型,元数据,整数] |
在不同类型之间转换指针。类似于int_spv_bitcast,但专门用于指针,并考虑了 SPIR-V 的严格类型系统。 |
int_spv_switch |
无 |
[类型,可变参数] |
根据值实现多路分支。启用复杂的控制流结构,类似于高级语言中的 switch 语句。 |
int_spv_cmpxchg |
32 位整数 |
[类型,可变参数] |
执行原子比较和交换操作。对于计算着色器中的同步和并发控制至关重要。 |
int_spv_unreachable |
无 |
[] |
标记永远不应该到达的代码点,通过指示不可到达的代码路径来启用优化。 |
int_spv_alloca |
类型 |
[] |
在栈上分配内存。函数中局部变量存储的基础。 |
int_spv_alloca_array |
类型 |
[任何整数] |
在栈上分配数组。扩展int_spv_alloca以支持数组分配,对于临时数组至关重要。 |
int_spv_undef |
32 位整数 |
[] |
生成未定义的值。对于优化和指示未初始化的变量很有用。 |
int_spv_inline_asm |
无 |
[元数据,元数据,可变参数] |
通过创建元数据并将原始参数保留下来,将内联汇编功能与内联汇编调用实例关联。不会直接发出,但用于支持 LLVM IR 中的 SPIR-V 表示。 |
int_spv_assume |
无 |
[1 位整数] |
为优化器提供有关可以对程序状态做出的假设的提示。提高优化潜力。 |
int_spv_expect |
任何整数类型 |
[类型,类型] |
通过指示预期的分支路径来指导分支预测。通过优化常见代码路径来提高性能。 |
int_spv_thread_id |
32 位整数 |
[32 位整数] |
检索工作组中的线程 ID。对于识别并行计算操作中的执行上下文至关重要。 |
int_spv_create_handle |
指针 |
[8 位整数] |
为图形或计算资源创建资源句柄。促进着色器中资源的管理和使用。 |
内置函数¶
以下部分重点介绍了 LLVM IR 中 SPIR-V 内置函数的表示,重点介绍了在 LLVM 中没有直接对应项的内置函数。
指令作为函数调用¶
没有直接 LLVM 对应项的 SPIR-V 内置函数表示为 LLVM 函数调用。这些函数称为 SPIR-V 内置函数,遵循带有 SPIR-V 特定扩展的 IA64 命名方案。在某些情况下支持解析对内置函数的非命名调用,但未进行广泛测试。通用格式为
__spirv_{OpCodeName}{_OptionalPostfixes}
其中{OpCodeName} 是 SPIR-V 操作码名称,不带“Op”前缀,{OptionalPostfixes} 是装饰特定的后缀(如果有)。命名和后缀允许在 LLVM 的框架内表示 SPIR-V 丰富的指令集。
扩展指令集¶
SPIR-V 为其他功能(例如 OpenCL 特定操作)定义了多个扩展指令集。在 LLVM IR 中,这些表示为对命名内置函数的函数调用,并根据环境进行选择。例如
acos_f32
表示来自 OpenCL 扩展指令集的acos 函数,用于 float32 输入。
内置变量¶
SPIR-V 内置变量提供对特殊硬件或执行模型属性的访问,映射到 LLVM 函数调用或 LLVM 全局变量。表示遵循命名约定
__spirv_BuiltIn{VariableName}
例如,SPIR-V 内置函数GlobalInvocationId 在 LLVM IR 中可访问为__spirv_BuiltInGlobalInvocationId。
向量加载和存储内置函数¶
SPIR-V 加载和存储向量的功能在 LLVM IR 中使用模仿 SPIR-V 指令的函数表示。这些内置函数处理 LLVM 本机指令无法直接支持的情况,从而能够对内存操作进行细粒度控制。
原子操作¶
SPIR-V 的原子操作,特别是那些对浮点数据进行操作的原子操作,在 LLVM IR 中使用相应的函数调用表示。这些内置函数确保在 LLVM 可能没有直接支持的操作中的原子性,这对于并行执行和同步至关重要。
图像操作¶
SPIR-V 为图像和采样器操作提供了广泛的支持,LLVM 通过对内置函数的函数调用来表示这些操作。这些包括图像读取、写入和查询,允许对图像数据和参数进行详细的操作。
组和子组操作¶
对于工作组和子组操作,LLVM 使用函数调用来表示 SPIR-V 的基于组的指令。这些内置函数促进组同步、数据共享和集体操作,这些操作对于高效的并行计算至关重要。