SPIR-V 目標使用者指南

簡介

SPIR-V 目標為 官方 SPIR-V 規格中描述的 SPIR-V 二進制格式提供程式碼生成。

使用方式

SPIR-V 後端可以從 LLVM 的靜態編譯器 (llc) 或 Clang 調用,允許開發人員將 LLVM 中間語言 (IL) 檔案或 OpenCL 核心來源直接編譯為 SPIR-V。本節概述了使用各種命令來針對不同目的利用 SPIR-V 後端。

靜態編譯器命令

  1. 基本 SPIR-V 編譯 命令: llc -mtriple=spirv32-unknown-unknown input.ll -o output.spvt 描述:此命令將 LLVM IL 檔案 (input.ll) 編譯為適用於 32 位元架構的 SPIR-V 二進制檔案 (output.spvt)。

  2. 使用擴充功能和優化的編譯 命令: llc -O1 -mtriple=spirv64-unknown-unknown –spirv-ext=+SPV_INTEL_arbitrary_precision_integers input.ll -o output.spvt 描述:使用 (-O1) 優化將 LLVM IL 檔案編譯為 SPIR-V,目標為 64 位元架構。它啟用了 SPV_INTEL_arbitrary_precision_integers 擴充功能。

  3. 使用實驗性的 NonSemantic.Shader.DebugInfo.100 支援進行編譯 命令: llc –spv-emit-nonsemantic-debug-info –spirv-ext=+SPV_KHR_non_semantic_info input.ll -o output.spvt 描述:使用額外的 NonSemantic.Shader.DebugInfo.100 指令將 LLVM IL 檔案編譯為 SPIR-V。它啟用所需的 SPV_KHR_non_semantic_info 擴充功能。

  4. SPIR-V 二進制檔案生成 命令: llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj input.ll -o output.spvt 描述:從 LLVM 模組生成 SPIR-V 物件檔案 (output.spvt),目標為 64 位元 SPIR-V 架構,且不進行優化。

Clang 命令

  1. SPIR-V 生成 命令: clang –target=spirv64 input.cl 描述:直接從 OpenCL 核心來源檔案 (input.cl) 生成 SPIR-V 檔案。

編譯器選項

目標三元組

若要交叉編譯為 SPIR-V,請使用選項

-target <架構><子架構>-<供應商>-<作業系統>-<環境>

以指定目標三元組

表 120 SPIR-V 架構

架構

描述

spirv32

具有 32 位元指標寬度的 SPIR-V。

spirv64

具有 64 位元指標寬度的 SPIR-V。

spirv

具有邏輯記憶體佈局的 SPIR-V。

表 121 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。

表 122 SPIR-V 供應商

供應商

描述

<空白>/unknown

不含任何供應商特定設定的通用 SPIR-V 目標。

amd

AMDGCN SPIR-V 目標,支援目標特定的內建函數和 ASM,旨在由 AMDGCN 工具鏈使用。

表 123 作業系統

作業系統

描述

<空白>/unknown

預設為 OpenCL 執行階段。

vulkan

Vulkan shader 執行階段。

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。

表 124 SPIR-V 環境

環境

描述

<空白>/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 擴充功能列表,依擴充功能名稱字母順序排序

表 125 支援的 SPIR-V 擴充功能

擴充功能名稱

描述

SPV_EXT_arithmetic_fence

新增一個指令,以防止在其引數和包含它的表達式之間進行快速數學優化。

SPV_EXT_demote_to_helper_invocation

新增一個指令,將片段著色器調用降級為輔助調用。

SPV_EXT_optnone

為函數控制遮罩新增 OptNoneEXT 值,指示不優化函數的請求。

SPV_EXT_shader_atomic_float16_add

擴充 SPV_EXT_shader_atomic_float_add 擴充功能,以支援原子性地加總記憶體中 16 位元浮點數。

SPV_EXT_shader_atomic_float_add

在浮點數上新增原子加法指令。

SPV_EXT_shader_atomic_float_min_max

在浮點數上新增原子最小值和最大值指令。

SPV_INTEL_arbitrary_precision_integers

允許生成任意寬度的整數類型。

SPV_INTEL_bindless_images

新增指令以將無符號整數句柄轉換為影像、取樣器和取樣影像。

SPV_INTEL_bfloat16_conversion

新增指令以在單精度 32 位元浮點值和 16 位元 bfloat16 值之間進行轉換。

SPV_INTEL_cache_controls

允許將快取控制資訊應用於記憶體存取指令。

SPV_INTEL_float_controls2

新增執行模式和裝飾,以控制浮點運算。

SPV_INTEL_function_pointers

允許轉換函數指標。

SPV_INTEL_inline_assembly

允許使用內聯組譯碼。

SPV_INTEL_global_variable_host_access

新增可以應用於全域(模組範圍)變數的裝飾。

SPV_INTEL_global_variable_fpga_decorations

新增可以應用於全域(模組範圍)變數的裝飾,以協助 FPGA 裝置的程式碼生成。

SPV_INTEL_media_block_io

新增額外的子群組區塊讀取和寫入功能,允許應用程式彈性地指定要從 2D 影像讀取或寫入的區塊寬度和高度。

SPV_INTEL_memory_access_aliasing

新增指令和裝飾,以指定記憶體存取別名,類似於 alias.scope 和 noalias LLVM 元數據。

SPV_INTEL_optnone

為函數控制遮罩新增 OptNoneINTEL 值,指示不優化函數的請求。

SPV_INTEL_split_barrier

新增 SPIR-V 指令,以將控制屏障拆分為兩個獨立的操作:第一個表示調用已「到達」屏障但應繼續執行,第二個表示調用應「等待」其他調用到達屏障,然後再進一步執行。

SPV_INTEL_subgroups

允許子群組中的工作項目在不使用本機記憶體和工作群組屏障的情況下共用資料,並利用專用硬體從影像或緩衝區載入和儲存資料區塊。

SPV_INTEL_usm_storage_classes

引入兩個新的儲存類別,它們是 CrossWorkgroup 儲存類別的子類別,提供可啟用優化的其他資訊。

SPV_INTEL_variable_length_array

允許配置在編譯時元素數量未知的本機陣列。

SPV_INTEL_joint_matrix

在 SPV_KHR_cooperative_matrix 擴充功能之上新增一些矩陣功能,例如矩陣預取、取得元素座標和檢查的載入/儲存/建構指令、張量浮點數 32 和 bfloat 類型用於乘法-加法指令的解譯。

SPV_KHR_bit_instructions

允許 SPIR-V 模組使用位元指令,而無需 Shader 功能。

SPV_KHR_expect_assume

向編譯器提供額外資訊,類似於 llvm.assume 和 llvm.expect 內建函數。

SPV_KHR_float_controls

提供新的執行模式來控制浮點運算,方法是覆寫實作的捨入模式、次正规数、帶符號零和無窮大的預設行為。

SPV_KHR_integer_dot_product

新增用於整數向量點積運算的指令,並具有可選的累加。整數向量包括 8 位元整數的 4 元件向量和封裝到 32 位元整數中的 8 位元整數的 4 元件向量。

SPV_KHR_linkonce_odr

允許使用 LinkOnceODR 連結類型,該類型允許在發生連結時,將函數或全域變數與相同名稱的其他函數或全域變數合併。

SPV_KHR_no_integer_wrap_decoration

新增裝飾以指示給定的指令不會導致整數換行。

SPV_KHR_shader_clock

新增擴充功能 cl_khr_kernel_clock,該擴充功能新增了核心從計算單元提供的時脈取樣值的能力。

SPV_KHR_subgroup_rotate

新增一個新的指令,可啟用在子群組內跨調用旋轉值。

SPV_KHR_uniform_group_instructions

允許在統一控制流程中支援額外的群組操作。

SPV_KHR_non_semantic_info

新增宣告對語意沒有影響且可以安全地從模組中移除的擴充指令集的能力。

若要啟用多個擴充功能,請以逗號分隔列出它們。例如,若要啟用對浮點數和任意精度整數的原子操作的支援,請使用

-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 Translator 相容的原則為指導。因此,SPIR-V 後端接受的輸入表示法與 LLVM 文件中 SPIR-V 表示法中詳述的表示法非常一致。本文件以及後續章節,將概述要點,並重點介紹此後端處理的 LLVM IR 與其他工具使用的慣例之間的任何差異。

特殊類型

SPIR-V 指定了幾種不透明類型。這些類型使用目標擴充類型表示,並表示如下

表 126 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 後端中使用的選定內建函數的詳細表格,以及其描述和引數詳細資訊。

表 127 適用於 SPIR-V 的 LLVM IR 內建函數

內建函數 ID

傳回類型

引數類型

描述

int_spv_assign_type

[類型、元數據]

將類型與元數據關聯,這對於在 SPIR-V 結構中維護類型資訊至關重要。不會直接發出,但會在內部支援類型系統。

int_spv_assign_ptr_type

[類型、元數據、整數]

int_spv_assign_type 類似,但適用於指標類型,並具有額外的整數來指定儲存類別。支援 SPIR-V 的詳細指標類型系統。不會直接發出。

int_spv_assign_name

[類型、Vararg]

將名稱指派給類型或值,增強 SPIR-V 程式碼的可讀性和可除錯性。不會直接發出,但用於元數據擴充。

int_spv_value_md

[元數據]

將一組屬性(例如名稱和資料類型)指派給作為關聯的 llvm.fake.use 內建函數呼叫的引數的值。後者用作用於將 IRTranslator 建立的虛擬暫存器對應到原始值的手段。

int_spv_assign_decoration

[類型、元數據]

透過將裝飾與元數據關聯,將裝飾指派給值。不會直接發出,但用於支援 LLVM IR 中的 SPIR-V 表示法。

int_spv_assign_aliasing_decoration

[類型、32 位元整數、元數據]

使用原始別名元數據節點,將兩個記憶體別名裝飾之一(由第二個引數指定)指派給指令。不會直接發出,但用於支援 LLVM IR 中的 SPIR-V 表示法。

int_spv_track_constant

類型

[類型、元數據]

追蹤 SPIR-V 模組中的常數。對於優化和減少冗餘至關重要。僅針對內部使用發出。

int_spv_init_global

[類型、類型]

初始化全域變數,這是確保 SPIR-V 中正確全域狀態管理的必要步驟。僅針對內部使用發出。

int_spv_unref_global

[類型]

透過將全域變數標記為未參考來管理全域變數的生命週期,從而啟用與全域變數使用相關的優化。僅針對內部使用發出。

int_spv_gep

指標

[布林值、類型、Vararg]

計算聚合類型的子元素的位址。對於存取陣列元素和結構欄位至關重要。支援以通用方式有條件地定址元素。

int_spv_load

32 位元整數

[指標、16 位元整數、8 位元整數]

從記憶體位置載入值。額外的整數指定記憶體存取和對齊詳細資訊,這對於確保正確且有效率的記憶體操作至關重要。

int_spv_store

[類型、指標、16 位元整數、8 位元整數]

將值儲存到記憶體位置。與 int_spv_load 類似,它包含記憶體存取和對齊的規格,這對於記憶體操作至關重要。

int_spv_extractv

類型

[32 位元整數、Vararg]

從向量中擷取值,允許在 SPIR-V 中進行向量操作。啟用向量元件的操控。

int_spv_insertv

32 位元整數

[32 位元整數、類型、Vararg]

將值插入向量中。與 int_spv_extractv 互補,它有助於向量的建構和操控。

int_spv_extractelt

類型

[類型、任何整數]

根據索引從聚合類型中擷取元素。對於陣列和向量上的操作至關重要。

int_spv_insertelt

類型

[類型、類型、任何整數]

在指定索引處將元素插入聚合類型中。允許建置和修改陣列和向量。

int_spv_const_composite

類型

[Vararg]

從給定元素建構複合類型。從個別元件建立陣列、結構和向量的關鍵。

int_spv_bitcast

類型

[類型]

在類型之間執行逐位元轉換。對於不變更位元表示的類型轉換至關重要。

int_spv_ptrcast

類型

[類型、元數據、整數]

在不同類型之間轉換指標。與 int_spv_bitcast 類似,但專門用於指標,並考慮到 SPIR-V 的嚴格類型系統。

int_spv_switch

[類型、Vararg]

根據值實作多向分支。啟用複雜的控制流程結構,類似於高階語言中的 switch 語句。

int_spv_cmpxchg

32 位元整數

[類型、Vararg]

執行原子比較和交換操作。對於計算著色器中的同步和並行控制至關重要。

int_spv_unreachable

[]

標記程式碼中不應到達的點,透過指示無法到達的程式碼路徑來啟用優化。

int_spv_alloca

類型

[]

在堆疊上配置記憶體。函數中本機變數儲存的基本要素。

int_spv_alloca_array

類型

[任何整數]

在堆疊上配置陣列。擴充 int_spv_alloca

int_spv_undef

32 位元整數

[]

產生未定義的值。適用於優化並指示未初始化的變數。

int_spv_inline_asm

[元數據、元數據、Vararg]

透過建立元數據並保留原始引數,將內聯組譯碼功能與內聯組譯碼呼叫實例關聯。不會直接發出,但用於支援 LLVM IR 中的 SPIR-V 表示法。

int_spv_assume

[1 位元整數]

提供關於可以對程式狀態做出的假設的提示給優化器。提升優化潛力。

int_spv_expect

任何整數類型

[類型、類型]

透過指示預期的分支路徑來引導分支預測。透過優化常見的程式碼路徑來提升效能。

int_spv_thread_id

32 位元整數

[32 位元整數]

擷取工作群組中的執行緒 ID。對於識別並行計算操作中的執行環境至關重要。

int_spv_create_handle

指標

[8 位元整數]

為圖形或計算資源建立資源句柄。有助於著色器中資源的管理和使用。

int_spv_resource_handlefrombinding

spirv.Image

[32 位元整數集、32 位元整數綁定、32 位元整數 arraySize、32 位元整數索引、布林值 isUniformIndex]

傳回給定集合和綁定處資源的句柄。如果 arraySize > 1,則綁定表示給定大小的資源陣列,並傳回給定索引處資源的句柄。如果索引可能為非統一,則必須將 isUniformIndex 設定為 true。

int_spv_typeBufferLoad

純量或向量

[spirv.Image ImageBuffer、32 位元整數座標]

從給定座標處的 Vulkan 影像緩衝區載入值。影像緩衝區資料假設儲存為 4 元素向量。如果傳回類型是純量,則傳回向量的第一個元素。如果傳回類型是 n 元素向量,則傳回 4 元素向量的前 n 個元素。

int_spv_resource_store_typedbuffer

void

[spirv.Image Image、32 位元整數座標、vec4 資料]

將資料儲存到給定座標處的影像緩衝區。資料必須是 4 元素向量。

內建函數

以下章節重點介紹 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

表示來自 float32 輸入的 OpenCL 擴充指令集中的 acos 函數。

內建變數

SPIR-V 內建變數(提供對特殊硬體或執行模型屬性的存取權)會對應到 LLVM 函數呼叫或 LLVM 全域變數。表示法遵循命名慣例

__spirv_BuiltIn{VariableName}

例如,SPIR-V 內建函數 GlobalInvocationId 在 LLVM IR 中可以作為 __spirv_BuiltInGlobalInvocationId 存取。

向量載入和儲存內建函數

LLVM IR 中使用模仿 SPIR-V 指令的函數來表示 SPIR-V 的向量載入和儲存功能。這些內建函數處理 LLVM 的原生指令不直接支援的情況,從而可以精細地控制記憶體操作。

原子操作

SPIR-V 的原子操作,尤其是對浮點資料進行操作的原子操作,在 LLVM IR 中以對應的函數呼叫表示。這些內建函數確保 LLVM 可能沒有直接支援的操作中的原子性,這對於並行執行和同步至關重要。

影像操作

SPIR-V 提供對影像和取樣器操作的廣泛支援,LLVM 透過對內建函數的函數呼叫來表示這些操作。這些包括影像讀取、寫入和查詢,允許詳細操控影像資料和參數。

群組和子群組操作

對於工作群組和子群組操作,LLVM 使用函數呼叫來表示 SPIR-V 的基於群組的指令。這些內建函數有助於群組同步、資料共用和對於有效率的並行計算至關重要的集體操作。