AMDGPU 后端用户指南¶
简介¶
AMDGPU 后端为 AMD GPU 提供 ISA 代码生成,从 R600 系列到当前的 GCN 系列。它位于 llvm/lib/Target/AMDGPU
目录中。
LLVM¶
目标三元组¶
使用 Clang 选项 -target <架构>-<供应商>-<操作系统>-<环境>
来指定目标三元组
表 18 AMDGPU 架构¶ 架构
描述
r600
AMD GPU HD2XXX-HD6XXX,用于图形和计算着色器。
amdgcn
AMD GPU GCN GFX6 及更高版本,用于图形和计算着色器。
表 19 AMDGPU 供应商¶ 供应商
描述
amd
可用于所有 AMD GPU 用途。
mesa
如果操作系统为
mesa3d
,则可以使用。
表 20 AMDGPU 操作系统¶ 操作系统
描述
<空>
默认为未知操作系统。
amdhsa
在 HSA [HSA] 兼容运行时(例如
AMD 的 ROCm™ 运行时 [AMD-ROCm])上执行的计算内核,在 Linux 上使用 rocm-amdhsa 加载器。 有关支持的硬件和软件,请参阅AMD ROCm 平台发行说明 [AMD-ROCm-Release-Notes]。
AMD 的 PAL 运行时,在 Windows 上使用 pal-amdhsa 加载器。
amdpal
在 AMD 的 PAL 运行时上执行的图形着色器和计算内核,在 Windows 和 Linux Pro 上使用 pal-amdpal 加载器。
mesa3d
在 AMD 的 Mesa 3D 运行时上执行的图形着色器和计算内核,在 Linux 上使用 mesa-mesa3d 加载器。
表 21 AMDGPU 环境¶ 环境
描述
<空>
默认。
处理器¶
使用 Clang 选项 -mcpu=<target-id>
或 --offload-arch=<target-id>
来指定 AMDGPU 处理器以及可选的目标特性。 有关 AMD GPU 目标特定信息,请参阅 目标 ID 和 目标特性。
每个处理器都支持每个 OS ABI(参见 AMDGPU 操作系统),但以下情况除外
amdhsa
在r600
架构中不受支持(参见 AMDGPU 架构)。表 22 AMDGPU 处理器¶ 处理器
备选处理器
目标三元组架构
dGPU/APU
支持的目标特性
目标属性
操作系统支持(有关当前信息和支持级别,请参阅 amdgpu-os 和相应的运行时发行说明)
示例产品
Radeon HD 2000/3000 系列 (R600) [AMD-RADEON-HD-2000-3000]
r600
r600
dGPU
不支持通用地址空间
r630
r600
dGPU
不支持通用地址空间
rs880
r600
dGPU
不支持通用地址空间
rv670
r600
dGPU
不支持通用地址空间
Radeon HD 4000 系列 (R700) [AMD-RADEON-HD-4000]
rv710
r600
dGPU
不支持通用地址空间
rv730
r600
dGPU
不支持通用地址空间
rv770
r600
dGPU
不支持通用地址空间
Radeon HD 5000 系列 (Evergreen) [AMD-RADEON-HD-5000]
cedar
r600
dGPU
不支持通用地址空间
cypress
r600
dGPU
不支持通用地址空间
juniper
r600
dGPU
不支持通用地址空间
redwood
r600
dGPU
不支持通用地址空间
sumo
r600
dGPU
不支持通用地址空间
Radeon HD 6000 系列 (Northern Islands) [AMD-RADEON-HD-6000]
barts
r600
dGPU
不支持通用地址空间
caicos
r600
dGPU
不支持通用地址空间
cayman
r600
dGPU
不支持通用地址空间
turks
r600
dGPU
不支持通用地址空间
GCN GFX6 (Southern Islands (SI)) [AMD-GCN-GFX6]
gfx600
tahiti
amdgcn
dGPU
不支持通用地址空间
pal-amdpal
gfx601
pitcairn
verde
amdgcn
dGPU
不支持通用地址空间
pal-amdpal
gfx602
hainan
oland
amdgcn
dGPU
不支持通用地址空间
pal-amdpal
GCN GFX7 (Sea Islands (CI)) [AMD-GCN-GFX7]
gfx700
kaveri
amdgcn
APU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
A6-7000
A6 Pro-7050B
A8-7100
A8 Pro-7150B
A10-7300
A10 Pro-7350B
FX-7500
A8-7200P
A10-7400P
FX-7600P
gfx701
hawaii
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
FirePro W8100
FirePro W9100
FirePro S9150
FirePro S9170
gfx702
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon R9 290
Radeon R9 290x
Radeon R390
Radeon R390x
gfx703
kabini
mullins
amdgcn
APU
偏移 flat scratch
pal-amdhsa
pal-amdpal
E1-2100
E1-2200
E1-2500
E2-3000
E2-3800
A4-5000
A4-5100
A6-5200
A4 Pro-3340B
gfx704
bonaire
amdgcn
dGPU
偏移 flat scratch
pal-amdhsa
pal-amdpal
Radeon HD 7790
Radeon HD 8770
R7 260
R7 260X
gfx705
amdgcn
APU
偏移 flat scratch
pal-amdhsa
pal-amdpal
待定
GCN GFX8 (Volcanic Islands (VI)) [AMD-GCN-GFX8]
gfx801
carrizo
amdgcn
APU
xnack
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
A6-8500P
Pro A6-8500B
A8-8600P
Pro A8-8600B
FX-8800P
Pro A12-8800B
A10-8700P
Pro A10-8700B
A10-8780P
A10-9600P
A10-9630P
A12-9700P
A12-9730P
FX-9800P
FX-9830P
E2-9010
A6-9210
A9-9410
gfx802
iceland
tonga
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon R9 285
Radeon R9 380
Radeon R9 385
gfx803
fiji
amdgcn
dGPU
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon R9 Nano
Radeon R9 Fury
Radeon R9 FuryX
Radeon Pro Duo
FirePro S9300x2
Radeon Instinct MI8
polaris10
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 470
Radeon RX 480
Radeon Instinct MI6
polaris11
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 460
gfx805
tongapro
amdgcn
dGPU
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
FirePro S7150
FirePro S7100
FirePro W7100
Mobile FirePro M7170
gfx810
stoney
amdgcn
APU
xnack
偏移 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
待定
GCN GFX9 (Vega) [AMD-GCN-GFX900-GFX904-VEGA] [AMD-GCN-GFX906-VEGA7NM] [AMD-GCN-GFX908-CDNA1] [AMD-GCN-GFX90A-CDNA2] [AMD-GCN-GFX942-CDNA3]
gfx900
amdgcn
dGPU
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon Vega Frontier Edition
Radeon RX Vega 56
Radeon RX Vega 64
Radeon RX Vega 64 Liquid
Radeon Instinct MI25
gfx902
amdgcn
APU
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Ryzen 3 2200G
Ryzen 5 2400G
gfx904
amdgcn
dGPU
xnack
rocm-amdhsa
pal-amdhsa
pal-amdpal
待定
gfx906
amdgcn
dGPU
sramecc
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon Instinct MI50
Radeon Instinct MI60
Radeon VII
Radeon Pro VII
gfx908
amdgcn
dGPU
sramecc
xnack
绝对 flat scratch
rocm-amdhsa
AMD Instinct MI100 加速器
gfx909
amdgcn
APU
xnack
绝对 flat scratch
pal-amdpal
待定
gfx90a
amdgcn
dGPU
sramecc
tgsplit
xnack
kernarg 预加载(MI210 除外)
绝对 flat scratch
打包的工作项 ID
rocm-amdhsa
rocm-amdhsa
rocm-amdhsa
AMD Instinct MI210 加速器
AMD Instinct MI250 加速器
AMD Instinct MI250X 加速器
gfx90c
amdgcn
APU
xnack
绝对 flat scratch
pal-amdpal
Ryzen 7 4700G
Ryzen 7 4700GE
Ryzen 5 4600G
Ryzen 5 4600GE
Ryzen 3 4300G
Ryzen 3 4300GE
Ryzen Pro 4000G
Ryzen 7 Pro 4700G
Ryzen 7 Pro 4750GE
Ryzen 5 Pro 4650G
Ryzen 5 Pro 4650GE
Ryzen 3 Pro 4350G
Ryzen 3 Pro 4350GE
gfx942
amdgcn
dGPU
sramecc
tgsplit
xnack
kernarg 预加载
架构化 flat scratch
打包的工作项 ID
AMD Instinct MI300X
AMD Instinct MI300A
gfx950
amdgcn
dGPU
sramecc
tgsplit
xnack
kernarg 预加载
架构化 flat scratch
打包的工作项 ID
待定
GCN GFX10.1 (RDNA 1) [AMD-GCN-GFX10-RDNA1]
gfx1010
amdgcn
dGPU
cumode
wavefrontsize64
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 5700
Radeon RX 5700 XT
Radeon Pro 5600 XT
Radeon Pro 5600M
gfx1011
amdgcn
dGPU
cumode
wavefrontsize64
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon Pro V520
gfx1012
amdgcn
dGPU
cumode
wavefrontsize64
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 5500
Radeon RX 5500 XT
gfx1013
amdgcn
APU
cumode
wavefrontsize64
xnack
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
待定
GCN GFX10.3 (RDNA 2) [AMD-GCN-GFX10-RDNA2]
gfx1030
amdgcn
dGPU
cumode
wavefrontsize64
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 6800
Radeon RX 6800 XT
Radeon RX 6900 XT
Radeon PRO W6800
Radeon PRO V620
gfx1031
amdgcn
dGPU
cumode
wavefrontsize64
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
Radeon RX 6700 XT
gfx1032
amdgcn
dGPU
cumode
wavefrontsize64
绝对 flat scratch
rocm-amdhsa
pal-amdhsa
pal-amdpal
待定
gfx1033
amdgcn
APU
cumode
wavefrontsize64
绝对 flat scratch
pal-amdpal
待定
gfx1034
amdgcn
dGPU
cumode
wavefrontsize64
绝对 flat scratch
pal-amdpal
待定
gfx1035
amdgcn
APU
cumode
wavefrontsize64
绝对 flat scratch
pal-amdpal
待定
gfx1036
amdgcn
APU
cumode
wavefrontsize64
绝对 flat scratch
pal-amdpal
待定
GCN GFX11 (RDNA 3) [AMD-GCN-GFX11-RDNA3]
gfx1100
amdgcn
dGPU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
pal-amdpal
Radeon PRO W7900 Dual Slot
Radeon PRO W7900
Radeon PRO W7800
Radeon RX 7900 XTX
Radeon RX 7900 XT
Radeon RX 7900 GRE
gfx1101
amdgcn
dGPU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1102
amdgcn
dGPU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1103
amdgcn
APU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
GCN GFX11 (RDNA 3.5) [AMD-GCN-GFX11-RDNA3.5]
gfx1150
amdgcn
APU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1151
amdgcn
APU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1152
amdgcn
APU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1153
amdgcn
APU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1200
amdgcn
dGPU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
gfx1201
amdgcn
dGPU
cumode
wavefrontsize64
架构化 flat scratch
打包的工作项 ID
待定
通用处理器允许在它支持的任何处理器上执行单个代码对象。此类代码对象的性能可能不如非通用处理器。
通用处理器仅在代码对象 V6 及更高版本上可用(参见 ELF 代码对象)。
通用处理器代码对象是版本化的。 有关版本控制如何工作的更多信息,请参阅 通用处理器版本控制。
表 23 AMDGPU 通用处理器¶ 处理器
目标三元组架构
支持的处理器
支持的目标特性
目标属性
目标限制
gfx9-generic
amdgcn
gfx900
gfx902
gfx904
gfx906
gfx909
gfx90c
xnack
绝对 flat scratch
v_mad_mix
指令在gfx900
、gfx902
、gfx909
、gfx90c
上不可用
v_fma_mix
指令在gfx904
上不可用sramecc 在
gfx906
上不可用以下指令在
gfx906
上不可用
v_fmac_f32
v_xnor_b32
v_dot4_i32_i8
v_dot8_i32_i4
v_dot2_i32_i16
v_dot2_u32_u16
v_dot4_u32_u8
v_dot8_u32_u4
v_dot2_f32_f16
gfx9-4-generic
amdgcn
gfx942
gfx950
sramecc
tgsplit
xnack
kernarg 预加载
架构化 flat scratch
打包的工作项 ID
FP8 和 BF8 指令、FP8 和 BF8 转换指令以及具有 XF32 格式支持的指令不可用。
gfx10-1-generic
amdgcn
gfx1010
gfx1011
gfx1012
gfx1013
xnack
wavefrontsize64
cumode
绝对 flat scratch
以下指令在
gfx1011
和gfx1012
上不可用
v_dot4_i32_i8
v_dot8_i32_i4
v_dot2_i32_i16
v_dot2_u32_u16
v_dot2c_f32_f16
v_dot4c_i32_i8
v_dot4_u32_u8
v_dot8_u32_u4
v_dot2_f32_f16
BVH 光线追踪指令在
gfx1013
上不可用
gfx10-3-generic
amdgcn
gfx1030
gfx1031
gfx1032
gfx1033
gfx1034
gfx1035
gfx1036
wavefrontsize64
cumode
绝对 flat scratch
无限制。
gfx11-generic
amdgcn
gfx1100
gfx1101
gfx1102
gfx1103
gfx1150
gfx1151
gfx1152
gfx1153
wavefrontsize64
cumode
架构化 flat scratch
打包的工作项 ID
应用各种代码生成悲观优化,以解决此系列中某些目标特有的一些风险。
并非所有 VGPR 都可以在以下项上使用
gfx1100
gfx1101
gfx1151
SALU 浮点指令在以下项上不可用
gfx1150
gfx1151
gfx1152
gfx1153
SGPR 在 dpp 指令中不支持用于 src1,用于
gfx1150
gfx1151
gfx1152
gfx1153
gfx12-generic
amdgcn
gfx1200
gfx1201
wavefrontsize64
cumode
架构化 flat scratch
打包的工作项 ID
无限制。
通用处理器版本控制¶
通用处理器(参见 AMDGPU 通用处理器)代码对象在 1 到 255 之间进行版本控制(参见 代码对象 V6 及更高版本的 AMDGPU ELF 头部 e_flags)。非通用代码对象的版本始终设置为 0。
对于通用代码对象,添加新的受支持处理器可能需要更改为通用目标生成的代码,以便它可以继续在以前支持的处理器以及新的处理器上执行。当发生这种情况时,通用代码对象版本号会在更新通用目标的同时递增。
通用目标的每个受支持处理器都映射到引入它的版本。如果正在加载的代码对象的版本大于或等于将处理器添加到通用目标中的版本,则通用代码对象可以在受支持的处理器上执行。
目标特性¶
目标特性控制如何生成代码以支持某些处理器特定功能。并非所有处理器都支持所有目标特性。运行时必须确保用于执行代码的设备支持的特性与生成代码时启用的特性相匹配。特性不匹配可能会导致执行不正确或性能下降。
每个处理器支持的目标特性在 处理器 中列出。
目标特性由以下 Clang 选项之一精确控制
-mcpu=<target-id>
或 --offload-arch=<target-id>
-mcpu
和--offload-arch
可以将目标特性指定为目标 ID 的可选组件。如果省略,则目标特性具有any
值。参见 目标 ID。
-m[no-]<target-feature>
未由目标 ID 指定的目标特性使用单独的选项指定。这些目标特性可以具有
on
或off
值。on
通过省略no-
前缀来指定,而off
通过包含no-
前缀来指定。如果未指定,则默认为off
。
例如
-mcpu=gfx908:xnack+
启用
xnack
特性。-mcpu=gfx908:xnack-
禁用
xnack
特性。-mcumode
启用
cumode
特性。-mno-cumode
禁用
cumode
特性。表 24 AMDGPU 目标特性¶ 目标特性
用于控制的 Clang 选项
描述
名称
cumode
-m[no-]cumode
控制为内核生成代码时使用的工作组执行模式。禁用时使用原生 WGP 工作组执行模式,启用时使用 CU 工作组执行模式(参见 内存模型)。
sramecc
-mcpu
--offload-arch
如果指定,则生成的代码只能在 SRAMECC 设置匹配的进程中加载和执行。
如果未为代码对象 V2 到 V3 指定,则生成的代码可以在启用 SRAMECC 的进程中加载和执行。
如果未为代码对象 V4 或更高版本指定,则生成的代码可以在 SRAMECC 的任一设置的进程中加载和执行。
tgsplit
-m[no-]tgsplit
启用/禁用生成假定工作组以线程组拆分模式启动的代码。启用后,工作组的波可以以不同的 CU 启动。
wavefrontsize64
-m[no-]wavefrontsize64
控制为内核生成代码时使用的工作组大小。禁用时使用原生工作组大小 32,启用时使用工作组大小 64。
xnack
-mcpu
--offload-arch
如果指定,则生成的代码只能在 XNACK 重放设置匹配的进程中加载和执行。
如果未为代码对象 V2 到 V3 指定,则生成的代码可以在启用 XNACK 重放的进程中加载和执行。
如果未为代码对象 V4 或更高版本指定,则生成的代码可以在 XNACK 重放的任一设置的进程中加载和执行。
XNACK 重放可用于按需分页和页面迁移。如果在设备中启用,则如果发生页面错误,则除非在启用 XNACK 重放的情况下生成代码,或者为代码对象 V4 或更高版本生成代码而未指定 XNACK 重放,否则代码可能执行不正确。在未启用 XNACK 重放的设备上执行使用 XNACK 重放启用的代码生成或为代码对象 V4 或更高版本生成代码而未指定 XNACK 重放的代码将正确执行,但性能可能低于为禁用 XNACK 重放生成的代码。
目标 ID¶
AMDGPU 支持目标 ID。 有关一般描述,请参阅 Clang Offload Bundler。 AMDGPU 目标特定信息是
- 处理器
是 AMDGPU 处理器 中指定的 AMDGPU 处理器或备选处理器名称。 非规范形式的目标 ID 允许主处理器和备选处理器名称。 规范形式的目标 ID 仅允许主处理器名称。
- 目标特性
是 AMDGPU 目标特性 中指定的目标特性名称,该特性受处理器支持。 每个处理器支持的目标特性在 AMDGPU 处理器 中指定。 可以在目标 ID 中指定的那些特性被标记为由
-mcpu
和--offload-arch
控制。 每个目标特性在目标 ID 中最多出现一次。 非规范形式的目标 ID 允许以任何顺序指定目标特性。 规范形式的目标 ID 要求按字母顺序指定目标特性。
代码对象 V2 到 V3 目标 ID¶
代码对象 V2 到 V3 的目标 ID 语法与 Clang Offload Bundler 中定义的语法相同,但用于 .amdgcn_target <target-triple> “-” <target-id> 汇编器指令和捆绑条目 ID 时除外。 在这些情况下,它具有以下 BNF 语法
<target-id> ::== <processor> ( "+" <target-feature> )*
其中,如果目标特性为 Off 则省略,如果为 On 或 Any 则存在。
注意
代码对象 V2 到 V3 无法表示 Any,并将其视为与 On 相同。
嵌入捆绑的代码对象¶
AMDGPU 支持 HIP 和 OpenMP 语言,这些语言执行代码对象嵌入,如 Clang Offload Bundler 中所述。
注意
用于捆绑条目 ID 的代码对象 V2 到 V3 的目标 ID 语法与其他地方使用的语法不同。 参见 代码对象 V2 到 V3 目标 ID。
地址空间¶
AMDGPU 架构支持多种内存地址空间。 地址空间名称使用 OpenCL 标准名称,并添加了一些内容。
AMDGPU 地址空间对应于 LLVM IR 中使用的目标架构特定 LLVM 地址空间编号。
AMDGPU 地址空间在 AMDGPU 地址空间 中描述。 amdgcn
目标仅支持 64 位进程地址空间。
表 25 AMDGPU 地址空间¶ 64 位进程地址空间
地址空间名称
LLVM IR 地址空间编号
HSA 段名称
硬件名称
地址大小
NULL 值
通用
0
flat
flat
64
0x0000000000000000
全局
1
global
global
64
0x0000000000000000
区域
2
N/A
GDS
32
未为 AMDHSA 实现
局部
3
group
LDS
32
0xFFFFFFFF
常量
4
constant
与全局相同
64
0x0000000000000000
私有
5
private
scratch
32
0xFFFFFFFF
常量 32 位
6
TODO
0x00000000
缓冲区胖指针
7
N/A
N/A
160
0
缓冲区资源
8
N/A
V#
128
0x00000000000000000000000000000000
缓冲区跨步指针(实验性)
9
TODO
Streamout 寄存器
128
N/A
GS_REGS
- 通用
除非 AMDGPU 处理器 的目标属性列指定不支持通用地址空间,否则支持通用地址空间。
通用地址空间使用硬件平面地址支持,用于两个固定范围的虚拟地址(私有和局部 aperture),它们位于可寻址全局内存范围之外,以将平面地址映射到私有或局部地址。这使用 FLAT 指令,该指令可以采用平面地址并访问全局、私有 (scratch) 和组 (LDS) 内存,具体取决于地址是否在 aperture 范围之一内。
对 scratch 的平面访问需要硬件 aperture 设置和内核序言中的设置(参见 Flat Scratch)。对 LDS 的平面访问需要硬件 aperture 设置和 M0 (GFX7-GFX8) 寄存器设置(参见 M0)。
要在私有或组地址空间地址(称为段地址)和平面地址之间进行转换,可以使用相应 aperture 的基地址。对于 GFX7-GFX8,这些地址可在 HSA AQL 队列 中获得,可以使用队列指针 SGPR 获取其地址(参见 初始内核执行状态)。对于 GFX9-GFX11,aperture 基地址可直接作为内联常量寄存器
SRC_SHARED_BASE/LIMIT
和SRC_PRIVATE_BASE/LIMIT
使用。在 64 位地址模式下,aperture 大小为 2^32 字节,基地址与 2^32 对齐,这使得从平面到段或从段到平面的转换更容易。全局地址空间地址在用作平面地址时具有相同的值,因此无需转换。
- 全局和常量
全局和常量地址空间都使用全局虚拟地址,这与 CPU 使用的虚拟地址空间相同。但是,某些虚拟地址可能仅可由 CPU 访问,某些仅可由 GPU 访问,而某些可由两者访问。
使用常量地址空间表示数据在内核执行期间不会更改。这允许使用标量读取指令。由于常量地址空间只能在主机端修改,因此从常量地址空间加载的通用指针可以安全地假定为全局指针,因为只有设备全局内存在主机端可见和管理。向量和标量 L1 缓存会在每次内核调度执行之前使易失性数据无效,以允许常量内存在内核调度之间更改值。
- 区域
区域地址空间使用硬件全局数据存储 (GDS)。在同一设备上执行的所有工作组将为任何给定的区域地址访问相同的内存。但是,在不同设备上执行的工作组访问的相同区域地址将访问不同的内存。它的性能高于全局内存。它由运行时分配。数据存储 (DS) 指令可用于访问它。
- 局部
局部地址空间使用硬件局部数据存储 (LDS),它在硬件创建工作组的工作组时自动分配,并在工作组的所有工作组终止时释放。属于同一工作组的所有工作组将为任何给定的局部地址访问相同的内存。但是,属于不同工作组的工作组访问的相同局部地址将访问不同的内存。它的性能高于全局内存。数据存储 (DS) 指令可用于访问它。
- 私有
私有地址空间使用硬件 scratch 内存支持,该支持在创建工作组时自动分配内存,并在工作组终止时释放内存。工作组通道为任何给定的私有地址访问的内存将与同一工作组或不同工作组的另一个通道为同一私有地址访问的内存不同。
如果内核调度使用 scratch,则硬件会为每个工作组从运行时分配的后备内存池中分配内存。工作组的通道使用 dword(4 字节)交错访问此内存。从私有地址到后备内存地址的映射是
wavefront-scratch-base + ((private-address / 4) * wavefront-size * 4) + (wavefront-lane-id * 4) + (private-address % 4)
如果 wavefront 的每个 lane 访问相同的私有地址,则交错会导致访问相邻的 dword,因此需要获取更少的缓存行。
wavefront scratch 基地址的确定方式有多种(请参阅 初始内核执行状态)。
可以使用带有 scratch 缓冲区描述符和每个 wavefront scratch 偏移量的缓冲区指令、scratch 指令或 flat 指令以交错方式访问 Scratch 内存。除了 GFX9-GFX11 中的 flat 和 scratch 指令外,不支持多 dword 访问。
在 wavefront 的其他 lane 中操作堆栈值的代码,例如通过将堆栈指针
addrspacecast
转换为通用指针并获取到达其他 lane 的偏移量,或者通过显式构造 scratch 缓冲区描述符,当它修改其他 lane 的 scratch 值时,会触发未定义的行为。编译器可能会假设不会发生此类修改。当使用代码对象 V5 时,LIBOMPTARGET_STACK_SIZE
可用于提供私有段大小(以字节为单位),以用于动态堆栈的情况。- 常量 32 位
TODO
- 缓冲区胖指针
缓冲区 fat 指针是一个实验性的地址空间,目前后端不支持。它公开了一个非整数指针,未来旨在支持 128 位缓冲区描述符加上 32 位缓冲区偏移量(总共封装了一个 160 位指针)的建模,允许使用正常的 LLVM 加载/存储/原子操作来建模在面向后端的图形工作负载中大量使用的缓冲区描述符。
用于构造缓冲区 fat 指针的缓冲区描述符必须是原始的:步幅必须为 0,“添加 tid”标志必须为 0,swizzle 使能位必须关闭,并且范围必须以字节为单位进行测量。(在可以禁用边界检查的子目标上,缓冲区 fat 指针可以选择启用或不启用边界检查)。gfx942 中引入的缓存 swizzle 支持可以使用。
这些指针可以通过 addrspacecast 从缓冲区资源 (ptr addrspace(8)) 创建,也可以使用 llvm.amdgcn.make.buffer.rsrc 直接生成 ptr addrspace(7),这将生成一个初始偏移量为 0 的缓冲区 fat 指针,并防止地址空间转换被重写掉。
- 缓冲区资源
地址空间 8 中的缓冲区资源指针是 AMDGPU IR 中表示缓冲区描述符的较新形式,取代了以前的 <4 x i32> 表示形式。它是一个非整数指针,表示 128 位缓冲区描述符资源 (V#)。
由于通常缓冲区资源支持 LLVM 中无法轻松表示的复杂寻址模式(例如,对结构化缓冲区的隐式 swizzle 访问),因此对缓冲区资源执行非平凡的地址计算(例如
getelementptr
操作)是非法的。它们可以传递给 AMDGPU 缓冲区内部函数,并且可以在i128
之间相互转换。允许将缓冲区资源转换为缓冲区 fat 指针,并添加 0 的偏移量。
缓冲区资源可以使用 llvm.amdgcn.make.buffer.rsrc intrinsic 从 64 位指针(应为 generic 或 global)创建,该 intrinsic 接受指针(成为资源的基础)、存储在 V# 的位 63:48 中的 16 位步幅(和 swizzle 控制)字段、32 位 NumRecords/extent 字段(位 95:64)和 32 位标志字段(位 127:96)。这些字段的具体解释因目标架构而异,并在 ISA 描述中详细说明。
- 缓冲区步幅指针
缓冲区索引指针是一个实验性的地址空间。它表示一个 128 位缓冲区描述符和一个 32 位偏移量,类似于缓冲区 Fat 指针。此外,它还包含一个缓冲区索引,允许直接寻址结构化元素。这些组件按该顺序出现,即描述符首先出现,然后是 32 位偏移量,最后是 32 位索引。
缓冲区描述符中的位必须满足以下要求:步幅是结构化元素的大小,“添加 tid”标志必须为 0,并且 swizzle 使能位必须关闭。
这些指针可以通过 addrspacecast 从缓冲区资源 (ptr addrspace(8)) 创建,也可以使用 llvm.amdgcn.make.buffer.rsrc 直接生成 ptr addrspace(9),这将生成一个缓冲区步幅指针,其初始索引和偏移值均为 0。这可以防止地址空间转换被重写掉。
- Streamout 寄存器
GS NGG Streamout 指令使用的专用寄存器。寄存器文件被建模为独立地址空间中的内存,因为它由类似地址的偏移量而不是命名寄存器索引,并且因为寄存器访问会影响 LGKMcnt。这是一个仅供编译器使用的内部地址空间。请勿将此地址空间用于 IR 指针。
内存作用域¶
本节提供当目标三元组 OS 为 amdhsa
时,AMDGPU 后端内存模型支持的 LLVM 内存同步作用域(请参阅 内存模型 和 目标三元组)。
支持的内存模型基于 HSA 内存模型 [HSA],该模型又基于具有作用域包含的 HRF-indirect [HRF]。先行发生关系在同步关系上是可传递的,与作用域无关,并且同步关系允许内存作用域实例是包含性的(请参阅表 AMDHSA LLVM 同步作用域)。
这与 OpenCL [OpenCL] 内存模型不同,后者没有作用域包含,并且要求内存作用域完全匹配。但是,这对于 OpenCL 来说是保守正确的。
表 26 AMDHSA LLVM 同步作用域¶ LLVM 同步作用域
描述
none
默认值:
system
。与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为
system
.
agent
并且由同一 agent 上的线程执行。
workgroup
并且由同一工作组中的线程执行。
wavefront
并且由同一 wavefront 中的线程执行。
agent
与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为
system
或agent
并且由同一 agent 上的线程执行。
workgroup
并且由同一工作组中的线程执行。
wavefront
并且由同一 wavefront 中的线程执行。
workgroup
与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为
system
、agent
或workgroup
并且由同一工作组中的线程执行。
wavefront
并且由同一 wavefront 中的线程执行。
wavefront
与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为
system
、agent
、workgroup
或wavefront
并且由同一 wavefront 中的线程执行。
singlethread
仅与在同一线程中运行的其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(例如,在信号处理程序中)。
one-as
与
system
相同,但仅与同一地址空间内的其他操作同步。
agent-one-as
与
agent
相同,但仅与同一地址空间内的其他操作同步。
workgroup-one-as
与
workgroup
相同,但仅与同一地址空间内的其他操作同步。
wavefront-one-as
与
wavefront
相同,但仅与同一地址空间内的其他操作同步。
singlethread-one-as
与
singlethread
相同,但仅与同一地址空间内的其他操作同步。
LLVM IR Intrinsic¶
AMDGPU 后端实现了以下 LLVM IR intrinsic。
本节正在进行中 (WIP)。
LLVM Intrinsic |
描述 |
---|---|
llvm.amdgcn.sqrt |
提供对 v_sqrt_f64、v_sqrt_f32 和 v_sqrt_f16(在支持 half 的目标上)的直接访问。执行 sqrt 函数。 |
llvm.amdgcn.log |
提供对 v_log_f32 和 v_log_f16(在支持 half 的目标上)的直接访问。执行 log2 函数。 |
llvm.amdgcn.exp2 |
提供对 v_exp_f32 和 v_exp_f16(在支持 half 的目标上)的直接访问。执行 exp2 函数。 |
为 half、float 和 double 实现。 |
|
为 float 和 half(以及 float 或 half 的向量)实现。未为 double 实现。硬件为 float 提供 1ULP 精度,为 half 提供 0.51ULP 精度。Float 指令本身不支持非正规输入。 |
|
为 double、float 和 half(以及向量)实现。 |
|
为 float 和 half(以及向量)实现。 |
|
为 float 和 half(以及向量)实现。 |
|
为 float 和 half(以及向量)实现。 |
|
为 float 和 half(以及 float 或 half 的向量)实现。未为 double 实现。硬件为 float 提供 1ULP 精度,为 half 提供 0.51ULP 精度。Float 指令本身不支持非正规输入。 |
|
已实现,必须使用 alloca 地址空间。 |
|
已实现,必须使用 alloca 地址空间。 |
|
自然的浮点模式类型是 i32。这是通过使用 s_getreg_b32 从 MODE 寄存器中提取相关位来实现的。前 10 位是核心浮点模式。位 12:18 是异常掩码。在 gfx9+ 上,位 23 是 FP16_OVFL。与浮点指令无关的位域为 0。 |
|
AMDGPU 支持两种可单独控制的舍入模式,具体取决于浮点类型。一种控制 float,另一种控制 double 和 half 操作。如果两种模式相同,则返回标准返回值之一。如果模式不同,则返回 12 个扩展值 之一,描述两种模式。 向最近舍入,远离零的 ties 不是受支持的模式。MODE 寄存器中的原始舍入模式值与 FLT_ROUNDS 值不完全匹配,因此会执行转换。 |
|
输入值应为 ‘ |
|
返回 AMDGPU 浮点环境的当前值。这存储与当前舍入模式、非正规化模式、启用的陷阱和浮点异常相关的信息。格式是 MODE 和 TRAPSTS 寄存器的 64 位串联。 |
|
将浮点环境设置为指定状态。 |
|
llvm.amdgcn.readfirstlane |
提供对 v_readfirstlane_b32 的直接访问。返回输入操作数的最低活动 lane 中的值。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.readlane |
提供对 v_readlane_b32 的直接访问。返回第一个输入操作数的指定 lane 中的值。第二个操作数指定要读取的 lane。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.writelane |
提供对 v_writelane_b32 的直接访问。将第一个输入操作数中的值写入发散输出的指定 lane。第二个操作数指定要写入的 lane。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.wave.reduce.umin |
对 wavefront 中每个 lane 提供的无符号值执行算术无符号最小值缩减。Intrinsic 使用第二个操作数中的提示来获取缩减策略:0:目标默认首选项,1:迭代策略,和 2:DPP。如果目标不支持 DPP 操作(例如 gfx6/7),则将使用默认迭代策略执行缩减。Intrinsic 当前仅为 i32 实现。 |
llvm.amdgcn.wave.reduce.umax |
对 wavefront 中每个 lane 提供的无符号值执行算术无符号最大值缩减。Intrinsic 使用第二个操作数中的提示来获取缩减策略:0:目标默认首选项,1:迭代策略,和 2:DPP。如果目标不支持 DPP 操作(例如 gfx6/7),则将使用默认迭代策略执行缩减。Intrinsic 当前仅为 i32 实现。 |
llvm.amdgcn.permlane16 |
提供对 v_permlane16_b32 的直接访问。在第二个输入操作数的行(16 个连续 lane)内执行任意 gather 样式操作。第三个和第四个输入必须是标量值。这些值组合成单个 64 位值,表示用于在每行内 swizzle 的 lane 选择。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.permlanex16 |
提供对 v_permlanex16_b32 的直接访问。在第二个输入操作数的两行(每行 16 个连续 lane)之间执行任意 gather 样式操作。第三个和第四个输入必须是标量值。这些值组合成单个 64 位值,表示用于在每行内 swizzle 的 lane 选择。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.permlane64 |
提供对 v_permlane64_b32 的直接访问。在输入操作数的 lane 之间执行特定的排列,其中 wave64 的高半部分和低半部分被交换。在 wave32 模式下不执行任何操作。当前为 i16、i32、float、half、bfloat、<2 x i16>、<2 x half>、<2 x bfloat>、i64、double、指针、32 位向量的倍数实现。 |
llvm.amdgcn.udot2 |
在支持此类指令的目标上提供对 v_dot2_u32_u16 的直接访问。这使用两个 v2i16 操作数执行无符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。 |
llvm.amdgcn.udot4 |
在支持此类指令的目标上提供对 v_dot4_u32_u8 的直接访问。这使用两个 i32 操作数(保存 4 个 8 位值的向量)执行无符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。 |
llvm.amdgcn.udot8 |
在支持此类指令的目标上提供对 v_dot8_u32_u4 的直接访问。这使用两个 i32 操作数(保存 8 个 4 位值的向量)执行无符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。 |
llvm.amdgcn.sdot2 |
在支持此类指令的目标上提供对 v_dot2_i32_i16 的直接访问。这使用两个 v2i16 操作数执行有符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。当适用时(例如,没有钳制),这会降低为支持它的目标的 v_dot2c_i32_i16。 |
llvm.amdgcn.sdot4 |
在支持此类指令的目标上提供对 v_dot4_i32_i8 的直接访问。这使用两个 i32 操作数(保存 4 个 8 位值的向量)执行有符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。当适用时(即,没有钳制/操作数修饰符),这会降低为支持它的目标的 v_dot4c_i32_i8。RDNA3 不提供 v_dot4_i32_i8,而是提供 v_dot4_i32_iu8,它具有用于保存向量操作数的有符号性的操作数。因此,对于 gfx11 目标,此 intrinsic 会降低为该指令的有符号版本。 |
llvm.amdgcn.sdot8 |
在支持此类指令的目标上提供对 v_dot8_u32_u4 的直接访问。这使用两个 i32 操作数(保存 8 个 4 位值的向量)执行有符号点积,并与第三个 i32 操作数求和。i1 第四个操作数用于钳制输出。当适用时(即,没有钳制/操作数修饰符),这会降低为支持它的目标的 v_dot8c_i32_i4。RDNA3 不提供 v_dot8_i32_i4,而是提供 v_dot4_i32_iu4,它具有用于保存向量操作数的有符号性的操作数。因此,对于 gfx11 目标,此 intrinsic 会降低为该指令的有符号版本。 |
llvm.amdgcn.sudot4 |
在 gfx11 目标上提供对 v_dot4_i32_iu8 的直接访问。这使用两个 i32 操作数(保存 4 个 8 位值的向量)执行点积,并与第五个 i32 操作数求和。i1 第六个操作数用于钳制输出。向量操作数之前的 i1 决定了有符号性。 |
llvm.amdgcn.sudot8 |
在 gfx11 目标上提供对 v_dot8_i32_iu4 的直接访问。这使用两个 i32 操作数(保存 8 个 4 位值的向量)执行点积,并与第五个 i32 操作数求和。i1 第六个操作数用于钳制输出。向量操作数之前的 i1 决定了有符号性。 |
llvm.amdgcn.sched.barrier |
控制在指令调度期间可能允许跨越 intrinsic 的指令类型。该参数是可跨越 intrinsic 调度的指令类型的掩码。
|
llvm.amdgcn.sched.group.barrier |
创建具有特定属性的调度组,以创建自定义调度流水线。组之间的顺序由指令调度器强制执行。Intrinsic 应用于 intrinsic 之前的代码。Intrinsic 采用三个值来控制调度组的行为。
掩码可以包括多种指令类型。设置超出有效掩码范围的值是未定义的行为。 组合多个 sched_group_barrier intrinsic 可以在指令调度期间启用特定指令类型的排序。例如,以下内容强制执行 1 个 VMEM 读取、然后是 1 个 VALU 指令、然后是 5 个 MFMA 指令的序列。 // 1 VMEM read __builtin_amdgcn_sched_group_barrier(32, 1, 0) // 1 VALU __builtin_amdgcn_sched_group_barrier(2, 1, 0) // 5 MFMA __builtin_amdgcn_sched_group_barrier(8, 5, 0) |
llvm.amdgcn.iglp.opt |
用于指令组级别并行性的实验性 intrinsic。Intrinsic 实现了预定义的指令调度顺序。Intrinsic 应用于周围的调度区域。Intrinsic 采用一个指定策略的值。编译器实现了两种策略。
在调度区域中只能使用一个 iglp_opt intrinsic。iglp_opt intrinsic 不能与 sched_barrier 或 sched_group_barrier 组合使用。 iglp_opt 策略实现可能会更改。 |
llvm.amdgcn.atomic.cond.sub.u32 |
在 gfx12 目标上提供对基于地址空间的 flat_atomic_cond_sub_u32、global_atomic_cond_sub_u32 和 ds_cond_sub_u32 的直接访问。仅当内存值大于或等于数据值时,才执行减法。 |
llvm.amdgcn.s.getpc |
提供对 s_getpc_b64 指令的访问,但即使在 s_getpc_b64 指令返回零扩展值的处理器上,返回值也从底层 PC 硬件寄存器的宽度进行符号扩展。 |
llvm.amdgcn.ballot |
返回一个位域(i32 或 i64),其中包含其 i1 参数在所有活动 lane 中的结果,以及在所有非活动 lane 中的零。提供了一种将 LLVM IR 中的 i1 转换为 i32 或 i64 lane 掩码的方法 - 硬件用于在使用 EXEC 寄存器时控制活动 lane 的位域。例如,ballot(i1 true) 返回 EXEC 掩码。 |
llvm.amdgcn.mfma.scale.f32.16x16x128.f8f6f4 |
发出 v_mfma_scale_f32_16x16x128_f8f6f4 以设置比例因子。最后 4 个操作数对应于比例输入。
|
llvm.amdgcn.mfma.scale.f32.32x32x64.f8f6f4 |
发出 v_mfma_scale_f32_32x32x64_f8f6f4 |
llvm.amdgcn.permlane16.swap |
在受支持的目标上提供对 v_permlane16_swap_b32 指令的直接访问。交换前 2 个操作数的 lane 之间的值。第一个操作数的奇数行与第二个操作数的偶数行交换(一行是 16 个 lane)。返回交换寄存器的对。返回的第一个元素对应于第一个参数的交换元素。 |
llvm.amdgcn.permlane32.swap |
在受支持的目标上提供对 v_permlane32_swap_b32 指令的直接访问。交换前 2 个操作数的 lane 之间的值。第一个操作数的行 2 和行 3 与第二个操作数的行 0 和行 1 交换(一行是 16 个 lane)。返回交换寄存器的对。返回的第一个元素对应于第一个参数的交换元素。 |
llvm.amdgcn.mov.dpp |
llvm.amdgcn.mov.dpp.`<type>` intrinsic 表示 AMDGPU 中的 mov.dpp 操作。此操作已弃用,可以用 llvm.amdgcn.update.dpp 替换。 |
llvm.amdgcn.update.dpp |
llvm.amdgcn.update.dpp.`<type>` intrinsic 表示 AMDGPU 中的 update.dpp 操作。它采用旧值、源操作数、DPP 控制操作数、行掩码、bank 掩码和边界控制。支持各种数据类型,包括 bf16、f16、f32、f64、i16、i32、i64、p0、p3、p5、v2f16、v2f32、v2i16、v2i32、v2p0、v3i32、v4i32、v8f16。此操作等效于 v_mov_b32 操作序列。在未来使用中,它优先于 llvm.amdgcn.mov.dpp.`<type>`。llvm.amdgcn.update.dpp.<type> <old> <src> <dpp_ctrl> <row_mask> <bank_mask> <bound_ctrl> 应等效于:- v_mov_b32 <dest> <old> - v_mov_b32 <dest> <src> <dpp_ctrl> <row_mask> <bank_mask> <bound_ctrl> |
LLVM IR 元数据¶
AMDGPU 后端实现了以下目标自定义 LLVM IR 元数据。
‘amdgpu.last.use
’ 元数据¶
在支持它的加载指令上设置 TH_LOAD_LU 时间提示。优先于非时间提示 (TH_LOAD_NT)。这不带任何参数。
%val = load i32, ptr %in, align 4, !amdgpu.last.use !{}
‘amdgpu.no.remote.memory
’ 元数据¶
断言内存操作不访问主机内存或远程连接的对等设备内存中的字节(地址必须是设备本地的)。这旨在与 atomicrmw 和其他原子指令一起使用。对于某些子目标上的某些 system 作用域 原子操作,这是发出本机硬件指令所必需的。对于大多数整数原子操作,这是发出本机原子指令的充分限制。
没有元数据的 atomicrmw 将被保守地视为需要在所有情况下保留操作行为。这通常与 !amdgpu.no.fine.grained.memory 结合使用。
; Indicates the atomic does not access fine-grained memory, or
; remote device memory.
%old0 = atomicrmw sub ptr %ptr0, i32 1 acquire, !amdgpu.no.fine.grained.memory !0, !amdgpu.no.remote.memory !0
; Indicates the atomic does not access peer device memory.
%old2 = atomicrmw sub ptr %ptr2, i32 1 acquire, !amdgpu.no.remote.memory !0
!0 = !{}
‘amdgpu.no.fine.grained.memory
’ 元数据¶
断言内存访问不访问在细粒度分配内存中分配的字节。这旨在与 atomicrmw 和其他原子指令一起使用。对于某些子目标上的某些 system 作用域 原子操作,这是发出本机硬件指令所必需的。没有元数据的 atomicrmw 将被保守地视为需要在所有情况下保留操作行为。这通常将与 !amdgpu.no.remote.memory.access 结合使用。
; Indicates the access does not access fine-grained memory, or
; remote device memory.
%old0 = atomicrmw sub ptr %ptr0, i32 1 acquire, !amdgpu.no.fine.grained.memory !0, !amdgpu.no.remote.memory.access !0
; Indicates the access does not access fine-grained memory
%old2 = atomicrmw sub ptr %ptr2, i32 1 acquire, !amdgpu.no.fine.grained.memory !0
!0 = !{}
‘amdgpu.ignore.denormal.mode
’ 元数据¶
用于 atomicrmw 浮点操作。指示非正规输入和结果的处理无关紧要,并且可能与预期的浮点模式不一致。这对于在某些目标上为某些地址空间发出本机原子指令是必要的,在这些地址空间中,float 非正规数会被无条件刷新。这通常与 !amdgpu.no.remote.memory.access 和 !amdgpu.no.fine.grained.memory 结合使用
%res0 = atomicrmw fadd ptr addrspace(1) %ptr, float %value seq_cst, align 4, !amdgpu.ignore.denormal.mode !0
%res1 = atomicrmw fadd ptr addrspace(1) %ptr, float %value seq_cst, align 4, !amdgpu.ignore.denormal.mode !0, !amdgpu.no.fine.grained.memory !0, !amdgpu.no.remote.memory.access !0
!0 = !{}
LLVM IR 属性¶
AMDGPU 后端支持以下 LLVM IR 属性。
表 28 AMDGPU LLVM IR 属性¶ LLVM 属性
描述
“amdgpu-flat-work-group-size”=”min,max”
指定在内核调度时将指定的最小和最大 flat 工作组大小。由
amdgpu_flat_work_group_size
CLANG 属性 [CLANG-ATTR] 生成。IR 隐含的默认值为 1,1024。Clang 可能会根据语言默认值发出具有更严格边界的此属性。如果实际的块或工作组大小在执行期间的任何时候超过限制,则行为未定义。例如,即使只有一个活动线程,但线程本地 ID 超过了限制,行为也是未定义的。“amdgpu-implicitarg-num-bytes”=”n”
要添加到内核参数块大小的内核参数字节数,用于隐式参数。这因操作系统和语言而异(对于 OpenCL,请参阅 为 AMDHSA OS 附加的 OpenCL 内核隐式参数)。
“amdgpu-num-sgpr”=”n”
指定要使用的 SGPR 的数量。由
amdgpu_num_sgpr
CLANG 属性 [CLANG-ATTR] 生成。“amdgpu-num-vgpr”=”n”
指定要使用的 VGPR 的数量。由
amdgpu_num_vgpr
CLANG 属性 [CLANG-ATTR] 生成。“amdgpu-waves-per-eu”=”m,n”
指定每个执行单元的最小和最大 wave 数量。由
amdgpu_waves_per_eu
CLANG 属性 [CLANG-ATTR] 生成。这是一个优化提示,后端可能无法满足请求。如果指定的范围与函数的 “amdgpu-flat-work-group-size” 值不兼容,则工作组大小隐含的占用率边界优先。“amdgpu-ieee” true/false。
仅限 GFX6-GFX11 指定函数是否期望在入口处设置模式寄存器的 IEEE 字段。覆盖调用约定的默认值。
“amdgpu-dx10-clamp” true/false。
仅限 GFX6-GFX11 指定函数是否期望在入口处设置模式寄存器的 DX10_CLAMP 字段。覆盖调用约定的默认值。
“amdgpu-no-workitem-id-x”
指示函数不依赖于 llvm.amdgcn.workitem.id.x intrinsic 的值。如果函数标有此属性,或通过标有此属性的调用站点到达,并且调用了该 intrinsic,则程序的行为未定义。(此处使用全程序未定义的行为是因为,例如,预加载寄存器集中缺少所需的工作项 ID 可能意味着所有其他预加载寄存器都比编译假设的要早。)后端通常可以在代码生成期间推断出这一点,因此前端标记具有此属性的函数通常没有任何好处。
“amdgpu-no-workitem-id-y”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.workitem.id.y intrinsic。
“amdgpu-no-workitem-id-z”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.workitem.id.z intrinsic。
“amdgpu-no-workgroup-id-x”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.workgroup.id.x intrinsic。
“amdgpu-no-workgroup-id-y”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.workgroup.id.y intrinsic。
“amdgpu-no-workgroup-id-z”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.workgroup.id.z intrinsic。
“amdgpu-no-dispatch-ptr”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.dispatch.ptr intrinsic。
“amdgpu-no-implicitarg-ptr”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.implicitarg.ptr intrinsic。
“amdgpu-no-dispatch-id”
与 amdgpu-no-workitem-id-x 相同,但适用于 llvm.amdgcn.dispatch.id intrinsic。
“amdgpu-no-queue-ptr”
类似于 amdgpu-no-workitem-id-x,但用于 llvm.amdgcn.queue.ptr intrinsic。请注意,与其他 ABI 提示属性不同,队列指针在 intrinsic 调用未直接出现在程序中的情况下可能是必需的。一些子目标需要队列指针来处理某些 addrspacecast,以及 llvm.amdgcn.is.shared、llvm.amdgcn.is.private、llvm.trap 和 llvm.debug intrinsics。
“amdgpu-no-hostcall-ptr”
类似于 amdgpu-no-implicitarg-ptr,但专门用于保存指向 hostcall 缓冲区的指针的隐式内核参数。如果缺少此属性,则 amdgpu-no-implicitarg-ptr 也会被移除。
“amdgpu-no-heap-ptr”
类似于 amdgpu-no-implicitarg-ptr,但专门用于保存指向符合 malloc/free 设备库 V1 版本实现要求的已初始化内存缓冲区的指针的隐式内核参数。如果缺少此属性,则 amdgpu-no-implicitarg-ptr 也会被移除。
“amdgpu-no-multigrid-sync-arg”
类似于 amdgpu-no-implicitarg-ptr,但专门用于保存 multigrid 同步指针的隐式内核参数。如果缺少此属性,则 amdgpu-no-implicitarg-ptr 也会被移除。
“amdgpu-no-default-queue”
类似于 amdgpu-no-implicitarg-ptr,但专门用于保存默认队列指针的隐式内核参数。如果缺少此属性,则 amdgpu-no-implicitarg-ptr 也会被移除。
“amdgpu-no-completion-action”
类似于 amdgpu-no-implicitarg-ptr,但专门用于保存 completion action 指针的隐式内核参数。如果缺少此属性,则 amdgpu-no-implicitarg-ptr 也会被移除。
“amdgpu-lds-size”=”min[,max]”
Min 是将在地址零的本地数据存储中分配的最小字节数。变量使用绝对符号元数据在此帧内分配,主要由 AMDGPULowerModuleLDS pass 完成。可选的 max 是将分配的最大字节数。请注意,min==max 表示无法向帧中添加更多变量。这是 LDS 变量如何降低的内部细节,语言前端不应设置此属性。
“amdgpu-gds-size”
预计在入口处 GDS 内存开始时分配的字节数。
“amdgpu-git-ptr-high”
AMDPAL OS 类型的全局信息表地址的硬连线高半部分。0xffffffff 表示没有硬连线高半部分,因为当前硬件仅允许 16 位值。
“amdgpu-32bit-address-high-bits”
假定的 32 位地址空间的高 32 位,这些地址空间实际上是被截断的 64 位地址 (即 addrspace(6))
“amdgpu-color-export”
如果设置为 1,则表示着色器导出颜色信息。对于 amdgpu_ps 默认为 1,对于其他调用约定默认为 0。决定着色器通过终止通道提前结束时空导出的必要性和类型。
“amdgpu-depth-export”
如果设置为 1,则表示着色器导出深度信息。决定着色器通过终止通道提前结束时空导出的必要性和类型。当没有空导出目标可用时(GFX11+),仅深度着色器将导出到深度通道。
“InitialPSInputAddr”
为 amdgpu_ps 着色器设置 spi_ps_input_addr 寄存器的初始值。此值启用的任何位都将在最终寄存器值中启用。
“amdgpu-wave-priority-threshold”
用于调整 wave 优先级的 VALU 指令计数阈值。如果超出,则在着色器函数开始时暂时提高 wave 优先级,直到其最后一个 VMEM 指令,以允许较新的 wave 也发布其 VMEM 指令。
“amdgpu-memory-bound”
由后端内部设置
“amdgpu-wave-limiter”
由后端内部设置
“amdgpu-unroll-threshold”
为此函数内的循环展开设置基本成本阈值偏好,默认为 300。实际阈值可能会因每个循环的元数据而异,或因启发式方法而降低。
“amdgpu-max-num-workgroups”=”x,y,z”
指定内核调度在 X、Y 和 Z 维度上的最大工作组数量。每个数字必须 >= 1。由
amdgpu_max_num_work_groups
CLANG 属性 [CLANG-ATTR] 生成。仅当所有三个数字都 >= 1 时,Clang 才会发出此属性。“amdgpu-hidden-argument”
此属性由后端内部使用,用于将函数参数标记为隐藏。隐藏参数由编译器管理,不属于用户提供的显式参数。
“amdgpu-agpr-alloc”=”min(,max)”
指示要提供的 AGPR 数量的最小和最大范围以进行分配。这些值将向上舍入到分配粒度(4)的下一个倍数。最小值被解释为函数分配所需的最小 AGPR 数量(即,该函数需要的寄存器不超过最小值)。如果仅指定一个值,则将其解释为最小寄存器预算。最大值将限制分配使用的 AGPR 不超过最大值。
如果满足这些值会违反其他分配约束,则可能会忽略这些值。
如果通过任何标有此属性较高值的函数到达需要比下限更多的 AGPR 的函数,则行为未定义。最小值为 0 表示该函数不需要任何 AGPR。
这仅与支持 accum_offset 的 AGPR 的目标相关 (gfx90a+)。
“amdgpu-sgpr-hazard-wait”
如果设置为 0,则禁用 SGPR hazard wait 插入。仅用于测试 SGPR hazard wait 的性能影响。
“amdgpu-sgpr-hazard-boundary-cull”
在函数调用边界启用 SGPR hazard cull 序列的插入。Cull 序列减少了未来的 hazard wait,但会产生性能成本。
“amdgpu-sgpr-hazard-mem-wait-cull”
在内存等待之前启用 SGPR hazard cull 序列的插入。Cull 序列减少了未来的 hazard wait,但会产生性能成本。尝试通过与内存访问重叠来分摊成本。
“amdgpu-sgpr-hazard-mem-wait-cull-threshold”
设置在内存等待时插入 cull 序列之前必须存在的活动 SGPR hazard 的数量。
调用约定¶
AMDGPU 后端支持以下调用约定
表 29 AMDGPU 调用约定¶ 调用约定
描述
ccc
C 调用约定。默认使用。有关更多详细信息,请参阅 非内核函数。
fastcc
快速调用约定。与
ccc
大致相同。
coldcc
冷调用约定。与
ccc
大致相同。
amdgpu_cs
用于 Mesa/AMDPAL 计算着色器。..TODO:: 描述。
amdgpu_cs_chain
类似于
amdgpu_cs
,但有如下所述的差异。具有此调用约定的函数不能直接调用。它们必须通过
llvm.amdgcn.cs.chain
intrinsic 启动。如果参数具有
inreg
属性,则在 SGPR 中传递参数,从 s0 开始;否则在 VGPR 中传递参数,从 v8 开始。不允许使用超过子目标中可用的 SGPR 或 VGPR。在使用 scratch 缓冲区描述符(而不是scratch_{load,store}_*
指令)的子目标上,scratch 缓冲区描述符在 s[48:51] 中传递。这限制了 SGPR /inreg
参数相当于 48 个 dword;不允许使用超过此数量的参数。返回类型必须为 void。不支持 Varargs、sret、byval、byref、inalloca、preallocated。
标量寄存器以及 v0-v7 中的值不会被保留。从 v8 开始的 VGPR 中的值不会为活动通道保留,但在使用 WWM 时,必须由被调用者为非活动通道保存(一个值得注意的例外是在函数中使用 llvm.amdgcn.init.whole.wave intrinsic 时 - 在这种情况下,后端假定入口处没有非活动通道;任何需要保留的非活动通道都必须显式地存在于 IR 中)。
Wave scratch 在函数边界处是“空的”。没有堆栈指针输入或输出值,但函数可以自由地从初始堆栈指针开始使用 scratch。允许调用
amdgpu_gfx
函数,并且其行为方式与amdgpu_cs
函数中相同。所有计数器(
lgkmcnt
、vmcnt
、storecnt
等)在函数入口处都被假定为未知状态。一个函数可以有多个出口(例如,一个链式出口和一个普通的
ret void
,用于 wave 结束时),但所有llvm.amdgcn.cs.chain
出口都必须在统一的控制流中。
amdgpu_cs_chain_preserve
与
amdgpu_cs_chain
相同,但从 v8 开始的 VGPR 的活动通道被保留。不允许调用amdgpu_gfx
函数,并且任何对llvm.amdgcn.cs.chain
的调用都不能传递比调用者的 VGPR 函数参数更多的 VGPR 参数。
amdgpu_es
用于 AMDPAL 着色器阶段,如果正在使用几何着色器,则在几何着色器之前。因此,如果正在使用 tessellation,则为域(= tessellation 评估)着色器,否则为顶点着色器。..TODO:: 描述。
amdgpu_gfx
用于 AMD 图形目标。具有此调用约定的函数不能用作入口点。..TODO:: 描述。
amdgpu_gs
用于 Mesa/AMDPAL 几何着色器。..TODO:: 描述。
amdgpu_hs
用于 Mesa/AMDPAL hull 着色器(= tessellation 控制着色器)。..TODO:: 描述。
amdgpu_kernel
请参阅 内核函数
amdgpu_ls
如果正在使用 tessellation,则用于 AMDPAL 顶点着色器。..TODO:: 描述。
amdgpu_ps
用于 Mesa/AMDPAL 像素着色器。..TODO:: 描述。
amdgpu_vs
用于 Mesa/AMDPAL 光栅化之前的最后一个着色器阶段(如果 tessellation 和几何图形未在使用中,则为顶点着色器;否则,如果需要,则为复制着色器)。..TODO:: 描述。
AMDGPU MCExpr¶
作为 AMDGPU MC 层的一部分,AMDGPU 提供了以下目标特定的 MCExpr
。
表 30 AMDGPU MCExpr 类型:¶ MCExpr
操作数
返回值
max(arg, ...)
1 个或多个
可变参数有符号运算,返回其所有参数的最大值。
or(arg, ...)
1 个或多个
可变参数有符号运算,返回其所有参数的按位或结果。
函数资源使用情况¶
函数的资源使用情况取决于其每个被调用者的资源使用情况。用于表示资源使用情况的表达式通过传播每个被调用者的等效表达式来反映这一点。当编译为汇编或对象格式时,这些表达式由编译器作为符号发出,不应被覆盖或重新定义。
以下描述了所有发出的函数资源使用情况符号
表 31 函数资源使用情况:¶ 符号
类型
描述
示例
<function_name>.num_vgpr
整数
<function_name> 使用的 VGPR 数量,自身及其被调用者的 VGPR 使用的最坏情况
.set foo.num_vgpr, max(32, bar.num_vgpr, baz.num_vgpr)
<function_name>.num_agpr
整数
<function_name> 使用的 AGPR 数量,自身及其被调用者的 AGPR 使用的最坏情况
.set foo.num_agpr, max(35, bar.num_agpr)
<function_name>.numbered_sgpr
整数
<function_name> 使用的 SGPR 数量,自身及其被调用者的 SGPR 使用的最坏情况(不包括任何隐式使用的 SGPR)
.set foo.num_sgpr, 21
<function_name>.private_seg_size
整数
<function_name> 所需的总堆栈大小,表达式为本地使用的堆栈大小 + 最坏情况的被调用者
.set foo.private_seg_size, 16+max(bar.private_seg_size, baz.private_seg_size)
<function_name>.uses_vcc
布尔值
<function_name> 或其任何被调用者是否使用 vcc
.set foo.uses_vcc, or(0, bar.uses_vcc)
<function_name>.uses_flat_scratch
布尔值
<function_name> 或其任何被调用者是否使用 flat scratch
.set foo.uses_flat_scratch, 1
<function_name>.has_dyn_sized_stack
布尔值
<function_name> 或其任何被调用者是否为动态大小
.set foo.has_dyn_sized_stack, 1
<function_name>.has_recursion
布尔值
<function_name> 或其任何被调用者是否包含递归
.set foo.has_recursion, 0
<function_name>.has_indirect_call
布尔值
<function_name> 或其任何被调用者是否包含间接调用
.set foo.has_indirect_call, max(0, bar.has_indirect_call)
此外,还额外发出三个符号,描述编译单元的最坏情况(即最大值)num_vgpr
、num_agpr
和 numbered_sgpr
,这些符号可以被上述符号表达式引用和使用。这三个符号是 amdgcn.max_num_vgpr
、amdgcn.max_num_agpr
和 amdgcn.max_num_sgpr
。
ELF 代码对象¶
AMDGPU 后端生成标准的 ELF [ELF] 可重定位代码对象,该对象可以由 lld
链接,以生成标准的 ELF 共享代码对象,该对象可以在 AMDGPU 目标上加载和执行。
标头¶
AMDGPU 后端使用以下 ELF 标头
表 32 AMDGPU ELF 标头¶ 字段
值
e_ident[EI_CLASS]
ELFCLASS64
e_ident[EI_DATA]
ELFDATA2LSB
e_ident[EI_OSABI]
ELFOSABI_NONE
ELFOSABI_AMDGPU_HSA
ELFOSABI_AMDGPU_PAL
ELFOSABI_AMDGPU_MESA3D
e_ident[EI_ABIVERSION]
ELFABIVERSION_AMDGPU_HSA_V2
ELFABIVERSION_AMDGPU_HSA_V3
ELFABIVERSION_AMDGPU_HSA_V4
ELFABIVERSION_AMDGPU_HSA_V5
ELFABIVERSION_AMDGPU_HSA_V6
ELFABIVERSION_AMDGPU_PAL
ELFABIVERSION_AMDGPU_MESA3D
e_type
ET_REL
ET_DYN
e_machine
EM_AMDGPU
e_entry
0
e_flags
请参阅 代码对象 V2 的 AMDGPU ELF 标头 e_flags、代码对象 V3 的 AMDGPU ELF 标头 e_flags、代码对象 V4 和 V5 的 AMDGPU ELF 标头 e_flags 以及 代码对象 V6 及更高版本的 AMDGPU ELF 标头 e_flags
表 33 AMDGPU ELF 标头枚举值¶ 名称
值
EM_AMDGPU
224
ELFOSABI_NONE
0
ELFOSABI_AMDGPU_HSA
64
ELFOSABI_AMDGPU_PAL
65
ELFOSABI_AMDGPU_MESA3D
66
ELFABIVERSION_AMDGPU_HSA_V2
0
ELFABIVERSION_AMDGPU_HSA_V3
1
ELFABIVERSION_AMDGPU_HSA_V4
2
ELFABIVERSION_AMDGPU_HSA_V5
3
ELFABIVERSION_AMDGPU_HSA_V6
4
ELFABIVERSION_AMDGPU_PAL
0
ELFABIVERSION_AMDGPU_MESA3D
0
e_ident[EI_CLASS]
ELF 类是
ELFCLASS32
用于r600
架构。ELFCLASS64
用于amdgcn
架构,该架构仅支持 64 位进程地址空间应用程序。
e_ident[EI_DATA]
所有 AMDGPU 目标都使用
ELFDATA2LSB
进行小端字节序。e_ident[EI_OSABI]
以下 AMDGPU 目标架构特定的 OS ABI 之一(请参阅 AMDGPU 操作系统)
ELFOSABI_NONE
用于未知操作系统。ELFOSABI_AMDGPU_HSA
用于amdhsa
操作系统。ELFOSABI_AMDGPU_PAL
用于amdpal
操作系统。ELFOSABI_AMDGPU_MESA3D
用于mesa3D
操作系统。
e_ident[EI_ABIVERSION]
代码对象符合的 AMDGPU 目标架构特定 OS ABI 的 ABI 版本
ELFABIVERSION_AMDGPU_HSA_V2
用于指定代码对象 V2 的 AMD HSA 运行时 ABI 版本。此版本的 LLVM 不再可以发出。ELFABIVERSION_AMDGPU_HSA_V3
用于指定代码对象 V3 的 AMD HSA 运行时 ABI 版本。此版本的 LLVM 不再可以发出。ELFABIVERSION_AMDGPU_HSA_V4
用于指定代码对象 V4 的 AMD HSA 运行时 ABI 版本。使用 Clang 选项-mcode-object-version=4
指定。ELFABIVERSION_AMDGPU_HSA_V5
用于指定代码对象 V5 的 AMD HSA 运行时 ABI 版本。使用 Clang 选项-mcode-object-version=5
指定。如果未指定,则这是默认的代码对象版本。ELFABIVERSION_AMDGPU_HSA_V6
用于指定代码对象 V6 的 AMD HSA 运行时 ABI 版本。使用 Clang 选项-mcode-object-version=6
指定。ELFABIVERSION_AMDGPU_PAL
用于指定 AMD PAL 运行时 ABI 的版本。ELFABIVERSION_AMDGPU_MESA3D
用于指定 AMD MESA 3D 运行时 ABI 的版本。
e_type
可以是以下值之一
ET_REL
AMDGPU 后端编译器生成的类型,因为它是一个可重定位的代码对象。
ET_DYN
链接器生成的类型,因为它是一个共享代码对象。
AMD HSA 运行时加载器需要
ET_DYN
代码对象。e_machine
值
EM_AMDGPU
用于r600
和amdgcn
架构支持的所有处理器的机器(请参阅 AMDGPU 处理器)。对于代码对象 V2,特定处理器在NT_AMD_HSA_ISA_VERSION
note 记录中指定(请参阅 代码对象 V2 Note 记录),对于代码对象 V3 及更高版本,特定处理器在e_flags
的EF_AMDGPU_MACH
位字段中指定(请参阅 代码对象 V3 的 AMDGPU ELF 标头 e_flags、代码对象 V4 和 V5 的 AMDGPU ELF 标头 e_flags 和 代码对象 V6 及更高版本的 AMDGPU ELF 标头 e_flags)。e_entry
入口点为 0,因为必须选择各个内核的入口点才能通过 AQL 数据包调用它们。
e_flags
AMDGPU 后端使用以下 ELF 标头标志
表 34 代码对象 V2 的 AMDGPU ELF 标头 e_flags
¶名称
值
描述
EF_AMDGPU_FEATURE_XNACK_V2
0x01
指示是否为代码对象中包含的所有代码启用了
xnack
目标特性。如果处理器不支持xnack
目标特性,则必须为 0。请参阅 目标特性。EF_AMDGPU_FEATURE_TRAP_HANDLER_V2
0x02
指示是否为代码对象中包含的所有代码启用了 trap 处理程序。如果处理器不支持 trap 处理程序,则必须为 0。请参阅 目标特性。
表 35 代码对象 V3 的 AMDGPU ELF 标头 e_flags
¶名称
值
描述
EF_AMDGPU_MACH
0x0ff
AMDGPU 处理器选择掩码,用于 AMDGPU EF_AMDGPU_MACH 值 中定义的
EF_AMDGPU_MACH_xxx
值。EF_AMDGPU_FEATURE_XNACK_V3
0x100
指示是否为代码对象中包含的所有代码启用了
xnack
目标特性。如果处理器不支持xnack
目标特性,则必须为 0。请参阅 目标特性。EF_AMDGPU_FEATURE_SRAMECC_V3
0x200
指示是否为代码对象中包含的所有代码启用了
sramecc
目标特性。如果处理器不支持sramecc
目标特性,则必须为 0。请参阅 目标特性。表 36 代码对象 V4 和 V5 的 AMDGPU ELF 标头 e_flags
¶名称
值
描述
EF_AMDGPU_MACH
0x0ff
AMDGPU 处理器选择掩码,用于 AMDGPU EF_AMDGPU_MACH 值 中定义的
EF_AMDGPU_MACH_xxx
值。EF_AMDGPU_FEATURE_XNACK_V4
0x300
XNACK 选择掩码,用于
EF_AMDGPU_FEATURE_XNACK_*_V4
值。EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4
0x000
不支持 XNACK。
EF_AMDGPU_FEATURE_XNACK_ANY_V4
0x100
XNACK 可以具有任何值。
EF_AMDGPU_FEATURE_XNACK_OFF_V4
0x200
禁用 XNACK。
EF_AMDGPU_FEATURE_XNACK_ON_V4
0x300
启用 XNACK。
EF_AMDGPU_FEATURE_SRAMECC_V4
0xc00
SRAMECC 选择掩码,用于
EF_AMDGPU_FEATURE_SRAMECC_*_V4
值。EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4
0x000
不支持 SRAMECC。
EF_AMDGPU_FEATURE_SRAMECC_ANY_V4
0x400
SRAMECC 可以具有任何值。
EF_AMDGPU_FEATURE_SRAMECC_OFF_V4
0x800
禁用 SRAMECC。
EF_AMDGPU_FEATURE_SRAMECC_ON_V4
0xc00
启用 SRAMECC。
表 37 代码对象 V6 及更高版本的 AMDGPU ELF 标头 e_flags
¶名称
值
描述
EF_AMDGPU_MACH
0x0ff
AMDGPU 处理器选择掩码,用于 AMDGPU EF_AMDGPU_MACH 值 中定义的
EF_AMDGPU_MACH_xxx
值。EF_AMDGPU_FEATURE_XNACK_V4
0x300
XNACK 选择掩码,用于
EF_AMDGPU_FEATURE_XNACK_*_V4
值。EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4
0x000
不支持 XNACK。
EF_AMDGPU_FEATURE_XNACK_ANY_V4
0x100
XNACK 可以具有任何值。
EF_AMDGPU_FEATURE_XNACK_OFF_V4
0x200
禁用 XNACK。
EF_AMDGPU_FEATURE_XNACK_ON_V4
0x300
启用 XNACK。
EF_AMDGPU_FEATURE_SRAMECC_V4
0xc00
SRAMECC 选择掩码,用于
EF_AMDGPU_FEATURE_SRAMECC_*_V4
值。EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4
0x000
不支持 SRAMECC。
EF_AMDGPU_FEATURE_SRAMECC_ANY_V4
0x400
SRAMECC 可以具有任何值。
EF_AMDGPU_FEATURE_SRAMECC_OFF_V4
0x800
禁用 SRAMECC。
EF_AMDGPU_FEATURE_SRAMECC_ON_V4
0xc00
启用 SRAMECC。
EF_AMDGPU_GENERIC_VERSION_V
0xff000000
通用代码对象版本选择掩码。这是一个介于 1 和 255 之间的值,存储在 EFLAGS 的最高有效字节中。请参阅 通用处理器版本控制
表 38 AMDGPU EF_AMDGPU_MACH
值¶名称
值
描述(请参阅 AMDGPU 处理器)
EF_AMDGPU_MACH_NONE
0x000
未指定
EF_AMDGPU_MACH_R600_R600
0x001
r600
EF_AMDGPU_MACH_R600_R630
0x002
r630
EF_AMDGPU_MACH_R600_RS880
0x003
rs880
EF_AMDGPU_MACH_R600_RV670
0x004
rv670
EF_AMDGPU_MACH_R600_RV710
0x005
rv710
EF_AMDGPU_MACH_R600_RV730
0x006
rv730
EF_AMDGPU_MACH_R600_RV770
0x007
rv770
EF_AMDGPU_MACH_R600_CEDAR
0x008
cedar
EF_AMDGPU_MACH_R600_CYPRESS
0x009
cypress
EF_AMDGPU_MACH_R600_JUNIPER
0x00a
juniper
EF_AMDGPU_MACH_R600_REDWOOD
0x00b
redwood
EF_AMDGPU_MACH_R600_SUMO
0x00c
sumo
EF_AMDGPU_MACH_R600_BARTS
0x00d
barts
EF_AMDGPU_MACH_R600_CAICOS
0x00e
caicos
EF_AMDGPU_MACH_R600_CAYMAN
0x00f
cayman
EF_AMDGPU_MACH_R600_TURKS
0x010
turks
保留
0x011 - 0x01f
为
r600
架构处理器保留。EF_AMDGPU_MACH_AMDGCN_GFX600
0x020
gfx600
EF_AMDGPU_MACH_AMDGCN_GFX601
0x021
gfx601
EF_AMDGPU_MACH_AMDGCN_GFX700
0x022
gfx700
EF_AMDGPU_MACH_AMDGCN_GFX701
0x023
gfx701
EF_AMDGPU_MACH_AMDGCN_GFX702
0x024
gfx702
EF_AMDGPU_MACH_AMDGCN_GFX703
0x025
gfx703
EF_AMDGPU_MACH_AMDGCN_GFX704
0x026
gfx704
保留
0x027
保留。
EF_AMDGPU_MACH_AMDGCN_GFX801
0x028
gfx801
EF_AMDGPU_MACH_AMDGCN_GFX802
0x029
gfx802
EF_AMDGPU_MACH_AMDGCN_GFX803
0x02a
gfx803
EF_AMDGPU_MACH_AMDGCN_GFX810
0x02b
gfx810
EF_AMDGPU_MACH_AMDGCN_GFX900
0x02c
gfx900
EF_AMDGPU_MACH_AMDGCN_GFX902
0x02d
gfx902
EF_AMDGPU_MACH_AMDGCN_GFX904
0x02e
gfx904
EF_AMDGPU_MACH_AMDGCN_GFX906
0x02f
gfx906
EF_AMDGPU_MACH_AMDGCN_GFX908
0x030
gfx908
EF_AMDGPU_MACH_AMDGCN_GFX909
0x031
gfx909
EF_AMDGPU_MACH_AMDGCN_GFX90C
0x032
gfx90c
EF_AMDGPU_MACH_AMDGCN_GFX1010
0x033
gfx1010
EF_AMDGPU_MACH_AMDGCN_GFX1011
0x034
gfx1011
EF_AMDGPU_MACH_AMDGCN_GFX1012
0x035
gfx1012
EF_AMDGPU_MACH_AMDGCN_GFX1030
0x036
gfx1030
EF_AMDGPU_MACH_AMDGCN_GFX1031
0x037
gfx1031
EF_AMDGPU_MACH_AMDGCN_GFX1032
0x038
gfx1032
EF_AMDGPU_MACH_AMDGCN_GFX1033
0x039
gfx1033
EF_AMDGPU_MACH_AMDGCN_GFX602
0x03a
gfx602
EF_AMDGPU_MACH_AMDGCN_GFX705
0x03b
gfx705
EF_AMDGPU_MACH_AMDGCN_GFX805
0x03c
gfx805
EF_AMDGPU_MACH_AMDGCN_GFX1035
0x03d
gfx1035
EF_AMDGPU_MACH_AMDGCN_GFX1034
0x03e
gfx1034
EF_AMDGPU_MACH_AMDGCN_GFX90A
0x03f
gfx90a
保留
0x040
保留。
EF_AMDGPU_MACH_AMDGCN_GFX1100
0x041
gfx1100
EF_AMDGPU_MACH_AMDGCN_GFX1013
0x042
gfx1013
EF_AMDGPU_MACH_AMDGCN_GFX1150
0x043
gfx1150
EF_AMDGPU_MACH_AMDGCN_GFX1103
0x044
gfx1103
EF_AMDGPU_MACH_AMDGCN_GFX1036
0x045
gfx1036
EF_AMDGPU_MACH_AMDGCN_GFX1101
0x046
gfx1101
EF_AMDGPU_MACH_AMDGCN_GFX1102
0x047
gfx1102
EF_AMDGPU_MACH_AMDGCN_GFX1200
0x048
gfx1200
保留
0x049
保留。
EF_AMDGPU_MACH_AMDGCN_GFX1151
0x04a
gfx1151
保留
0x04b
保留。
EF_AMDGPU_MACH_AMDGCN_GFX942
0x04c
gfx942
保留
0x04d
保留。
EF_AMDGPU_MACH_AMDGCN_GFX1201
0x04e
gfx1201
EF_AMDGPU_MACH_AMDGCN_GFX950
0x04f
gfx950
保留
0x050
保留。
EF_AMDGPU_MACH_AMDGCN_GFX9_GENERIC
0x051
gfx9-generic
EF_AMDGPU_MACH_AMDGCN_GFX10_1_GENERIC
0x052
gfx10-1-generic
EF_AMDGPU_MACH_AMDGCN_GFX10_3_GENERIC
0x053
gfx10-3-generic
EF_AMDGPU_MACH_AMDGCN_GFX11_GENERIC
0x054
gfx11-generic
EF_AMDGPU_MACH_AMDGCN_GFX1152
0x055
gfx1152
.保留
0x056
保留。
保留
0x057
保留。
EF_AMDGPU_MACH_AMDGCN_GFX1153
0x058
gfx1153
.EF_AMDGPU_MACH_AMDGCN_GFX12_GENERIC
0x059
gfx12-generic
EF_AMDGPU_MACH_AMDGCN_GFX9_4_GENERIC
0x05f
gfx9-4-generic
节¶
AMDGPU 目标 ELF 代码对象具有标准的 ELF 节,其中包括
表 39 AMDGPU ELF 节¶ 名称
类型
属性
.bss
SHT_NOBITS
SHF_ALLOC
+SHF_WRITE
.data
SHT_PROGBITS
SHF_ALLOC
+SHF_WRITE
.debug_
*
SHT_PROGBITS
none
.dynamic
SHT_DYNAMIC
SHF_ALLOC
.dynstr
SHT_PROGBITS
SHF_ALLOC
.dynsym
SHT_PROGBITS
SHF_ALLOC
.got
SHT_PROGBITS
SHF_ALLOC
+SHF_WRITE
.hash
SHT_HASH
SHF_ALLOC
.note
SHT_NOTE
none
.rela
name
SHT_RELA
none
.rela.dyn
SHT_RELA
none
.rodata
SHT_PROGBITS
SHF_ALLOC
.shstrtab
SHT_STRTAB
none
.strtab
SHT_STRTAB
none
.symtab
SHT_SYMTAB
none
.text
SHT_PROGBITS
SHF_ALLOC
+SHF_EXECINSTR
这些节具有其标准含义(请参阅 [ELF]),并且仅在需要时生成。
.debug
*标准的 DWARF 节。有关 AMDGPU 后端生成的 DWARF 的信息,请参阅 DWARF 调试信息。
.dynamic
、.dynstr
、.dynsym
、.hash
动态加载器使用的标准节。
.note
有关 AMDGPU 后端支持的 note 记录,请参阅 Note 记录。
.rela
name、.rela.dyn
对于可重定位的代码对象,name 是应用重定位记录的节的名称。例如,
.rela.text
是与.text
节关联的重定位记录的节名称。对于链接的共享代码对象,
.rela.dyn
包含来自每个可重定位代码对象的.rela
name 节的所有重定位记录。有关 AMDGPU 后端支持的重定位记录,请参阅 重定位记录。
.text
内核和它们调用的函数的可执行机器代码。生成为位置无关代码。有关 isa 生成中使用的约定的信息,请参阅 代码约定。
.amdgpu.kernel.runtime.handle
用于设备排队的符号。
Note 记录¶
AMDGPU 后端代码对象在 .note
节中包含 ELF note 记录。生成的 note 集合及其语义取决于代码对象版本;请参阅 代码对象 V2 Note 记录 和 代码对象 V3 及更高版本的 Note 记录。
根据 ELFCLASS32
和 ELFCLASS64
的要求,必须在 name
字段后生成最少的零字节填充,以确保 desc
字段是 4 字节对齐的。此外,必须生成最少的零字节填充,以确保 desc
字段大小是 4 字节的倍数。.note
节的 sh_addralign
字段必须至少为 4,以指示至少 8 字节对齐。
代码对象 V2 Note 记录¶
警告
此版本的 LLVM 不再支持代码对象 V2 的生成。
当为代码对象 V2 编译时,AMDGPU 后端代码对象在 .note
节中使用以下 ELF note 记录。
note 记录 vendor 字段为 “AMD”。
可能存在其他 note 记录,但此处未记录的任何 note 记录都已弃用,不应使用。
表 41 AMDGPU 代码对象 V2 ELF 注释记录枚举值¶ 名称
值
NT_AMD_HSA_CODE_OBJECT_VERSION
1
NT_AMD_HSA_HSAIL
2
NT_AMD_HSA_ISA_VERSION
3
保留
4-9
NT_AMD_HSA_METADATA
10
NT_AMD_HSA_ISA_NAME
11
NT_AMD_HSA_CODE_OBJECT_VERSION
指定代码对象版本号。描述字段具有以下布局
struct amdgpu_hsa_note_code_object_version_s { uint32_t major_version; uint32_t minor_version; };
major_version
的值小于或等于 2。NT_AMD_HSA_HSAIL
指定 HSAIL Finalizer 使用的 HSAIL 属性。描述字段具有以下布局
struct amdgpu_hsa_note_hsail_s { uint32_t hsail_major_version; uint32_t hsail_minor_version; uint8_t profile; uint8_t machine_model; uint8_t default_float_round; };
NT_AMD_HSA_ISA_VERSION
指定目标 ISA 版本。描述字段具有以下布局
struct amdgpu_hsa_note_isa_s { uint16_t vendor_name_size; uint16_t architecture_name_size; uint32_t major; uint32_t minor; uint32_t stepping; char vendor_and_architecture_name[1]; };
vendor_name_size
和architecture_name_size
分别是供应商和架构名称的长度,包括 NUL 字符。vendor_and_architecture_name
包含供应商的 NUL 结尾字符串,后紧跟架构的 NUL 结尾字符串。此注释记录由 HSA 运行时加载器使用。
代码对象 V2 仅支持有限数量的处理器,并且针对目标功能具有固定设置。 有关处理器列表和相应的目标 ID,请参阅 AMDGPU 代码对象 V2 支持的处理器和固定目标功能设置。 在表中,注释记录 ISA 名称是供应商名称、架构名称、主版本号、次版本号和步进号的串联,并以“:”分隔。
目标 ID 列显示 LLVM 编译器使用的处理器名称和固定目标功能。 LLVM 编译器不生成
NT_AMD_HSA_HSAIL
注释记录。Finalizer 生成的代码对象也使用代码对象 V2,并且始终生成
NT_AMD_HSA_HSAIL
注释记录。 处理器名称和sramecc
目标功能如 AMDGPU 代码对象 V2 支持的处理器和固定目标功能设置 中所示,但xnack
目标功能由EF_AMDGPU_FEATURE_XNACK_V2
e_flags
位指定。NT_AMD_HSA_ISA_NAME
将目标 ISA 名称指定为非 NUL 结尾的字符串。
HSA 运行时加载器不使用此注释记录。
请参阅
NT_AMD_HSA_ISA_VERSION
注释记录描述,了解代码对象 V2 对处理器的有限支持以及目标功能的固定设置。有关从字符串到相应目标 ID 的映射,请参阅 AMDGPU 代码对象 V2 支持的处理器和固定目标功能设置。 如果支持并启用
xnack
目标功能,则 LLVM 编译器生成的字符串可能会附加+xnack
。 Finlizer 不执行附加操作,而是使用EF_AMDGPU_FEATURE_XNACK_V2
e_flags
位。NT_AMD_HSA_METADATA
指定与在 HSA [HSA] 兼容运行时上执行的代码对象关联的可扩展元数据(请参阅 AMDGPU 操作系统)。 当目标三元组 OS 为
amdhsa
时,这是必需的(请参阅 目标三元组)。 有关代码对象元数据字符串的语法,请参阅 代码对象 V2 元数据。表 42 AMDGPU 代码对象 V2 支持的处理器和固定目标功能设置¶ 注释记录 ISA 名称
目标 ID
AMD:AMDGPU:6:0:0
gfx600
AMD:AMDGPU:6:0:1
gfx601
AMD:AMDGPU:6:0:2
gfx602
AMD:AMDGPU:7:0:0
gfx700
AMD:AMDGPU:7:0:1
gfx701
AMD:AMDGPU:7:0:2
gfx702
AMD:AMDGPU:7:0:3
gfx703
AMD:AMDGPU:7:0:4
gfx704
AMD:AMDGPU:7:0:5
gfx705
AMD:AMDGPU:8:0:0
gfx802
AMD:AMDGPU:8:0:1
gfx801:xnack+
AMD:AMDGPU:8:0:2
gfx802
AMD:AMDGPU:8:0:3
gfx803
AMD:AMDGPU:8:0:4
gfx803
AMD:AMDGPU:8:0:5
gfx805
AMD:AMDGPU:8:1:0
gfx810:xnack+
AMD:AMDGPU:9:0:0
gfx900:xnack-
AMD:AMDGPU:9:0:1
gfx900:xnack+
AMD:AMDGPU:9:0:2
gfx902:xnack-
AMD:AMDGPU:9:0:3
gfx902:xnack+
AMD:AMDGPU:9:0:4
gfx904:xnack-
AMD:AMDGPU:9:0:5
gfx904:xnack+
AMD:AMDGPU:9:0:6
gfx906:sramecc-:xnack-
AMD:AMDGPU:9:0:7
gfx906:sramecc-:xnack+
AMD:AMDGPU:9:0:12
gfx90c:xnack-
代码对象 V3 及以上版本注释记录¶
当为代码对象 V3 及以上版本编译时,AMDGPU 后端代码对象在 .note
区段中使用以下 ELF 注释记录。
注释记录供应商字段为 “AMDGPU”。
可能存在其他 note 记录,但此处未记录的任何 note 记录都已弃用,不应使用。
表 44 AMDGPU 代码对象 V3 及以上版本 ELF 注释记录枚举值¶ 名称
值
保留
0-31
NT_AMDGPU_METADATA
32
NT_AMDGPU_KFD_CORE_STATE
33
NT_AMDGPU_METADATA
指定与 AMDGPU 代码对象关联的可扩展元数据。 它以 Message Pack [MsgPack] 二进制数据格式的映射形式进行编码。 有关为
amdhsa
OS 定义的映射键,请参阅 代码对象 V3 元数据、代码对象 V4 元数据 和 代码对象 V5 元数据。
符号¶
符号包括以下内容
表 45 AMDGPU ELF 符号¶ 名称
类型
区段
描述
链接名称
STT_OBJECT
.data
.rodata
.bss
全局变量
链接名称
.kd
STT_OBJECT
.rodata
内核描述符
链接名称
STT_FUNC
.text
内核入口点
链接名称
STT_OBJECT
SHN_AMDGPU_LDS
LDS 中的全局变量
- 全局变量
由编译单元使用和定义的全局变量。
如果符号在编译单元中定义,则根据其是否具有初始化数据或是否为只读,将其分配到相应的区段中。
如果符号是外部符号,则其区段为
STN_UNDEF
,并且加载器将使用另一个代码对象提供的定义或运行时显式定义的定义来解析重定位。如果符号驻留在本地/组内存 (LDS) 中,则其区段是特殊的处理器特定区段名称
SHN_AMDGPU_LDS
,并且st_value
字段描述对齐要求,就像通用符号一样。- 内核描述符
每个 HSA 内核都有一个关联的内核描述符。 在 AQL 调度数据包中用于调用内核的是内核描述符的地址,而不是内核入口点。 HSA 内核描述符的布局在 内核描述符 中定义。
- 内核入口点
每个 HSA 内核还在其机器代码入口点处有一个符号。
重定位记录¶
AMDGPU 后端为 AMDHSA 生成 Elf64_Rela
重定位记录,或为 Mesa/AMDPAL 生成 Elf64_Rel
重定位记录。 支持的重定位字段为
word32
这指定一个 32 位字段,占用 4 个字节,具有任意字节对齐方式。 这些值使用与 AMDGPU 架构中的其他字值相同的字节顺序。
word64
这指定一个 64 位字段,占用 8 个字节,具有任意字节对齐方式。 这些值使用与 AMDGPU 架构中的其他字值相同的字节顺序。
以下符号用于指定重定位计算
- A
表示用于计算可重定位字段值的加数。 如果加数字段小于 64 位,则将其零扩展为 64 位,以便在以下计算中使用。 (实际上,这仅影响 Mesa/AMDPAL 上的
_HI
重定位类型,其中加数来自 32 位字段,但计算结果取决于完整 64 位地址的高位。)- G
表示全局偏移表中的偏移量,在执行期间重定位条目的符号将驻留在该偏移量处。
- GOT
表示全局偏移表的地址。
- P
表示正在重定位的存储单元的位置(
et_rel
的区段偏移量或et_dyn
的地址)(使用r_offset
计算)。- S
表示索引位于重定位条目中的符号的值。 不使用此符号的重定位必须指定
STN_UNDEF
的符号索引。- B
表示已加载的可执行文件或共享对象的基地址,即 ELF 地址与实际加载地址之间的差值。 使用此符号的重定位仅在可执行文件或共享对象中有效。
支持以下重定位类型
表 46 AMDGPU ELF 重定位记录¶ 重定位类型
种类
值
字段
计算
R_AMDGPU_NONE
0
none
none
R_AMDGPU_ABS32_LO
静态,动态
1
word32
(S + A) & 0xFFFFFFFF
R_AMDGPU_ABS32_HI
静态,动态
2
word32
(S + A) >> 32
R_AMDGPU_ABS64
静态,动态
3
word64
S + A
R_AMDGPU_REL32
静态
4
word32
S + A - P
R_AMDGPU_REL64
静态
5
word64
S + A - P
R_AMDGPU_ABS32
静态,动态
6
word32
S + A
R_AMDGPU_GOTPCREL
静态
7
word32
G + GOT + A - P
R_AMDGPU_GOTPCREL32_LO
静态
8
word32
(G + GOT + A - P) & 0xFFFFFFFF
R_AMDGPU_GOTPCREL32_HI
静态
9
word32
(G + GOT + A - P) >> 32
R_AMDGPU_REL32_LO
静态
10
word32
(S + A - P) & 0xFFFFFFFF
R_AMDGPU_REL32_HI
静态
11
word32
(S + A - P) >> 32
保留
12
R_AMDGPU_RELATIVE64
动态
13
word64
B + A
R_AMDGPU_REL16
静态
14
word16
((S + A - P) - 4) / 4
R_AMDGPU_ABS32_LO
和 R_AMDGPU_ABS32_HI
仅受 mesa3d
OS 支持,该 OS 不支持 R_AMDGPU_ABS64
。
当前没有 OS 加载器支持 32 位程序,因此不使用 R_AMDGPU_ABS32
。
已加载代码对象路径统一资源标识符 (URI)¶
AMDGPU 代码对象加载器将从中加载代码对象的 ELF 共享对象的路径表示为文本统一资源标识符 (URI)。 请注意,代码对象是以内存加载的 ELF 共享对象重定位形式。 可以在同一进程中从同一 ELF 共享对象在不同的内存地址加载多个代码对象。
已加载代码对象路径 URI 语法由以下 BNF 语法定义
code_object_uri ::== file_uri | memory_uri
file_uri ::== "file://" file_path [ range_specifier ]
memory_uri ::== "memory://" process_id range_specifier
range_specifier ::== [ "#" | "?" ] "offset=" number "&" "size=" number
file_path ::== URI_ENCODED_OS_FILE_PATH
process_id ::== DECIMAL_NUMBER
number ::== HEX_NUMBER | DECIMAL_NUMBER | OCTAL_NUMBER
- number
是一个 C 整数文字,其中十六进制值以 “0x” 或 “0X” 为前缀,八进制值以 “0” 为前缀。
- file_path
是以 URI 编码的 UTF-8 字符串形式指定的文件路径。 在 URI 编码中,正则表达式
[a-zA-Z0-9/_.~-]
中不存在的每个字符都编码为两个大写十六进制数字,并以 “%” 开头。 路径中的目录以 “/” 分隔。- offset
是代码对象起始位置的从 0 开始的字节偏移量。 对于文件 URI,它来自
file_path
指定的文件开头,如果省略,则默认为 0。 对于内存 URI,它是内存地址并且是必需的。- size
是代码对象中的字节数。 对于文件 URI,如果省略,则默认为文件的大小。 对于内存 URI,这是必需的。
- process_id
是拥有内存的进程的标识。 对于 Linux,它是进程 ID (PID) 的 C 无符号整数十进制文字。
例如
file:///dir1/dir2/file1
file:///dir3/dir4/file2#offset=0x2000&size=3000
memory://1234#offset=0x20000&size=3000
DWARF 调试信息¶
警告
本节介绍对 AMDGPU DWARF [DWARF] 的临时支持,该支持当前尚未完全实现,并且可能会发生更改。
AMDGPU 生成 DWARF [DWARF] 调试信息 ELF 区段(请参阅 ELF 代码对象),其中包含将代码对象可执行代码和数据映射到源语言构造的信息。 调试器和分析器等工具可以使用它。 它使用 用于异构调试的 DWARF 扩展 中定义的功能,这些功能在 DWARF 版本 4 和 DWARF 版本 5 中作为 LLVM 供应商扩展提供。
本节定义 AMDGPU 目标架构特定的 DWARF 映射。
寄存器标识符¶
本节定义 DWARF 操作表达式(请参阅 DWARF 版本 5 第 2.5 节和 A.2.5.4 DWARF 操作表达式)和调用帧信息指令(请参阅 DWARF 版本 5 第 6.4 节和 A.6.4 调用帧信息)中使用的 AMDGPU 目标架构寄存器号。
单个代码对象可以包含用于具有不同 wavefront 大小的内核的代码。 向量寄存器和某些标量寄存器基于 wavefront 大小。 AMDGPU 为每个 wavefront 大小定义了不同的 DWARF 寄存器。 这简化了 DWARF 的使用者,以便每个寄存器都具有固定大小,而不是根据 wavefront 大小模式动态变化。 同样,为那些大小根据进程地址大小而变化的寄存器定义了不同的 DWARF 寄存器。 这允许使用者将特定的 AMDGPU 处理器视为单个架构,而不管其在运行时如何配置。 编译器显式指定与它正在生成的代码将要执行的模式匹配的 DWARF 寄存器。
DWARF 寄存器编码为数字,这些数字映射到架构寄存器。 AMDGPU 的映射在 AMDGPU DWARF 寄存器映射 中定义。 所有 AMDGPU 目标都使用相同的映射。
DWARF 寄存器 |
AMDGPU 寄存器 |
位大小 |
描述 |
---|---|---|---|
0 |
PC_32 |
32 |
在 32 位进程地址空间中执行时的程序计数器 (PC)。 在 CFI 中用于描述调用帧的 PC。 |
1 |
EXEC_MASK_32 |
32 |
在 wavefront 32 模式下执行时的执行掩码寄存器。 |
2-15 |
保留 |
保留用于使用 DWARF 快捷方式高度访问的寄存器。 |
|
16 |
PC_64 |
64 |
在 64 位进程地址空间中执行时的程序计数器 (PC)。 在 CFI 中用于描述调用帧的 PC。 |
17 |
EXEC_MASK_64 |
64 |
在 wavefront 64 模式下执行时的执行掩码寄存器。 |
18-31 |
保留 |
保留用于使用 DWARF 快捷方式高度访问的寄存器。 |
|
32-95 |
SGPR0-SGPR63 |
32 |
标量通用寄存器。 |
96-127 |
保留 |
保留用于使用 DWARF 1 字节 ULEB 频繁访问的寄存器。 |
|
128 |
STATUS |
32 |
状态寄存器。 |
129-511 |
保留 |
保留用于未来的标量架构寄存器。 |
|
512 |
VCC_32 |
32 |
在 wavefront 32 模式下执行时的向量条件代码寄存器。 |
513-767 |
保留 |
保留用于未来在 wavefront 32 模式下执行时的向量架构寄存器。 |
|
768 |
VCC_64 |
64 |
在 wavefront 64 模式下执行时的向量条件代码寄存器。 |
769-1023 |
保留 |
保留用于未来在 wavefront 64 模式下执行时的向量架构寄存器。 |
|
1024-1087 |
保留 |
保留用于填充。 |
|
1088-1129 |
SGPR64-SGPR105 |
32 |
标量通用寄存器。 |
1130-1535 |
保留 |
保留用于未来标量通用寄存器。 |
|
1536-1791 |
VGPR0-VGPR255 |
32*32 |
在 wavefront 32 模式下执行时的向量通用寄存器。 |
1792-2047 |
保留 |
保留用于未来在 wavefront 32 模式下执行时的向量通用寄存器。 |
|
2048-2303 |
AGPR0-AGPR255 |
32*32 |
在 wavefront 32 模式下执行时的向量累加寄存器。 |
2304-2559 |
保留 |
保留用于未来在 wavefront 32 模式下执行时的向量累加寄存器。 |
|
2560-2815 |
VGPR0-VGPR255 |
64*32 |
在 wavefront 64 模式下执行时的向量通用寄存器。 |
2816-3071 |
保留 |
保留用于未来在 wavefront 64 模式下执行时的向量通用寄存器。 |
|
3072-3327 |
AGPR0-AGPR255 |
64*32 |
在 wavefront 64 模式下执行时的向量累加寄存器。 |
3328-3583 |
保留 |
保留用于未来在 wavefront 64 模式下执行时的向量累加寄存器。 |
向量寄存器表示为 wavefront 的完整大小。 它们被组织为连续的双字(32 位),每个通道一个,其中最低有效位位置的双字对应于通道 0,依此类推。 涉及 DW_OP_LLVM_offset
和 DW_OP_LLVM_push_lane
操作的 DWARF 位置表达式用于选择向量寄存器的部分,该部分对应于在使用 SIMD 或 SIMT 执行模型实现的语言中执行当前执行线程的通道。
如果 wavefront 大小为 32 个通道,则使用 wavefront 32 模式寄存器定义。 如果 wavefront 大小为 64 个通道,则使用 wavefront 64 模式寄存器定义。 某些 AMDGPU 目标支持在 wavefront 32 和 wavefront 64 模式下执行。 将使用与生成的代码的 wavefront 模式相对应的寄存器定义。
如果生成的代码在 32 位进程地址空间中执行,则使用 32 位进程地址空间寄存器定义。 如果生成的代码在 64 位进程地址空间中执行,则使用 64 位进程地址空间寄存器定义。 amdgcn
目标仅支持 64 位进程地址空间。
内存空间标识符¶
DWARF 内存空间表示源语言内存空间。 请参阅 DWARF 版本 5 第 2.12 节,该节由 用于异构调试的 DWARF 扩展 部分 A.2.14 内存空间 更新。
用于 AMDGPU 的 DWARF 内存空间映射在 AMDGPU DWARF 内存空间映射 中定义。
DWARF |
AMDGPU |
|
---|---|---|
内存空间名称 |
值 |
内存空间 |
|
0x0000 |
通用(平面) |
|
0x0001 |
全局 |
|
0x0002 |
全局 |
|
0x0003 |
本地(组/LDS) |
|
0x0004 |
私有(暂存) |
|
0x8000 |
区域 (GDS) |
使用 用于异构调试的 DWARF 扩展 部分 A.2.14 内存空间 中定义的 DWARF 内存空间值。
此外,DW_ADDR_AMDGPU_region
被编码为供应商扩展。 这可用于 AMD 扩展,以访问硬件 GDS 内存,该内存是每个设备分配的暂存内存。
对于 AMDGPU,如果不存在 DW_AT_LLVM_memory_space
属性,则使用 DW_MSPACE_LLVM_none
的默认内存空间。
有关 DWARF 内存空间到 DWARF 地址空间的 AMDGPU 映射的信息,包括地址大小和 NULL 值,请参阅 地址空间标识符。
地址空间标识符¶
DWARF 地址空间对应于目标架构特定的线性可寻址内存区域。 请参阅 DWARF 版本 5 第 2.12 节和 用于异构调试的 DWARF 扩展 部分 A.2.13 地址空间。
用于 AMDGPU 的 DWARF 地址空间映射在 AMDGPU DWARF 地址空间映射 中定义。
DWARF |
AMDGPU |
注释 |
|||
---|---|---|---|---|---|
地址空间名称 |
值 |
地址 |
位大小 |
LLVM IR 地址空间 |
|
64 位进程地址空间 |
32 位进程地址空间 |
||||
|
0x00 |
64 |
32 |
全局 |
默认地址空间 |
|
0x01 |
64 |
32 |
通用(平面) |
|
|
0x02 |
32 |
32 |
区域 (GDS) |
|
|
0x03 |
32 |
32 |
本地(组/LDS) |
|
保留 |
0x04 |
||||
|
0x05 |
32 |
32 |
私有(暂存) |
聚焦通道 |
|
0x06 |
32 |
32 |
私有(暂存) |
非混淆 wavefront |
有关 AMDGPU LLVM IR 地址空间的信息,包括地址大小和 NULL 值,请参阅 地址空间。
DW_ASPACE_LLVM_none
地址空间是 DWARF 操作中使用的默认目标架构地址空间,这些操作未指定地址空间。 因此,它必须映射到全局地址空间,以便 DW_OP_addr*
和相关操作可以引用程序代码中的地址。
DW_ASPACE_AMDGPU_generic
地址空间允许位置表达式指定平面地址空间。 如果地址对应于本地地址空间中的地址,则它对应于正在执行聚焦执行线程的 wavefront。 如果地址对应于私有地址空间中的地址,则它对应于在使用 SIMD 或 SIMT 执行模型实现的语言中执行聚焦执行线程的通道。
注意
类似 CUDA 的语言(例如 HIP)在语言类型系统中没有地址空间,但允许变量分配在不同的地址空间中,需要在 DWARF 表达式操作中显式指定 DW_ASPACE_AMDGPU_generic
地址空间,因为默认地址空间是全局地址空间。
DW_ASPACE_AMDGPU_local
地址空间允许位置表达式指定与正在执行聚焦执行线程的 wavefront 相对应的本地地址空间。
DW_ASPACE_AMDGPU_private_lane
地址空间允许位置表达式指定与在使用 SIMD 或 SIMT 执行模型实现的语言中执行聚焦执行线程的通道相对应的私有地址空间。
DW_ASPACE_AMDGPU_private_wave
地址空间允许位置表达式指定与正在执行聚焦执行线程的 wavefront 相对应的非混淆私有地址空间。 私有内存的 wavefront 视图是 地址空间 中定义的每个 wavefront 非混淆后备内存布局,使得地址 0 对应于 wavefront 的后备内存的第一个位置(即,地址不偏移 wavefront-scratch-base
)。 以下公式可用于从 DW_ASPACE_AMDGPU_private_lane
地址转换为 DW_ASPACE_AMDGPU_private_wave
地址
private-address-wavefront =
((private-address-lane / 4) * wavefront-size * 4) +
(wavefront-lane-id * 4) + (private-address-lane % 4)
如果 DW_ASPACE_AMDGPU_private_lane
地址是双字对齐的,并且需要以通道 0 开头的每个通道的双字的开头,则这简化为
private-address-wavefront =
private-address-lane * wavefront-size
编译器可以使用 DW_ASPACE_AMDGPU_private_wave
地址空间将完整的溢出向量寄存器读回 CFI 中的完整向量寄存器。 帧指针可以是双字对齐的私有通道地址,可以对其进行移位以乘以 wavefront 大小,然后用于形成私有 wavefront 地址,该地址为一组连续的双字(每个通道一个)提供位置,向量寄存器双字溢出到该位置。 编译器知道 wavefront 大小,因为它生成代码。 请注意,地址类型可能必须转换,因为 DW_ASPACE_AMDGPU_private_lane
地址的大小可能小于 DW_ASPACE_AMDGPU_private_wave
地址的大小。
通道标识符¶
DWARF 通道标识符指定以 SIMD 或 SIMT 方式执行的硬件的目标架构通道位置,以及源语言在其上将其执行线程映射到这些通道的位置。 DWARF 通道标识符由 DW_OP_LLVM_push_lane
DWARF 表达式操作推送。 请参阅 DWARF 版本 5 第 2.5 节,该节由 用于异构调试的 DWARF 扩展 部分 A.2.5.4 DWARF 操作表达式 更新。
对于 AMDGPU,通道标识符对应于 wavefront 的硬件通道 ID。 它从 0 编号到 wavefront 大小减 1。
操作表达式¶
DWARF 表达式用于计算程序值和程序对象的位置。 请参阅 DWARF 版本 5 第 2.5 节和 A.2.5.4 DWARF 操作表达式。
DWARF 位置描述描述了如何访问包括内存和寄存器在内的存储。 在 AMDGPU 上访问存储时,字节按最低有效字节优先的顺序排列,位在字节内按最低有效位优先的顺序排列。
对于 AMDGPU CFI 表达式,DW_OP_LLVM_select_bit_piece
用于描述将执行掩码下溢出的向量寄存器展开到内存:零单位置描述是向量寄存器,而一单位置描述是溢出内存位置描述。 DW_OP_LLVM_form_aspace_address
用于指定内存位置描述的地址空间。
在 AMDGPU 表达式中,DW_OP_LLVM_select_bit_piece
由 DW_AT_LLVM_lane_pc
属性表达式使用,其中发散控制流由执行掩码控制。 未定义的位置描述与 DW_OP_LLVM_extend
一起使用,以指示通道在进入子程序时未处于活动状态。 有关示例,请参阅 DW_AT_LLVM_lane_pc。
基本类型转换¶
对于 AMDGPU 表达式,DW_OP_convert
可用于在不同地址空间中 DW_ATE_address
编码的基本类型之间进行转换。
当满足此处描述的所有相关条件时,转换的定义如 地址空间 中所述,否则会导致评估错误。
注意
对于不支持特定地址空间的目标,与该地址空间之间进行转换始终是评估错误。
对于支持通用地址空间的目标,当通用地址位于全局地址空间中时,定义了从 DW_ASPACE_AMDGPU_generic
到 DW_ASPACE_LLVM_none
的转换。 转换不需要更改地址的文字值。
当存在相关的硬件支持,已完成任何必需的硬件设置,并且通用地址位于相应的地址空间中时,定义了从 DW_ASPACE_AMDGPU_generic
到 DW_ASPACE_AMDGPU_local
、DW_ASPACE_AMDGPU_private_wave
或 DW_ASPACE_AMDGPU_private_lane
中的任何一个的转换。 转换为 DW_ASPACE_AMDGPU_private_lane
还需要上下文包含活动通道。
调试器信息条目属性¶
本节介绍 AMDGPU 如何使用某些调试器信息条目属性。 请参阅 DWARF 版本 5 第 3.3.5 节和 3.1.1 节中的内容,这些内容由 用于异构调试的 DWARF 扩展 部分 A.3.3.5 低级别信息 和 A.3.1.1 完整和部分编译单元条目 更新。
DW_AT_LLVM_lane_pc
¶
对于 AMDGPU,DW_AT_LLVM_lane_pc
属性用于指定 SIMT 线程的各个通道的程序位置。
如果通道是活动通道,则这将与当前程序位置相同。
如果通道处于非活动状态,但在进入子程序时处于活动状态,则这是通道的执行在子程序中概念性定位的程序位置。
如果通道在进入子程序时未激活,则这将是未定义的位置。客户端调试器可以通过检查通道是否在网格内关联工作组的范围内(考虑部分工作组)来检查通道是否是有效工作组的一部分。如果不是,则调试器可以省略该通道的任何信息。否则,调试器可以反复展开堆栈并检查调用子程序的 DW_AT_LLVM_lane_pc
,直到找到非未定义的位置。从概念上讲,通道仅具有具有非未定义 DW_AT_LLVM_lane_pc
的调用帧。
以下示例说明了 AMDGPU 后端如何为以下子程序伪代码的嵌套 IF/THEN/ELSE
结构生成 DWARF 位置列表表达式,目标平台为每个 wavefront 包含 64 个通道。
1SUBPROGRAM X
2BEGIN
3 a;
4 IF (c1) THEN
5 b;
6 IF (c2) THEN
7 c;
8 ELSE
9 d;
10 ENDIF
11 e;
12 ELSE
13 f;
14 ENDIF
15 g;
16END
AMDGPU 后端可能会生成以下伪 LLVM MIR 来操作执行掩码 (EXEC
) 以线性化控制流。条件被评估以生成一个掩码,该掩码表示条件评估为真的通道。首先,通过将 EXEC
掩码设置为当前 EXEC
掩码与条件掩码的逻辑 AND
,来执行 THEN
区域。然后,通过否定 EXEC
掩码以及在区域开始时保存的 EXEC
掩码的逻辑 AND
,来执行 ELSE
区域。在 IF/THEN/ELSE
区域之后,EXEC
掩码将恢复为其在该区域开始时的值。如下所示。其他方法也是可能的,但基本概念是相同的。
1$lex_start:
2 a;
3 %1 = EXEC
4 %2 = c1
5$lex_1_start:
6 EXEC = %1 & %2
7$if_1_then:
8 b;
9 %3 = EXEC
10 %4 = c2
11$lex_1_1_start:
12 EXEC = %3 & %4
13$lex_1_1_then:
14 c;
15 EXEC = ~EXEC & %3
16$lex_1_1_else:
17 d;
18 EXEC = %3
19$lex_1_1_end:
20 e;
21 EXEC = ~EXEC & %1
22$lex_1_else:
23 f;
24 EXEC = %1
25$lex_1_end:
26 g;
27$lex_end:
为了创建 DWARF 位置列表表达式,该表达式定义通道程序位置向量的位置描述,可以使用 LLVM MIR DBG_VALUE
伪指令来注释线性化的控制流。这可以通过为通道 PC 定义一个人工变量来完成。为其创建的 DWARF 位置列表表达式用作子程序的调试信息条目上 DW_AT_LLVM_lane_pc
属性的值。
为每个良好嵌套的结构化控制流区域定义了一个 DWARF 过程,如果通道未激活(即它是发散的),则该过程为通道提供概念性的通道程序位置。每个区域的 DWARF 操作表达式在概念上继承了紧邻的封闭区域的值,并根据该区域的语义对其进行修改。
对于 IF/THEN/ELSE
区域,由于 THEN
区域首先执行,因此发散程序位置位于 THEN
区域的开始处。对于 ELSE
区域,由于 THEN
区域已完成,因此发散程序位置位于 IF/THEN/ELSE
区域的末尾。
通道 PC 人工变量在每个区域转换时被赋值。它使用紧邻的封闭区域的 DWARF 过程来计算每个通道的程序位置(假设它们是发散的),然后通过插入当前程序位置来修改结果,对于每个通道,EXEC
掩码指示它们处于活动状态。
通过为每个区域设置单独的 DWARF 过程,它们可以被重用来定义任何嵌套区域的值。这减少了 DWARF 操作表达式的总大小。
以下提供了使用伪 LLVM MIR 的示例。
1$lex_start:
2 DEFINE_DWARF %__uint_64 = DW_TAG_base_type[
3 DW_AT_name = "__uint64";
4 DW_AT_byte_size = 8;
5 DW_AT_encoding = DW_ATE_unsigned;
6 ];
7 DEFINE_DWARF %__active_lane_pc = DW_TAG_dwarf_procedure[
8 DW_AT_name = "__active_lane_pc";
9 DW_AT_location = [
10 DW_OP_regx PC;
11 DW_OP_LLVM_extend 64, 64;
12 DW_OP_regval_type EXEC, %uint_64;
13 DW_OP_LLVM_select_bit_piece 64, 64;
14 ];
15 ];
16 DEFINE_DWARF %__divergent_lane_pc = DW_TAG_dwarf_procedure[
17 DW_AT_name = "__divergent_lane_pc";
18 DW_AT_location = [
19 DW_OP_LLVM_undefined;
20 DW_OP_LLVM_extend 64, 64;
21 ];
22 ];
23 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
24 DW_OP_call_ref %__divergent_lane_pc;
25 DW_OP_call_ref %__active_lane_pc;
26 ];
27 a;
28 %1 = EXEC;
29 DBG_VALUE %1, $noreg, %__lex_1_save_exec;
30 %2 = c1;
31$lex_1_start:
32 EXEC = %1 & %2;
33$lex_1_then:
34 DEFINE_DWARF %__divergent_lane_pc_1_then = DW_TAG_dwarf_procedure[
35 DW_AT_name = "__divergent_lane_pc_1_then";
36 DW_AT_location = DIExpression[
37 DW_OP_call_ref %__divergent_lane_pc;
38 DW_OP_addrx &lex_1_start;
39 DW_OP_stack_value;
40 DW_OP_LLVM_extend 64, 64;
41 DW_OP_call_ref %__lex_1_save_exec;
42 DW_OP_deref_type 64, %__uint_64;
43 DW_OP_LLVM_select_bit_piece 64, 64;
44 ];
45 ];
46 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
47 DW_OP_call_ref %__divergent_lane_pc_1_then;
48 DW_OP_call_ref %__active_lane_pc;
49 ];
50 b;
51 %3 = EXEC;
52 DBG_VALUE %3, %__lex_1_1_save_exec;
53 %4 = c2;
54$lex_1_1_start:
55 EXEC = %3 & %4;
56$lex_1_1_then:
57 DEFINE_DWARF %__divergent_lane_pc_1_1_then = DW_TAG_dwarf_procedure[
58 DW_AT_name = "__divergent_lane_pc_1_1_then";
59 DW_AT_location = DIExpression[
60 DW_OP_call_ref %__divergent_lane_pc_1_then;
61 DW_OP_addrx &lex_1_1_start;
62 DW_OP_stack_value;
63 DW_OP_LLVM_extend 64, 64;
64 DW_OP_call_ref %__lex_1_1_save_exec;
65 DW_OP_deref_type 64, %__uint_64;
66 DW_OP_LLVM_select_bit_piece 64, 64;
67 ];
68 ];
69 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
70 DW_OP_call_ref %__divergent_lane_pc_1_1_then;
71 DW_OP_call_ref %__active_lane_pc;
72 ];
73 c;
74 EXEC = ~EXEC & %3;
75$lex_1_1_else:
76 DEFINE_DWARF %__divergent_lane_pc_1_1_else = DW_TAG_dwarf_procedure[
77 DW_AT_name = "__divergent_lane_pc_1_1_else";
78 DW_AT_location = DIExpression[
79 DW_OP_call_ref %__divergent_lane_pc_1_then;
80 DW_OP_addrx &lex_1_1_end;
81 DW_OP_stack_value;
82 DW_OP_LLVM_extend 64, 64;
83 DW_OP_call_ref %__lex_1_1_save_exec;
84 DW_OP_deref_type 64, %__uint_64;
85 DW_OP_LLVM_select_bit_piece 64, 64;
86 ];
87 ];
88 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
89 DW_OP_call_ref %__divergent_lane_pc_1_1_else;
90 DW_OP_call_ref %__active_lane_pc;
91 ];
92 d;
93 EXEC = %3;
94$lex_1_1_end:
95 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
96 DW_OP_call_ref %__divergent_lane_pc;
97 DW_OP_call_ref %__active_lane_pc;
98 ];
99 e;
100 EXEC = ~EXEC & %1;
101$lex_1_else:
102 DEFINE_DWARF %__divergent_lane_pc_1_else = DW_TAG_dwarf_procedure[
103 DW_AT_name = "__divergent_lane_pc_1_else";
104 DW_AT_location = DIExpression[
105 DW_OP_call_ref %__divergent_lane_pc;
106 DW_OP_addrx &lex_1_end;
107 DW_OP_stack_value;
108 DW_OP_LLVM_extend 64, 64;
109 DW_OP_call_ref %__lex_1_save_exec;
110 DW_OP_deref_type 64, %__uint_64;
111 DW_OP_LLVM_select_bit_piece 64, 64;
112 ];
113 ];
114 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc, DIExpression[
115 DW_OP_call_ref %__divergent_lane_pc_1_else;
116 DW_OP_call_ref %__active_lane_pc;
117 ];
118 f;
119 EXEC = %1;
120$lex_1_end:
121 DBG_VALUE $noreg, $noreg, %DW_AT_LLVM_lane_pc DIExpression[
122 DW_OP_call_ref %__divergent_lane_pc;
123 DW_OP_call_ref %__active_lane_pc;
124 ];
125 g;
126$lex_end:
DWARF 过程 %__active_lane_pc
用于使用当前程序位置更新处于活动状态的通道 pc 元素。
人工变量 %__lex_1_save_exec 和 %__lex_1_1_save_exec 是为区域入口处保存的执行掩码创建的。使用 DBG_VALUE
伪指令,将创建位置列表条目,用于描述人工变量在任何给定程序位置的分配位置。编译器可以将它们分配给寄存器或溢出到内存中。
每个区域的 DWARF 过程都使用保存的执行掩码人工变量的值,仅更新在进入区域时处于活动状态的通道。所有其他通道都保留它们上次处于活动状态的封闭区域的值。如果它们在进入子程序时未激活,则将具有未定义的位置描述。
其他结构化控制流区域可以类似地处理。例如,循环会将区域的发散程序位置设置在循环的末尾。任何活动的通道都将在循环中,而任何未活动的通道必定已退出循环。
IF/THEN/ELSEIF/ELSEIF/...
区域可以被视为 IF/THEN/ELSE
区域的嵌套。
DWARF 过程可以使用 DW_AT_LLVM_active_lane 中描述的活动通道人工变量,而不是实际的 EXEC
掩码,以便支持整个或四路 wavefront 模式。
DW_AT_LLVM_active_lane
¶
子程序调试信息条目上的 DW_AT_LLVM_active_lane
属性用于指定对于 SIMT 线程概念上处于活动状态的通道。
可以修改执行掩码以实现整个或四路 wavefront 模式操作。例如,可能需要暂时激活所有通道以执行整个 wavefront 操作。此类区域将保存 EXEC
掩码,更新它以启用必要的通道,执行操作,然后从保存的值中恢复 EXEC
掩码。在执行整个 wavefront 区域时,概念上的执行掩码是保存的值,而不是 EXEC
值。
这可以通过为活动通道掩码定义人工变量来处理。活动通道掩码人工变量对于正常区域将是实际的 EXEC
掩码,对于临时更新掩码的区域将是保存的执行掩码。为此人工变量创建的位置列表表达式用于定义 DW_AT_LLVM_active_lane
属性的值。
DW_AT_LLVM_augmentation
¶
对于 AMDGPU,编译单元调试信息条目的 DW_AT_LLVM_augmentation
属性对于增强字符串具有以下值
[amdgpu:v0.0]
“vX.Y” 指定编译单元的 DWARF 中使用的 AMDGPU 扩展的主版本号 X 和次版本号 Y。版本号符合 [SEMVER]。
调用帧信息¶
DWARF 调用帧信息 (CFI) 描述了消费者如何在运行进程或核心转储中虚拟地展开调用帧。请参阅 DWARF 版本 5 第 6.4 节和 A.6.4 调用帧信息。
对于 AMDGPU,公共信息条目 (CIE) 字段具有以下值
augmentation
字符串包含以下以 null 结尾的 UTF-8 字符串[amd:v0.0]
vX.Y
指定此 CIE 中或使用它的 FDE 中使用的 AMDGPU 扩展的主版本号 X 和次版本号 Y。版本号符合 [SEMVER]。address_size
对于Global
地址空间在 地址空间标识符 中定义。segment_selector_size
为 0,因为 AMDGPU 不使用段选择器。code_alignment_factor
为 4 字节。data_alignment_factor
为 4 字节。return_address_register
对于 32 位进程为PC_32
,对于 64 位进程为PC_64
,在 寄存器标识符 中定义。initial_instructions
由于寄存器较少的子程序 X 可以从分配了更多寄存器的子程序 Y 调用,因此 X 不会更改任何额外的寄存器,因为它无法访问它们。因此,所有列的默认规则是same value
。
对于 AMDGPU,寄存器号遵循 寄存器标识符 中定义的编号。
对于 AMDGPU,指令大小是可变的。消费者可以从返回地址中减去 1,以获取调用站点指令中字节的地址。请参阅 DWARF 版本 5 第 6.4.4 节。
加速访问¶
请参阅 DWARF 版本 5 第 6.1 节。
按名称查找节头¶
请参阅 DWARF 版本 5 第 6.1.1.4.1 节和 A.6.1.1 按名称查找。
对于 AMDGPU,按名称查找节头表
augmentation_string_size
(uword)
设置为
augmentation_string
值的长度,该值始终是 4 的倍数。
augmentation_string
(UTF-8 字符序列)
包含以下 UTF-8 字符串,该字符串以 null 填充到 4 字节的倍数
[amdgpu:v0.0]“vX.Y” 指定此索引的 DWARF 中使用的 AMDGPU 扩展的主版本号 X 和次版本号 Y。版本号符合 [SEMVER]。
注意
这与 DWARF 版本 5 定义的不同,后者要求前 4 个字符为供应商 ID。但这与其他增强字符串一致,并且确实允许多个供应商的贡献。但是,向后兼容性可能更可取。
按地址查找节头¶
请参阅 DWARF 版本 5 第 6.1.2 节。
对于 AMDGPU,按地址查找节头表
address_size
(ubyte)
匹配 地址空间标识符 中定义的
Global
地址空间的地址大小。
segment_selector_size
(ubyte)
AMDGPU 不使用段选择器,因此此值为 0。
.debug_aranges
中的条目没有段选择器。
行号信息¶
请参阅 DWARF 版本 5 第 6.2 节和 A.6.2 行号信息。
AMDGPU 不使用 isa
状态机寄存器,并始终将其设置为 0。指令集必须从 ELF 文件头 e_flags
字段中的 EF_AMDGPU_MACH
位位置获取(请参阅 ELF 头部)。请参阅 DWARF 版本 5 第 6.2.2 节。
对于 AMDGPU,行号程序头字段具有以下值(请参阅 DWARF 版本 5 第 6.2.4 节)
address_size
(ubyte)匹配 地址空间标识符 中定义的
Global
地址空间的地址大小。segment_selector_size
(ubyte)AMDGPU 不使用段选择器,因此此值为 0。
minimum_instruction_length
(ubyte)对于 GFX9-GFX11,此值为 4。
maximum_operations_per_instruction
(ubyte)对于 GFX9-GFX11,此值为 1。
在线编译程序(例如,由 OpenCL 语言运行时编译的程序)的源代码文本可以嵌入到 DWARF 版本 5 行表中。请参阅 DWARF 版本 5 第 6.2.4.1 节,该节由异构调试的 DWARF 扩展部分 DW_LNCT_LLVM_source 更新。
用于控制 AMDGPU 中源代码嵌入的 Clang 选项在 AMDGPU Clang 调试选项 中定义。
表 50 AMDGPU Clang 调试选项¶ 调试标志
描述
-g[no-]embed-source
启用/禁用在 DWARF 调试节中嵌入源代码文本。对于无法将源代码写入磁盘的环境(例如,执行在线编译时)很有用。
例如
-gembed-source
启用嵌入式源代码。
-gno-embed-source
禁用嵌入式源代码。
32 位和 64 位 DWARF 格式¶
请参阅 DWARF 版本 5 第 7.4 节和 A.7.4 32 位和 64 位 DWARF 格式。
对于 AMDGPU
对于
amdgcn
目标架构,仅支持 64 位进程地址空间。生产者可以生成 32 位或 64 位 DWARF 格式。LLVM 生成 32 位 DWARF 格式。
单元头¶
对于 AMDGPU,以下值适用于 DWARF 版本 5 第 7.5.1.1、7.5.1.2 和 7.5.1.3 节中描述的每个单元头
address_size
(ubyte)匹配 地址空间标识符 中定义的
Global
地址空间的地址大小。
代码约定¶
本节提供用于每个受支持的目标三元组 OS 的代码约定(请参阅 目标三元组)。
AMDHSA¶
本节提供当目标三元组 OS 为 amdhsa
时使用的代码约定(请参阅 目标三元组)。
代码对象元数据¶
代码对象元数据指定与在 HSA [HSA] 兼容的运行时上执行的代码对象关联的可扩展元数据(请参阅 AMDGPU 操作系统)。此元数据的编码和语义取决于代码对象版本;请参阅 代码对象 V2 元数据、代码对象 V3 元数据、代码对象 V4 元数据 和 代码对象 V5 元数据。
代码对象元数据在注释记录中指定(请参阅 注释记录),并且当目标三元组 OS 为 amdhsa
时是必需的(请参阅 目标三元组)。它必须包含支持 HSA 兼容运行时内核查询所需的最低信息。例如,调度数据包中需要的段大小。此外,高级语言运行时可能需要包含其他信息。例如,AMD OpenCL 运行时记录内核参数信息。
代码对象 V2 元数据¶
警告
此版本的 LLVM 不再支持代码对象 V2 的生成。
代码对象 V2 元数据由 NT_AMD_HSA_METADATA
注释记录指定(请参阅 代码对象 V2 注释记录)。
元数据指定为 YAML 格式的字符串(请参阅 [YAML] 和 YAML I/O)。
元数据表示为单个 YAML 文档,该文档由表 AMDHSA 代码对象 V2 元数据映射 和引用的表定义的映射组成。
对于布尔值,字符串值 false
和 true
分别用于 false 和 true。
可以将其他信息添加到映射中。为避免冲突,任何非 AMD 键名都应以“vendor-name.”为前缀。
表 51 AMDHSA 代码对象 V2 元数据映射¶ 字符串键
值类型
必需?
描述
“Version”
2 个整数的序列
必需
第一个整数是主版本。当前为 1。
第二个整数是次版本。当前为 0。
“Printf”
字符串序列
每个字符串都是关于 printf 函数调用的编码信息。编码信息组织为由冒号(‘:’)分隔的字段
ID:N:S[0]:S[1]:...:S[N-1]:FormatString
其中
ID
一个 32 位整数,作为每个 printf 函数调用的唯一 ID
N
一个 32 位整数,等于 printf 函数调用的参数数量减 1
S[i]
(其中 i = 0, 1, … , N-1)printf 函数调用的第 i 个 FormatString 参数的大小(以字节为单位)的 32 位整数
- FormatString
传递给 printf 函数调用的格式字符串。
“Kernels”
映射序列
必需
代码对象中每个内核的映射序列。有关映射的定义,请参阅 AMDHSA 代码对象 V2 内核元数据映射。
表 52 AMDHSA 代码对象 V2 内核元数据映射¶ 字符串键
值类型
必需?
描述
“Name”
字符串
必需
内核的源名称。
“SymbolName”
字符串
必需
内核描述符 ELF 符号的名称。
“Language”
字符串
内核的源语言。值包括
“OpenCL C”
“OpenCL C++”
“HCC”
“OpenMP”
“LanguageVersion”
2 个整数的序列
第一个整数是主版本。
第二个整数是次版本。
“Attrs”
映射
内核属性的映射。有关映射定义,请参阅 AMDHSA 代码对象 V2 内核属性元数据映射。
“Args”
映射序列
内核参数的映射序列。有关映射定义,请参阅 AMDHSA 代码对象 V2 内核参数元数据映射。
“CodeProps”
映射
与内核代码相关的属性映射。有关映射定义,请参阅 AMDHSA 代码对象 V2 内核代码属性元数据映射。
表 53 AMDHSA 代码对象 V2 内核属性元数据映射¶ 字符串键
值类型
必需?
描述
“ReqdWorkGroupSize”
3 个整数的序列
如果不是 0, 0, 0,则所有值必须 >=1,并且调度工作组大小 X、Y、Z 必须与指定值对应。默认为 0, 0, 0。
对应于 OpenCL
reqd_work_group_size
属性。“WorkGroupSizeHint”
3 个整数的序列
调度工作组大小 X、Y、Z 很可能为指定值。
对应于 OpenCL
work_group_size_hint
属性。“VecTypeHint”
字符串
标量或向量类型的名称。
对应于 OpenCL
vec_type_hint
属性。“RuntimeHandle”
字符串
与内核关联的外部符号名称。OpenCL 运行时为符号分配一个全局缓冲区,并将内核的地址保存到其中,该地址用于设备端排队。仅适用于设备端排队内核。
表 54 AMDHSA 代码对象 V2 内核参数元数据映射¶ 字符串键
值类型
必需?
描述
“Name”
字符串
内核参数名称。
“TypeName”
字符串
内核参数类型名称。
“Size”
整数
必需
内核参数大小(以字节为单位)。
“Align”
整数
必需
内核参数对齐(以字节为单位)。必须是 2 的幂。
“ValueKind”
字符串
必需
内核参数类型,指定如何设置相应的参数。值包括
- “ByValue”
参数直接复制到 kernarg 中。
- “GlobalBuffer”
全局地址空间指针,指向缓冲区数据,在 kernarg 中传递。
- “DynamicSharedPointer”
组地址空间指针,指向动态分配的 LDS,在 kernarg 中传递。
- “Sampler”
全局地址空间指针,指向 S#,在 kernarg 中传递。
- “Image”
全局地址空间指针,指向 T#,在 kernarg 中传递。
- “Pipe”
全局地址空间指针,指向 OpenCL 管道,在 kernarg 中传递。
- “Queue”
全局地址空间指针,指向 OpenCL 设备排队队列,在 kernarg 中传递。
- “HiddenGlobalOffsetX”
OpenCL 网格调度全局偏移量,用于 X 维度,在 kernarg 中传递。
- “HiddenGlobalOffsetY”
OpenCL 网格调度全局偏移量,用于 Y 维度,在 kernarg 中传递。
- “HiddenGlobalOffsetZ”
OpenCL 网格调度全局偏移量,用于 Z 维度,在 kernarg 中传递。
- “HiddenNone”
内核未使用的参数。需要为其留出空间,但不需要设置。
- “HiddenPrintfBuffer”
全局地址空间指针,指向运行时 printf 缓冲区,在 kernarg 中传递。与“HiddenHostcallBuffer”互斥。
- “HiddenHostcallBuffer”
全局地址空间指针,指向运行时 hostcall 缓冲区,在 kernarg 中传递。与“HiddenPrintfBuffer”互斥。
- “HiddenDefaultQueue”
全局地址空间指针,指向内核默认应使用的 OpenCL 设备排队队列,在 kernarg 中传递。
- “HiddenCompletionAction”
全局地址空间指针,用于帮助将排队内核链接到祖先树中,以确定父内核何时完成。
- “HiddenMultiGridSyncArg”
全局地址空间指针,用于多网格同步,在 kernarg 中传递。
“ValueType”
字符串
未使用且已弃用。不应再发出此项,但为了兼容性而接受。
“PointeeAlign”
整数
指针类型内核参数的被指向类型对齐(以字节为单位)。必须是 2 的幂。仅当“ValueKind”为“DynamicSharedPointer”时存在。
“AddrSpaceQual”
字符串
内核参数地址空间限定符。仅当“ValueKind”为“GlobalBuffer”或“DynamicSharedPointer”时存在。值为
“Private”
“Global”
“Constant”
“Local”
“Generic”
“Region”
“AccQual”
字符串
内核参数访问限定符。仅当“ValueKind”为“Image”或“Pipe”时存在。值为
“ReadOnly”
“WriteOnly”
“ReadWrite”
“ActualAccQual”
字符串
内核对内核参数执行的实际内存访问。仅当“ValueKind”为“GlobalBuffer”、“Image”或“Pipe”时存在。这可能比“AccQual”指示的更严格,以反映内核的实际操作。如果不存在,则运行时必须假设“AccQual”和“IsConst”暗示的内容。值为
“ReadOnly”
“WriteOnly”
“ReadWrite”
“IsConst”
布尔值
指示内核参数是否为 const 限定。仅当“ValueKind”为“GlobalBuffer”时存在。
“IsRestrict”
布尔值
指示内核参数是否为 restrict 限定。仅当“ValueKind”为“GlobalBuffer”时存在。
“IsVolatile”
布尔值
指示内核参数是否为 volatile 限定。仅当“ValueKind”为“GlobalBuffer”时存在。
“IsPipe”
布尔值
指示内核参数是否为 pipe 限定。仅当“ValueKind”为“Pipe”时存在。
表 55 AMDHSA 代码对象 V2 内核代码属性元数据映射¶ 字符串键
值类型
必需?
描述
“KernargSegmentSize”
整数
必需
内核参数段的大小(以字节为单位),该段保存内核参数的值。
“GroupSegmentFixedSize”
整数
必需
工作组所需组段内存量(以字节为单位)。这不包括内核调度时可能添加的任何动态分配的组段内存。
“PrivateSegmentFixedSize”
整数
必需
工作项所需的固定私有地址空间内存量(以字节为单位)。如果内核使用动态调用堆栈,则必须将额外的空间添加到此值中以用于调用堆栈。
“KernargSegmentAlign”
整数
必需
内核参数段中参数的最大字节对齐方式。必须是 2 的幂。
“WavefrontSize”
整数
必需
波前大小。必须是 2 的幂。
“NumSGPRs”
整数
必需
GFX6-GFX11 的波前使用的标量寄存器数量。这包括 VCC、Flat Scratch(GFX7-GFX10)和 XNACK(对于 GFX8-GFX10)的特殊 SGPR。它不包括启用陷阱处理程序时添加的 16 个 SGPR。它不会向上舍入到分配粒度。
“NumVGPRs”
整数
必需
GFX6-GFX11 的每个工作项使用的向量寄存器数量
“MaxFlatWorkGroupSize”
整数
必需
内核支持的最大扁平工作组大小(以工作项为单位)。如果非 0、0、0,则必须 >=1 且与 ReqdWorkGroupSize 一致。
“NumSpilledSGPRs”
整数
从标量寄存器到寄存器分配器创建的溢出位置的存储次数。
“NumSpilledVGPRs”
整数
从向量寄存器到寄存器分配器创建的溢出位置的存储次数。
代码对象 V3 元数据¶
警告
此 LLVM 版本不再支持代码对象 V3 生成。
代码对象 V3 及以上版本的元数据由 NT_AMDGPU_METADATA
注释记录指定(参见 代码对象 V3 及以上版本的注释记录)。
元数据表示为 Message Pack 格式化的二进制数据(参见 [MsgPack])。顶层是一个 Message Pack 映射,其中包括表 AMDHSA 代码对象 V3 元数据映射 和引用的表中定义的键。
可以将其他信息添加到映射中。为了避免冲突,任何键名都应以“vendor-name.”为前缀,其中 vendor-name
可以是供应商的名称和生成信息的特定供应商工具。当出现在由同一 vendor-name 添加的映射中时,前缀缩写为简单的“.”。
表 56 AMDHSA 代码对象 V3 元数据映射¶ 字符串键
值类型
必需?
描述
“amdhsa.version”
2 个整数的序列
必需
第一个整数是主版本。当前为 1。
第二个整数是次版本。当前为 0。
“amdhsa.printf”
字符串序列
每个字符串都是关于 printf 函数调用的编码信息。编码信息组织为由冒号(‘:’)分隔的字段
ID:N:S[0]:S[1]:...:S[N-1]:FormatString
其中
ID
一个 32 位整数,作为每个 printf 函数调用的唯一 ID
N
一个 32 位整数,等于 printf 函数调用的参数数量减 1
S[i]
(其中 i = 0, 1, … , N-1)printf 函数调用的第 i 个 FormatString 参数的大小(以字节为单位)的 32 位整数
- FormatString
传递给 printf 函数调用的格式字符串。
“amdhsa.kernels”
映射序列
必需
代码对象中每个内核的映射序列。有关该映射中包含的键的定义,请参见 AMDHSA 代码对象 V3 内核元数据映射。
表 57 AMDHSA 代码对象 V3 内核元数据映射¶ 字符串键
值类型
必需?
描述
“.name”
字符串
必需
内核的源名称。
“.symbol”
字符串
必需
内核描述符 ELF 符号的名称。
“.language”
字符串
内核的源语言。值包括
“OpenCL C”
“OpenCL C++”
“HCC”
“HIP”
“OpenMP”
“Assembler”
“.language_version”
2 个整数的序列
第一个整数是主版本。
第二个整数是次版本。
“.args”
映射序列
内核参数的映射序列。有关该映射中包含的键的定义,请参见 AMDHSA 代码对象 V3 内核参数元数据映射。
“.reqd_workgroup_size”
3 个整数的序列
如果不是 0, 0, 0,则所有值必须 >=1,并且调度工作组大小 X、Y、Z 必须与指定值对应。默认为 0, 0, 0。
对应于 OpenCL
reqd_work_group_size
属性。“.workgroup_size_hint”
3 个整数的序列
调度工作组大小 X、Y、Z 很可能为指定值。
对应于 OpenCL
work_group_size_hint
属性。“.vec_type_hint”
字符串
标量或向量类型的名称。
对应于 OpenCL
vec_type_hint
属性。“.device_enqueue_symbol”
字符串
与内核关联的外部符号名称。OpenCL 运行时为符号分配一个全局缓冲区,并将内核的地址保存到其中,该地址用于设备端排队。仅适用于设备端排队内核。
“.kernarg_segment_size”
整数
必需
内核参数段的大小(以字节为单位),该段保存内核参数的值。
“.group_segment_fixed_size”
整数
必需
工作组所需组段内存量(以字节为单位)。这不包括内核调度时可能添加的任何动态分配的组段内存。
“.private_segment_fixed_size”
整数
必需
工作项所需的固定私有地址空间内存量(以字节为单位)。如果内核使用动态调用堆栈,则必须将额外的空间添加到此值中以用于调用堆栈。
“.kernarg_segment_align”
整数
必需
内核参数段中参数的最大字节对齐方式。必须是 2 的幂。
“.wavefront_size”
整数
必需
波前大小。必须是 2 的幂。
“.sgpr_count”
整数
必需
GFX6-GFX9 的波前所需的标量寄存器数量。如果显式使用寄存器,或者如果显式使用编号更高的寄存器,则需要寄存器。这包括 VCC、Flat Scratch(GFX7-GFX9)和 XNACK(对于 GFX8-GFX9)的特殊 SGPR。它不包括启用陷阱处理程序时添加的 16 个 SGPR。它不会向上舍入到分配粒度。
“.vgpr_count”
整数
必需
GFX6-GFX9 的每个工作项所需的向量寄存器数量。如果显式使用寄存器,或者如果显式使用编号更高的寄存器,则需要寄存器。
“.agpr_count”
整数
必需
GFX90A、GFX908 的每个工作项所需的累加器寄存器数量。
“.max_flat_workgroup_size”
整数
必需
内核支持的最大扁平工作组大小(以工作项为单位)。如果非 0、0、0,则必须 >=1 且与 ReqdWorkGroupSize 一致。
“.sgpr_spill_count”
整数
从标量寄存器到寄存器分配器创建的溢出位置的存储次数。
“.vgpr_spill_count”
整数
从向量寄存器到寄存器分配器创建的溢出位置的存储次数。
“.kind”
字符串
内核的种类,具有以下值
- “normal”
常规内核。
- “init”
这些内核必须在加载包含的代码对象后调用,并且必须在调用同一代码对象中的任何 normal 和 fini 内核之前完成。
- “fini”
这些内核必须在卸载包含的代码对象之前调用,并在同一代码对象中的所有 init 和 normal 内核都已调用并完成后调用。
如果省略,则假定为“normal”。
“.max_num_work_groups_{x,y,z}”
整数
X、Y 和 Z 维度中启动的最大工作组数量。每个数字必须 >=1。
表 58 AMDHSA 代码对象 V3 内核参数元数据映射¶ 字符串键
值类型
必需?
描述
“.name”
字符串
内核参数名称。
“.type_name”
字符串
内核参数类型名称。
“.size”
整数
必需
内核参数大小(以字节为单位)。
“.offset”
整数
必需
内核参数偏移量(以字节为单位)。偏移量必须是参数所需对齐方式的倍数。
“.value_kind”
字符串
必需
内核参数类型,指定如何设置相应的参数。值包括
- “by_value”
参数直接复制到 kernarg 中。
- “global_buffer”
全局地址空间指针,指向缓冲区数据,在 kernarg 中传递。
- “dynamic_shared_pointer”
组地址空间指针,指向动态分配的 LDS,在 kernarg 中传递。
- “sampler”
全局地址空间指针,指向 S#,在 kernarg 中传递。
- “image”
全局地址空间指针,指向 T#,在 kernarg 中传递。
- “pipe”
全局地址空间指针,指向 OpenCL 管道,在 kernarg 中传递。
- “queue”
全局地址空间指针,指向 OpenCL 设备排队队列,在 kernarg 中传递。
- “hidden_global_offset_x”
OpenCL 网格调度全局偏移量,用于 X 维度,在 kernarg 中传递。
- “hidden_global_offset_y”
OpenCL 网格调度全局偏移量,用于 Y 维度,在 kernarg 中传递。
- “hidden_global_offset_z”
OpenCL 网格调度全局偏移量,用于 Z 维度,在 kernarg 中传递。
- “hidden_none”
内核未使用的参数。需要为其留出空间,但不需要设置。
- “hidden_printf_buffer”
指向运行时 printf 缓冲区的全局地址空间指针在 kernarg 中传递。在代码对象 V5 之前,与“hidden_hostcall_buffer”互斥。
- “hidden_hostcall_buffer”
指向运行时 hostcall 缓冲区的全局地址空间指针在 kernarg 中传递。在代码对象 V5 之前,与“hidden_printf_buffer”互斥。
- “hidden_default_queue”
全局地址空间指针,指向内核默认应使用的 OpenCL 设备排队队列,在 kernarg 中传递。
- “hidden_completion_action”
全局地址空间指针,用于帮助将排队内核链接到祖先树中,以确定父内核何时完成。
- “hidden_multigrid_sync_arg”
全局地址空间指针,用于多网格同步,在 kernarg 中传递。
“.value_type”
字符串
未使用且已弃用。不应再发出此项,但为了兼容性而接受。
“.pointee_align”
整数
指针类型内核参数的被指向类型对齐方式(以字节为单位)。必须是 2 的幂。仅当 “.value_kind” 为 “dynamic_shared_pointer” 时才存在。
“.address_space”
字符串
内核参数地址空间限定符。仅当 “.value_kind” 为 “global_buffer” 或 “dynamic_shared_pointer” 时才存在。值包括
“private”
“global”
“constant”
“local”
“generic”
“region”
“.access”
字符串
内核参数访问限定符。仅当 “.value_kind” 为 “image” 或 “pipe” 时才存在。值包括
“read_only”
“write_only”
“read_write”
“.actual_access”
字符串
内核对内核参数执行的实际内存访问。仅当 “.value_kind” 为 “global_buffer”、“image” 或 “pipe” 时才存在。这可能比 “.access” 指示的更严格,以反映内核实际执行的操作。如果不存在,则运行时必须假定 “.access” 和 “.is_const” 所暗示的内容。值包括
“read_only”
“write_only”
“read_write”
“.is_const”
布尔值
指示内核参数是否为 const 限定。仅当 “.value_kind” 为 “global_buffer” 时才存在。
“.is_restrict”
布尔值
指示内核参数是否为 restrict 限定。仅当 “.value_kind” 为 “global_buffer” 时才存在。
“.is_volatile”
布尔值
指示内核参数是否为 volatile 限定。仅当 “.value_kind” 为 “global_buffer” 时才存在。
“.is_pipe”
布尔值
指示内核参数是否为 pipe 限定。仅当 “.value_kind” 为 “pipe” 时才存在。
代码对象 V4 元数据¶
- . 警告:
代码对象 V4 不是此 LLVM 版本发出的默认代码对象版本。
代码对象 V4 元数据与 代码对象 V3 元数据 相同,但具有表 AMDHSA 代码对象 V4 元数据映射更改 中定义的更改和添加。
代码对象 V5 元数据¶
代码对象 V5 元数据与 代码对象 V4 元数据 相同,但具有表 AMDHSA 代码对象 V5 元数据映射更改、表 AMDHSA 代码对象 V5 内核元数据映射添加 和表 AMDHSA 代码对象 V5 内核参数元数据映射添加和更改 中定义的更改。
表 60 AMDHSA 代码对象 V5 元数据映射更改¶ 字符串键
值类型
必需?
描述
“amdhsa.version”
2 个整数的序列
必需
第一个整数是主版本。当前为 1。
第二个整数是次版本号。当前为 2。
表 61 AMDHSA 代码对象 V5 内核元数据映射添加¶ 字符串键
值类型
必需?
描述
“.uses_dynamic_stack”
布尔值
指示生成的机器代码是否使用动态大小的堆栈。
“.workgroup_processor_mode”
布尔值
(GFX10+) 控制 代码对象 V3 内核描述符 中的 ENABLE_WGP_MODE。
表 62 AMDHSA 代码对象 V5 内核属性元数据映射¶ 字符串键
值类型
必需?
描述
“.uniform_work_group_size”
整数
指示内核是否要求全局大小的每个维度都是工作组大小的相应维度的倍数。值 1 表示 true,值 0 表示 false。仅当值为 1 时才发出元数据。
表 63 AMDHSA 代码对象 V5 内核参数元数据映射添加和更改¶ 字符串键
值类型
必需?
描述
“.value_kind”
字符串
必需
内核参数种类,指定如何设置相应的参数。值包括:与代码对象 V3 元数据相同(参见 AMDHSA 代码对象 V3 内核参数元数据映射),并添加了以下内容
- “hidden_block_count_x”
X 维度的网格调度工作组计数在 kernarg 中传递。某些语言(例如 OpenCL)支持每个维度中的最后一个工作组是部分的。此计数仅包括非部分工作组计数。这与 AQL 调度数据包中的值不同,后者具有以工作项为单位的网格大小。
- “hidden_block_count_y”
Y 维度的网格调度工作组计数在 kernarg 中传递。某些语言(例如 OpenCL)支持每个维度中的最后一个工作组是部分的。此计数仅包括非部分工作组计数。这与 AQL 调度数据包中的值不同,后者具有以工作项为单位的网格大小。如果网格维度为 1,则必须为 1。
- “hidden_block_count_z”
Z 维度的网格调度工作组计数在 kernarg 中传递。某些语言(例如 OpenCL)支持每个维度中的最后一个工作组是部分的。此计数仅包括非部分工作组计数。这与 AQL 调度数据包中的值不同,后者具有以工作项为单位的网格大小。如果网格维度为 1 或 2,则必须为 1。
- “hidden_group_size_x”
X 维度的网格调度工作组大小在 kernarg 中传递。此大小仅适用于非部分工作组。这与 AQL 调度数据包工作组大小的值相同。
- “hidden_group_size_y”
Y 维度的网格调度工作组大小在 kernarg 中传递。此大小仅适用于非部分工作组。这与 AQL 调度数据包工作组大小的值相同。如果网格维度为 1,则必须为 1。
- “hidden_group_size_z”
Z 维度的网格调度工作组大小在 kernarg 中传递。此大小仅适用于非部分工作组。这与 AQL 调度数据包工作组大小的值相同。如果网格维度为 1 或 2,则必须为 1。
- “hidden_remainder_x”
X 维度的部分工作组的网格调度工作组大小(如果存在)。如果 X 维度中不存在部分工作组,则必须为零。
- “hidden_remainder_y”
Y 维度的部分工作组的网格调度工作组大小(如果存在)。如果 Y 维度中不存在部分工作组,则必须为零。
- “hidden_remainder_z”
Z 维度的部分工作组的网格调度工作组大小(如果存在)。如果 Z 维度中不存在部分工作组,则必须为零。
- “hidden_grid_dims”
网格调度维度。这与 AQL 调度数据包维度相同。必须是介于 1 和 3 之间的值。
- “hidden_heap_v1”
指向符合 malloc/free 设备库 V1 版本实现要求的已初始化内存缓冲区的全局地址空间指针。
- “hidden_dynamic_lds_size”
动态分配的 LDS 内存的大小在 kernarg 中传递。
- “hidden_private_base”
扁平寻址私有光圈基址的高 32 位。仅由 GFX8 使用,以允许在私有段和扁平地址之间进行转换。参见 扁平暂存。
- “hidden_shared_base”
扁平寻址共享光圈基址的高 32 位。仅由 GFX8 使用,以允许在共享段和扁平地址之间进行转换。参见 扁平暂存。
- “hidden_queue_ptr”
指向 ROCm 运行时
struct amd_queue_t
结构的全局内存地址空间指针,该结构用于关联调度 AQL 数据包的 HSA 队列。仅在 pre-GFX9 设备上对于陷阱处理程序 ABI 是必需的(参见 陷阱处理程序 ABI)。
内核调度¶
HSA 架构的排队语言 (AQL) 定义了一个用户空间内存接口,可用于以代理独立的方式控制内核的调度。代理可以使用 HSA 兼容运行时为其创建零个或多个 AQL 队列(参见 AMDGPU 操作系统),可以在其中放置 AQL 数据包(所有数据包均为 64 字节)。有关 AQL 队列机制和数据包布局,请参见 HSA 平台系统架构规范 [HSA]。
内核代理的数据包处理器负责检测和调度与其关联的 AQL 队列中的 HSA 内核。对于 AMD GPU,数据包处理器由硬件命令处理器 (CP)、异步调度控制器 (ADC) 和着色器处理器输入控制器 (SPI) 实现。
HSA 兼容运行时可用于分配 AQL 队列对象。它使用内核模式驱动程序来初始化 AQL 队列并在 CP 中注册。
要调度内核,请执行以下操作。这可以在 CPU 主机程序中发生,也可以从在 GPU 上执行的 HSA 内核发生。
获取指向要在其上执行内核的内核代理的 AQL 队列的指针。
获取指向要执行的内核的内核描述符(参见 内核描述符)的指针。它必须用于包含在代码对象中的内核,该代码对象由与 AQL 队列关联的内核代理上的 HSA 兼容运行时加载。
使用 HSA 兼容运行时分配器为具有内核代理的 kernarg 属性的内存区域分配内核参数的空间,该内核代理将执行内核。它必须至少 16 字节对齐。
内核参数值被分配给内核参数内存分配。布局在 HSA 程序员语言参考 [HSA] 中定义。对于 AMDGPU,内核执行直接访问内核参数内存,方式与访问常量内存的方式相同。(请注意,HSA 规范允许实现将内核参数内容复制到内核访问的另一个位置。)
在 AQL 队列上创建 AQL 内核调度数据包。HSA 兼容运行时 API 使用 64 位原子操作来保留 AQL 队列中数据包的空间。必须设置数据包,并且最终写入必须使用原子存储释放来设置数据包种类,以确保数据包内容对内核代理可见。AQL 定义了门铃信号机制,以通知内核代理 AQL 队列已更新。这些规则以及 AQL 队列和内核调度数据包的布局在 HSA 系统架构规范 [HSA] 中定义。
内核调度数据包包括有关实际调度的信息(例如网格和工作组大小),以及来自代码对象的有关内核的信息(例如段大小)。可以使用 HSA 兼容运行时查询内核符号来获取代码对象值,这些值记录在 代码对象元数据 中。
CP 执行微代码,并负责检测和设置 GPU 以执行内核调度的波前。
CP 确保当波前开始执行内核机器代码时,标量通用寄存器 (SGPR) 和向量通用寄存器 (VGPR) 按照机器代码的要求进行设置。所需的设置在 内核描述符 中定义。初始寄存器状态在 初始内核执行状态 中定义。
内核机器代码的序言(参见 内核序言)根据需要设置机器状态,然后再继续执行与内核对应的机器代码。
当内核调度完成执行时,CP 会发出内核调度数据包中指定的完成信号(如果非 0)。
内存空间¶
内存空间属性为
表 64 AMDHSA 内存空间¶ 内存空间名称
HSA 段名称
硬件名称
地址大小
NULL 值
私有
private
scratch
32
0x00000000
局部
group
LDS
32
0xFFFFFFFF
全局
global
global
64
0x0000000000000000
常量
constant
与全局相同
64
0x0000000000000000
通用
flat
flat
64
0x0000000000000000
区域
N/A
GDS
32
未为 AMDHSA 实现
全局和常量内存空间都使用全局虚拟地址,这与 CPU 使用的虚拟地址空间相同。但是,某些虚拟地址可能仅可由 CPU 访问,某些仅可由 GPU 访问,而某些可由两者访问。
使用常量内存空间表示数据在内核执行期间不会更改。这允许使用标量读取指令。向量和标量 L1 缓存会在每次内核调度执行之前失效易失性数据,以允许常量内存在内核调度之间更改值。
本地内存空间使用硬件本地数据存储 (LDS),该存储在硬件创建波前工作组时自动分配,并在工作组的所有波前终止时释放。数据存储 (DS) 指令可用于访问它。
私有内存空间使用硬件暂存内存支持。如果内核使用暂存,则硬件会分配内存,该内存使用波前通道 dword(4 字节)交错访问。从私有地址到物理地址的映射使用
wavefront-scratch-base + (private-address * wavefront-size * 4) + (wavefront-lane-id * 4)
波前暂存基址由波前确定的方式不同(参见 初始内核执行状态)。可以使用带有暂存缓冲区描述符和每个波前暂存偏移量的缓冲区指令、暂存指令或扁平指令以交错方式访问此内存。如果波前的每个通道访问相同的私有地址,则交错会导致访问相邻的 dword,因此需要获取更少的缓存行。除 GFX9-GFX11 中的扁平指令和暂存指令外,不支持多 dword 访问。
通用地址空间使用 GFX7-GFX11 中可用的硬件扁平地址支持。这使用两个固定范围的虚拟地址(私有和本地光圈),它们位于可寻址全局内存范围之外,以将扁平地址映射到私有或本地地址。
FLAT 指令可以采用扁平地址并访问全局、私有(暂存)和组(LDS)内存,具体取决于地址是否在光圈范围之一内。对暂存的扁平访问需要硬件光圈设置和内核序言中的设置(参见 扁平暂存)。对 LDS 的扁平访问需要硬件光圈设置和 M0 (GFX7-GFX8) 寄存器设置(参见 M0)。
要在段地址和扁平地址之间进行转换,可以使用光圈地址的基址。对于 GFX7-GFX8,这些在 HSA AQL 队列 中可用,其地址可以使用队列指针 SGPR 获取(参见 初始内核执行状态)。对于 GFX9-GFX11,光圈基址可直接作为内联常量寄存器 SRC_SHARED_BASE/LIMIT
和 SRC_PRIVATE_BASE/LIMIT
使用。在 64 位地址模式下,光圈大小为 2^32 字节,基址与 2^32 对齐,这使得从扁平地址到段地址或从段地址到扁平地址的转换更容易。
图像和采样器¶
由 HSA 兼容运行时创建的图像和采样器句柄(参见 AMDGPU 操作系统)分别是硬件 32 字节 V# 和 48 字节 S# 对象的 64 位地址。为了支持 HSA query_sampler
操作,使用了两个额外的 dword 来存储 HSA BRIG 枚举值,这些值无法从 S# 表示中轻易推导出来。
HSA 信号¶
由 HSA 兼容运行时创建的 HSA 信号句柄(参见 AMDGPU 操作系统)是在 CPU 和 GPU 均可访问的内存中分配的结构的 64 位地址。该结构由运行时定义,并且在不同版本之间可能会更改。例如,参见 [AMD-ROCm-github]。
HSA AQL 队列¶
HSA AQL 队列结构由 HSA 兼容运行时定义(参见 AMDGPU 操作系统),并且在不同版本之间可能会更改。例如,参见 [AMD-ROCm-github]。对于某些处理器,它包含实现某些语言功能(例如扁平地址光圈基址)所需的字段。它还包含 CP 使用的字段,例如管理暂存内存的分配。
内核描述符¶
内核描述符由 CP 启动内核执行所需的信息组成,包括实现内核的机器代码的入口点地址。
代码对象 V3 内核描述符¶
CP 微代码要求内核描述符以 64 字节对齐方式分配。
CP 用于 V3 之前的代码对象的字段也与 代码对象 V3 内核描述符 中指定的字段匹配。
表 65 代码对象 V3 内核描述符¶ 位
大小
字段名称
描述
31:0
4 字节
GROUP_SEGMENT_FIXED_SIZE
工作组所需的固定本地地址空间内存量(以字节为单位)。这不包括内核调度时可能添加的任何动态分配的本地地址空间内存。
63:32
4 字节
PRIVATE_SEGMENT_FIXED_SIZE
工作项所需的固定私有地址空间内存量(以字节为单位)。当无法预测时,代码对象 v4 和更旧版本将此值设置为高于最低要求。
95:64
4 字节
KERNARG_SIZE
AQL 调度数据包指向的 kernarg 内存的大小。kernarg 内存用于将参数传递给内核。
如果调度数据包中的 kernarg 指针为 NULL,则没有内核参数。
如果调度数据包中的 kernarg 指针不为 NULL 且此值为 0,则 kernarg 内存大小未指定。
如果调度数据包中的 kernarg 指针不为 NULL 且此值不为 0,则该值以字节为单位指定 kernarg 内存大小。建议提供一个值,因为它可能被 CP 用于优化使 kernarg 内存对内核代码可见。
127:96
4 字节
保留,必须为 0。
191:128
8 字节
KERNEL_CODE_ENTRY_BYTE_OFFSET
从内核描述符的基址到内核的入口点指令的字节偏移量(可能为负),该指令必须为 256 字节对齐。
351:192
20 字节
保留,必须为 0。
383:352
4 字节
COMPUTE_PGM_RSRC3
- GFX6-GFX9
保留,必须为 0。
- GFX90A, GFX942
CP 用于设置
COMPUTE_PGM_RSRC3
配置寄存器的计算着色器 (CS) 程序设置。参见 GFX90A、GFX942 的 compute_pgm_rsrc3。- GFX10-GFX11
CP 用于设置
COMPUTE_PGM_RSRC3
配置寄存器的计算着色器 (CS) 程序设置。参见 GFX10-GFX11 的 compute_pgm_rsrc3。- GFX12
CP 用于设置
COMPUTE_PGM_RSRC3
配置寄存器的计算着色器 (CS) 程序设置。参见 GFX12 的 compute_pgm_rsrc3。415:384
4 字节
COMPUTE_PGM_RSRC1
CP 用于设置
COMPUTE_PGM_RSRC1
配置寄存器的计算着色器 (CS) 程序设置。参见 GFX6-GFX12 的 compute_pgm_rsrc1。447:416
4 字节
COMPUTE_PGM_RSRC2
CP 用于设置
COMPUTE_PGM_RSRC2
配置寄存器的计算着色器 (CS) 程序设置。参见 GFX6-GFX12 的 compute_pgm_rsrc2。458:448
7 位
参见下面的单独位。
启用 SGPR 用户数据寄存器的设置(参见 初始内核执行状态)。
请求的 SGPR 用户数据寄存器的总数不得超过 16,并且必须与
compute_pgm_rsrc2.user_sgpr.user_sgpr_count
中的值匹配。超出 16 的任何请求都将被忽略。>448
1 位
ENABLE_SGPR_PRIVATE_SEGMENT _BUFFER
如果 AMDGPU 处理器 的目标属性列指定架构扁平暂存,则不支持,并且必须为 0。
>449
1 位
ENABLE_SGPR_DISPATCH_PTR
>450
1 位
ENABLE_SGPR_QUEUE_PTR
>451
1 位
ENABLE_SGPR_KERNARG_SEGMENT_PTR
>452
1 位
ENABLE_SGPR_DISPATCH_ID
>453
1 位
ENABLE_SGPR_FLAT_SCRATCH_INIT
如果 AMDGPU 处理器 的目标属性列指定架构扁平暂存,则不支持,并且必须为 0。
>454
1 位
ENABLE_SGPR_PRIVATE_SEGMENT _SIZE
457:455
3 位
保留,必须为 0。
458
1 位
ENABLE_WAVEFRONT_SIZE32
- GFX6-GFX9
保留,必须为 0。
- GFX10-GFX11
如果为 0,则以波前大小 64 模式执行。
如果为 1,则以本机波前大小 32 模式执行。
459
1 位
USES_DYNAMIC_STACK
指示生成的机器代码是否使用动态大小的堆栈。仅在代码对象 v5 和更高版本中设置。
463:460
4 位
保留,必须为 0。
470:464
7 位
KERNARG_PRELOAD_SPEC_LENGTH
- GFX6-GFX9
保留,必须为 0。
- GFX90A, GFX942
从 kernarg 段预加载到用户 SGPR 中的 dword 数量,在内核执行之前。(参见 预加载的内核参数)。
479:471
9 位
KERNARG_PRELOAD_SPEC_OFFSET
- GFX6-GFX9
保留,必须为 0。
- GFX90A, GFX942
kernarg 段中 dword 的偏移量,用于开始将数据预加载到用户 SGPR 中。(参见 预加载的内核参数)。
511:480
4 字节
保留,必须为 0。
512
总大小 64 字节。
表 66 GFX6-GFX12 的 compute_pgm_rsrc1¶ 位
大小
字段名称
描述
5:0
6 位
GRANULATED_WORKITEM_VGPR_COUNT
每个工作项使用的向量寄存器块数;粒度是设备特定的
- GFX6-GFX9
vgprs_used 0..256
max(0, ceil(vgprs_used / 4) - 1)
- GFX90A, GFX942
vgprs_used 0..512
- vgprs_used = align(arch_vgprs, 4)
acc_vgprs
max(0, ceil(vgprs_used / 8) - 1)
- GFX10-GFX12(波前大小 64)
max_vgpr 1..256
max(0, ceil(vgprs_used / 4) - 1)
- GFX10-GFX12(波前大小 32)
max_vgpr 1..256
max(0, ceil(vgprs_used / 8) - 1)
其中 vgprs_used 定义为显式引用的最高 VGPR 编号加一。
CP 使用它来设置
COMPUTE_PGM_RSRC1.VGPRS
。汇编器 从 .amdhsa_kernel 指令通过 .amdhsa_next_free_vgpr 嵌套指令提供的值,为选定的处理器自动计算此值(参见 AMDHSA 内核汇编器指令)。
9:6
4 位
GRANULATED_WAVEFRONT_SGPR_COUNT
波前使用的标量寄存器块数;粒度是设备特定的
- GFX6-GFX8
sgprs_used 0..112
max(0, ceil(sgprs_used / 8) - 1)
- GFX9
sgprs_used 0..112
2 * max(0, ceil(sgprs_used / 16) - 1)
- GFX10-GFX12
保留,必须为 0。(始终分配 128 个 SGPR。)
其中 sgprs_used 定义为显式引用的最高 SGPR 编号加一,加上目标特定的 VCC、FLAT_SCRATCH (GFX7+) 和 XNACK_MASK (GFX8+) 的特殊 SGPR 附加数量,以及任何其他目标特定限制。它不包括启用陷阱处理程序时添加的 16 个 SGPR。
目标特定限制和特殊 SGPR 布局在硬件文档中定义,可以在 处理器 表中找到。
CP 使用它来设置
COMPUTE_PGM_RSRC1.SGPRS
。对于选定的处理器,汇编器 会根据提供给 .amdhsa_kernel 指令的值,以及 .amdhsa_next_free_sgpr 和 .amdhsa_reserve_* 嵌套指令(参见 AMDHSA 内核汇编器指令)自动计算此值。
11:10
2 位
优先级
必须为 0。
以指定的优先级开始执行波前。
CP 负责填充
COMPUTE_PGM_RSRC1.PRIORITY
。13:12
2 位
FLOAT_ROUND_MODE_32
波前以指定的舍入模式开始执行单精度(32 位)浮点运算。
浮点舍入模式值在 浮点舍入模式枚举值 中定义。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FLOAT_MODE
。15:14
2 位
FLOAT_ROUND_MODE_16_64
波前以指定的舍入和反常值模式开始执行半精度/双精度(16 位和 64 位)浮点运算。
浮点舍入模式值在 浮点舍入模式枚举值 中定义。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FLOAT_MODE
。17:16
2 位
FLOAT_DENORM_MODE_32
波前以指定的反常值模式开始执行单精度(32 位)浮点运算。
浮点反常值模式值在 浮点反常值模式枚举值 中定义。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FLOAT_MODE
。19:18
2 位
FLOAT_DENORM_MODE_16_64
波前以指定的反常值模式开始执行半精度/双精度(16 位和 64 位)浮点运算。
浮点反常值模式值在 浮点反常值模式枚举值 中定义。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FLOAT_MODE
。20
1 位
PRIV
必须为 0。
在特权陷阱处理模式下开始执行波前。
CP 负责填充
COMPUTE_PGM_RSRC1.PRIV
。21
1 位
ENABLE_DX10_CLAMP
WG_RR_EN
- GFX9-GFX11
波前以启用 DX10 钳位模式开始执行。向量 ALU 使用此模式强制执行对 NaN 的 DX10 样式处理(设置后,将 NaN 钳位为零,否则传递 NaN)。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.DX10_CLAMP
。- GFX12
如果为 1,则波前相对于 SIMD 的其他波前以轮询方式调度。 否则,波前以最旧的年龄顺序调度。
CP 负责填充
COMPUTE_PGM_RSRC1.WG_RR_EN
。22
1 位
DEBUG_MODE
必须为 0。
在单步模式下开始执行波前。
CP 负责填充
COMPUTE_PGM_RSRC1.DEBUG_MODE
。23
1 位
ENABLE_IEEE_MODE
DISABLE_PERF
- GFX9-GFX11
波前以启用 IEEE 模式开始执行。支持异常标志收集的浮点操作码将根据 IEEE 754-2008 标准静默并传播 signaling-NaN 输入。 Min_dx10 和 max_dx10 由于 signaling-NaN 传播和静默而变得符合 IEEE 754-2008 标准。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.IEEE_MODE
。- GFX12
保留。 必须为 0。
24
1 位
BULKY
必须为 0。
仅允许一个工作组在计算单元上执行。
CP 负责填充
COMPUTE_PGM_RSRC1.BULKY
。25
1 位
CDBG_USER
必须为 0。
可用于控制调试代码的标志。
CP 负责填充
COMPUTE_PGM_RSRC1.CDBG_USER
。26
1 位
FP16_OVFL
- GFX6-GFX8
保留,必须为 0。
- GFX9-GFX12
波前以指定的 fp16 溢出模式开始执行。
如果为 0,则 fp16 溢出会生成 +/-INF 值。
如果为 1,则 fp16 溢出是 +/-INF 输入值或除以 0 的结果,则产生 +/-INF,否则将计算出的溢出钳位为 +/-MAX_FP16(如果适用)。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FP16_OVFL
。28:27
2 位
保留,必须为 0。
29
1 位
WGP_MODE
- GFX6-GFX9
保留,必须为 0。
- GFX10-GFX12
如果为 0,则在 CU 波前执行模式下执行工作组。
如果为 1,则在 WGP 波前执行模式下执行工作组。
参见 内存模型。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.WGP_MODE
。30
1 位
MEM_ORDERED
- GFX6-GFX9
保留,必须为 0。
- GFX10-GFX12
控制 s_waitcnt 的 vmcnt 和 vscnt 计数器的行为。
如果为 0,则 vmcnt 报告乱序完成的加载和带返回的原子操作与 sample 指令,vscnt 报告按顺序完成的存储和不带返回的原子操作。
如果为 1,则 vmcnt 报告按顺序完成的加载、带返回的原子操作和 sample 指令,vscnt 报告按顺序完成的存储和不带返回的原子操作。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.MEM_ORDERED
。31
1 位
FWD_PROGRESS
- GFX6-GFX9
保留,必须为 0。
- GFX10-GFX12
如果为 0,则使用最旧优先策略执行 SIMD 波前。
如果为 1,则执行 SIMD 波前以确保波前取得一些前进进展。
CP 使用此项来设置
COMPUTE_PGM_RSRC1.FWD_PROGRESS
。32
总大小 4 字节
表 67 适用于 GFX6-GFX12 的 compute_pgm_rsrc2¶ 位
大小
字段名称
描述
0
1 位
ENABLE_PRIVATE_SEGMENT
启用私有段的设置。
如果 AMDGPU 处理器 的“目标属性”列未指定架构化平面暂存,则启用 SGPR 波前暂存偏移系统寄存器的设置(参见 初始内核执行状态)。
如果 AMDGPU 处理器 的“目标属性”列指定架构化平面暂存,则启用 FLAT_SCRATCH 寄存器对的设置(参见 初始内核执行状态)。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.SCRATCH_EN
。5:1
5 位
USER_SGPR_COUNT
请求的 SGPR 用户数据寄存器总数。 此数字必须大于或等于启用的用户数据寄存器数量。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.USER_SGPR
。6
1 位
ENABLE_TRAP_HANDLER
- GFX6-GFX11
必须为 0。
此位表示
COMPUTE_PGM_RSRC2.TRAP_PRESENT
,如果运行时已安装陷阱处理程序,则 CP 会设置此位。- GFX12
保留,必须为 0。
7
1 位
ENABLE_SGPR_WORKGROUP_ID_X
启用 X 维度中工作组 ID 的系统 SGPR 寄存器的设置(参见 初始内核执行状态)。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.TGID_X_EN
。8
1 位
ENABLE_SGPR_WORKGROUP_ID_Y
启用 Y 维度中工作组 ID 的系统 SGPR 寄存器的设置(参见 初始内核执行状态)。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.TGID_Y_EN
。9
1 位
ENABLE_SGPR_WORKGROUP_ID_Z
启用 Z 维度中工作组 ID 的系统 SGPR 寄存器的设置(参见 初始内核执行状态)。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.TGID_Z_EN
。10
1 位
ENABLE_SGPR_WORKGROUP_INFO
启用工作组信息的系统 SGPR 寄存器的设置(参见 初始内核执行状态)。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.TGID_SIZE_EN
。12:11
2 位
ENABLE_VGPR_WORKITEM_ID
启用用于工作项 ID 的 VGPR 系统寄存器的设置。系统 VGPR 工作项 ID 枚举值 定义了这些值。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.TIDIG_CMP_CNT
。13
1 位
ENABLE_EXCEPTION_ADDRESS_WATCH
必须为 0。
波前以启用地址监视异常开始执行,当 L1 检测到线程访问感兴趣的地址时,会生成这些异常。
CP 负责根据运行时的请求,在
COMPUTE_PGM_RSRC2.EXCP_EN_MSB
中填充地址监视位。14
1 位
ENABLE_EXCEPTION_MEMORY
必须为 0。
波前以启用内存违规异常开始执行,当此波前发生来自 L1 或 LDS 的内存违规时(写入只读内存、未对齐的原子操作、LDS 地址超出范围、非法地址等),会生成这些异常。
CP 根据运行时的请求,在
COMPUTE_PGM_RSRC2.EXCP_EN_MSB
中设置内存违规位。23:15
9 位
GRANULATED_LDS_SIZE
必须为 0。
CP 使用来自调度包的舍入值,而不是此值,因为调度可能包含动态分配的组段内存。 CP 直接写入
COMPUTE_PGM_RSRC2.LDS_SIZE
。要为每个工作组分配的组段 (LDS) 大小。粒度是设备特定的
- GFX6
roundup(lds-size / (64 * 4))
- GFX7-GFX11
roundup(lds-size / (128 * 4))
- GFX950
roundup(lds-size / (320 * 4))
24
1 位
ENABLE_EXCEPTION_IEEE_754_FP _INVALID_OPERATION
波前以指定的启用异常开始执行。
CP 使用此项来设置
COMPUTE_PGM_RSRC2.EXCP_EN
(从位 0..6 设置)。IEEE 754 FP 无效操作
25
1 位
ENABLE_EXCEPTION_FP_DENORMAL _SOURCE
FP 反常值:一个或多个输入操作数是反常值数
26
1 位
ENABLE_EXCEPTION_IEEE_754_FP _DIVISION_BY_ZERO
IEEE 754 FP 除以零
27
1 位
ENABLE_EXCEPTION_IEEE_754_FP _OVERFLOW
IEEE 754 FP FP 溢出
28
1 位
ENABLE_EXCEPTION_IEEE_754_FP _UNDERFLOW
IEEE 754 FP 下溢
29
1 位
ENABLE_EXCEPTION_IEEE_754_FP _INEXACT
IEEE 754 FP 不精确
30
1 位
ENABLE_EXCEPTION_INT_DIVIDE_BY _ZERO
整数除以零(仅限 rcp_iflag_f32 指令)
31
1 位
保留
保留,必须为 0。
32
总大小 4 字节。
表 68 适用于 GFX90A、GFX942 的 compute_pgm_rsrc3¶ 位
大小
字段名称
描述
5:0
6 位
ACCUM_OFFSET
统一寄存器文件中第一个 AccVGPR 的偏移量。 粒度为 4。 值 0-63。 0 - accum-offset = 4,1 - accum-offset = 8,…,63 - accum-offset = 256。
15:6
10 位
保留,必须为 0。
16
1 位
TG_SPLIT
如果为 0,则工作组的波在同一 CU 中启动。
如果为 1,则工作组的波可以在不同的 CU 中启动。 波不能使用 S_BARRIER 或 LDS。
31:17
15 位
保留,必须为 0。
32
总大小 4 字节。
表 69 适用于 GFX10-GFX11 的 compute_pgm_rsrc3¶ 位
大小
字段名称
描述
3:0
4 位
SHARED_VGPR_COUNT
以子向量模式执行时共享 VGPR 块的数量。 对于波前大小 64,该值为 0-15,表示 0-120 个 VGPR(粒度为 8),因此 (compute_pgm_rsrc1.vgprs +1)*4 + shared_vgpr_count*8 不超过 256。对于波前大小 32,shared_vgpr_count 必须为 0。
9:4
6 位
INST_PREF_SIZE
- GFX10
保留,必须为 0。
- GFX11
在波前开始执行之前,要预取的指令字节数,从内核的入口点指令开始。 该值为 0..63,粒度为 128 字节。
10
1 位
TRAP_ON_START
- GFX10
保留,必须为 0。
- GFX11
必须为 0。
如果为 1,则波前通过陷入陷阱处理程序开始执行。
CP 负责根据运行时的请求,在
COMPUTE_PGM_RSRC3.TRAP_ON_START
中填充启动时陷阱位。11
1 位
TRAP_ON_END
- GFX10
保留,必须为 0。
- GFX11
必须为 0。
如果为 1,则波前执行通过陷入陷阱处理程序终止。
CP 负责根据运行时的请求,在
COMPUTE_PGM_RSRC3.TRAP_ON_END
中填充结束时陷阱位。30:12
19 位
保留,必须为 0。
31
1 位
IMAGE_OP
- GFX10
保留,必须为 0。
- GFX11
如果为 1,则内核执行包含图像指令。 如果作为图形管线的一部分执行,则图像读取指令将停顿,等待执行任何必要的
WAIT_SYNC
栅栏,以指示较早的管线阶段已完成写入图像。不用于不属于图形管线的计算内核,并且必须为 0。
32
总大小 4 字节。
表 70 适用于 GFX12 的 compute_pgm_rsrc3¶ 位
大小
字段名称
描述
3:0
4 位
保留
保留,必须为 0。
11:4
8 位
INST_PREF_SIZE
在波前开始执行之前,要预取的指令字节数,从内核的入口点指令开始。 该值为 0..255,粒度为 128 字节。
12
1 位
保留
保留,必须为 0。
13
1 位
GLG_EN
如果为 1,则将为此调度启用组启动保证
30:14
17 位
保留
保留,必须为 0。
31
1 位
IMAGE_OP
如果为 1,则内核执行包含图像指令。 如果作为图形管线的一部分执行,则图像读取指令将停顿,等待执行任何必要的
WAIT_SYNC
栅栏,以指示较早的管线阶段已完成写入图像。不用于不属于图形管线的计算内核,并且必须为 0。
32
总大小 4 字节。
表 71 浮点舍入模式枚举值¶ 枚举名称
值
描述
FLOAT_ROUND_MODE_NEAR_EVEN
0
舍入到最接近的偶数
FLOAT_ROUND_MODE_PLUS_INFINITY
1
向 +无穷大舍入
FLOAT_ROUND_MODE_MINUS_INFINITY
2
向 -无穷大舍入
FLOAT_ROUND_MODE_ZERO
3
向 0 舍入
表 72 扩展的 FLT_ROUNDS 枚举值¶ F32 NEAR_EVEN
F32 PLUS_INFINITY
F32 MINUS_INFINITY
F32 ZERO
F64/F16 NEAR_EVEN
1
11
14
17
F64/F16 PLUS_INFINITY
8
2
15
18
F64/F16 MINUS_INFINITY
9
12
3
19
F64/F16 ZERO
10
13
16
0
表 73 浮点反常值模式枚举值¶ 枚举名称
值
描述
FLOAT_DENORM_MODE_FLUSH_SRC_DST
0
刷新源和目标反常值
FLOAT_DENORM_MODE_FLUSH_DST
1
刷新输出反常值
FLOAT_DENORM_MODE_FLUSH_SRC
2
刷新源反常值
FLOAT_DENORM_MODE_FLUSH_NONE
3
不刷新
反常值刷新是尊重符号的。 即
"denormal-fp-math"="preserve-sign"
预期的行为。 使用"denormal-fp-math"="positive-zero"
时,行为未定义
表 74 系统 VGPR 工作项 ID 枚举值¶ 枚举名称
值
描述
SYSTEM_VGPR_WORKITEM_ID_X
0
设置工作项 X 维度 ID。
SYSTEM_VGPR_WORKITEM_ID_X_Y
1
设置工作项 X 和 Y 维度 ID。
SYSTEM_VGPR_WORKITEM_ID_X_Y_Z
2
设置工作项 X、Y 和 Z 维度 ID。
SYSTEM_VGPR_WORKITEM_ID_UNDEFINED
3
未定义。
初始内核执行状态¶
本节定义了数据包处理器在每次波前开始执行之前将设置的寄存器状态。 这受到 CP/ADC/SPI 硬件控制器的约束限制。
定义了 SGPR 寄存器的顺序,但编译器可以使用 enable_sgpr_*
位字段在内核描述符中指定实际设置的寄存器(参见 内核描述符)。 用于已启用寄存器的寄存器编号从 SGPR0 开始密集排列:第一个已启用寄存器是 SGPR0,下一个已启用寄存器是 SGPR1,依此类推; 已禁用寄存器没有 SGPR 编号。
初始 SGPR 包括最多 16 个用户 SGPR,这些 SGPR 由 CP 设置并应用于网格的所有波前。 可以使用 enable_sgpr_*
位字段指定超过 16 个用户 SGPR,在这种情况下,实际上只初始化前 16 个。 紧随其后的是系统 SGPR,它们由 ADC/SPI 设置,并且对于网格调度的每个波前可以具有不同的值。
SGPR 寄存器初始状态在 SGPR 寄存器设置顺序 中定义。
表 75 SGPR 寄存器设置顺序¶ SGPR 顺序
名称(内核描述符启用字段)
SGPR 数量
描述
首先
私有段缓冲区 (enable_sgpr_private _segment_buffer)
4
参见 私有段缓冲区。
然后
调度指针 (enable_sgpr_dispatch_ptr)
2
正在执行的内核调度的 AQL 调度包的 64 位地址。
然后
队列指针 (enable_sgpr_queue_ptr)
2
在其上排队调度包的 AQL 队列的 64 位地址 amd_queue_t 对象。
然后
内核参数段指针 (enable_sgpr_kernarg _segment_ptr)
2
内核参数段的 64 位地址。 这直接从内核调度包中的 kernarg_address 复制而来。
让 CP 加载一次可以避免在每个波前开始时都加载它。
然后
调度 ID (enable_sgpr_dispatch_id)
2
正在执行的调度包的 64 位调度 ID。
然后
平面暂存初始化 (enable_sgpr_flat_scratch _init)
2
参见 平面暂存。
然后
私有段大小 (enable_sgpr_private _segment_size)
1
单个工作项的内存分配的 32 位字节大小。 这是来自内核调度包私有段字节大小的值,由 CP 向上舍入到 DWORD 的倍数。
让 CP 加载一次可以避免在每个波前开始时都加载它。
这不用于 GFX7-GFX8,因为它与平面暂存初始化的第二个 SGPR 的值相同。 但是,GFX9-GFX11 可能需要它,因为 GFX9-GFX11 更改了平面暂存初始化值的含义。
然后
预加载的内核参数 (kernarg_preload_spec _length)
N/A
参见 预加载内核参数。
然后
工作组 Id X (enable_sgpr_workgroup_id _X)
1
波前的网格的 X 维度中的 32 位工作组 ID。
然后
工作组 Id Y (enable_sgpr_workgroup_id _Y)
1
波前的网格的 Y 维度中的 32 位工作组 ID。
然后
工作组 Id Z (enable_sgpr_workgroup_id _Z)
1
波前的网格的 Z 维度中的 32 位工作组 ID。
然后
工作组信息 (enable_sgpr_workgroup _info)
1
{first_wavefront, 14’b0000, ordered_append_term[10:0], threadgroup_size_in_wavefronts[5:0]}
然后
暂存波前偏移量 (enable_sgpr_private _segment_wavefront_offset)
1
定义了 VGPR 寄存器的顺序,但编译器可以使用 enable_vgpr*
位字段在内核描述符中指定实际设置的寄存器(参见 内核描述符)。 用于已启用寄存器的寄存器编号从 VGPR0 开始密集排列:第一个已启用寄存器是 VGPR0,下一个已启用寄存器是 VGPR1,依此类推; 已禁用寄存器没有 VGPR 编号。
VGPR 初始状态有不同的方法
除非 AMDGPU 处理器 的“目标属性”列另有说明,否则每个工作项 ID 使用一个单独的 VGPR 寄存器。 此方法的 VGPR 寄存器初始状态在 未打包工作项 ID 方法的 VGPR 寄存器设置顺序 中定义。
如果 AMDGPU 处理器 的“目标属性”列指定打包工作项 ID,则 VGPR0 寄存器的初始值用于所有工作项 ID。 此方法的寄存器布局在 打包工作项 ID 方法的寄存器布局 中定义。
表 76 未打包工作项 ID 方法的 VGPR 寄存器设置顺序¶ VGPR 顺序
名称(内核描述符启用字段)
VGPR 数量
描述
首先
工作项 Id X(始终初始化)
1
波前通道的工作组的 X 维度中的 32 位工作项 ID。
然后
工作项 Id Y (enable_vgpr_workitem_id > 0)
1
波前通道的工作组的 Y 维度中的 32 位工作项 ID。
然后
工作项 Id Z (enable_vgpr_workitem_id > 1)
1
波前通道的工作组的 Z 维度中的 32 位工作项 ID。
表 77 打包工作项 ID 方法的寄存器布局¶ 位
大小
字段名称
描述
0:9
10 位
工作项 Id X
波前通道的工作组的 X 维度中的工作项 ID。
始终初始化。
10:19
10 位
工作项 Id Y
波前通道的工作组的 Y 维度中的工作项 ID。
如果 enable_vgpr_workitem_id > 0,则初始化;否则设置为 0。
20:29
10 位
工作项 Id Z
波前通道的工作组的 Z 维度中的工作项 ID。
如果 enable_vgpr_workitem_id > 1,则初始化;否则设置为 0。
30:31
2 位
保留,设置为 0。
寄存器的设置由 GPU CP/ADC/SPI 硬件按如下方式完成
工作组 ID 之前的 SGPR 由 CP 使用 16 个用户数据寄存器设置。
工作组 ID 寄存器 X、Y、Z 由 ADC 设置,ADC 支持任何组合,包括无。
暂存波前偏移量由 SPI 在每个波前的基础上设置,这就是为什么其值不能包含在每个队列的平面暂存初始化值中的原因(参见 平面暂存)。
VGPR 由 SPI 设置,SPI 仅支持指定 (X)、(X, Y) 或 (X, Y, Z)。
平面暂存寄存器对初始化在 平面暂存 中描述。
可以使用缓冲区指令(GFX6,具有 V# 64 位地址支持)、平面指令(GFX7-GFX11)或全局指令(GFX9-GFX11)访问全局段。
如果使用缓冲区操作,则编译器可以生成具有以下属性的 V#
基地址为 0
无混合
ATC:如果存在 IOMMU(例如 APU),则为 1
ptr64:1
MTYPE 设置为支持与运行时匹配的内存一致性(例如 APU 的 CC 和 dGPU 的 NC)。
预加载内核参数¶
在支持此功能的硬件上,内核参数可以预加载到用户 SGPR 中,最多可达到可用的用户 SGPR 的最大数量。 预加载 SGPR 的分配直接发生在最后一个启用的非内核参数预加载用户 SGPR 之后。 (参见 初始内核执行状态)
预加载的数据从内核参数段复制而来,数据量由内核描述符的 kernarg_preload_spec_length 字段中指定的值确定。 然后,此数据被加载到连续的用户 SGPR 中。 接收预加载内核参数数据的 SGPR 数量与 kernarg_preload_spec_length 给出的值相对应。 预加载从内核参数段内的 dword 偏移量开始,该偏移量由 kernarg_preload_spec_offset 字段指定。
如果 kernarg_preload_spec_length 非零,则 CP 固件会将额外的 256 字节附加到 kernel_code_entry_byte_offset。 这种添加有助于将序言合并到内核入口点,以处理为内核参数预加载设计的代码在配备不兼容固件的硬件上执行的情况。 如果硬件具有兼容的固件,则将跳过内核入口点开始处的 256 个字节。
对于代码对象 V5 及更高版本,通常通过隐式参数指针访问的隐藏内核参数可以预加载到用户 SGPR 中。 这些参数被添加到内核函数签名中,并标有属性 “inreg” 和 “amdgpu-hidden-argument”。 (参见 AMDGPU LLVM IR 属性)。
内核序言¶
编译器根据目标以及有关内核和调用的函数中的堆栈使用情况等信息,在内核序言中执行初始化。 其中一些初始化要求编译器请求某些用户和系统 SGPR 出现在 初始内核执行状态 中,通过 内核描述符。
CFI¶
CFI 返回地址未定义。
CFI CFA 使用表达式定义,该表达式评估为位置描述,该位置描述包含一个内存位置描述,用于
DW_ASPACE_AMDGPU_private_lane
地址空间地址0
。
M0¶
- GFX6-GFX8
如果内核可能通过 DS 或平面操作访问 LDS,则 M0 寄存器必须使用至少为总 LDS 大小的值进行初始化。 总 LDS 大小在调度包中可用。 对于 M0,也可以使用给定目标的最大可能的 LDS 值(GFX6 为 0x7FFF,GFX7-GFX8 为 0xFFFF)。
- GFX9-GFX11
M0 寄存器不用于范围检查 LDS 访问,因此无需在序言中初始化。
栈指针¶
如果内核有函数调用,则它必须通过将 SGPR32 设置为超出最后一个本地分配的地址的未混合暂存偏移量,来设置 非内核函数 中描述的 ABI 栈指针。
帧指针¶
如果内核出于 SIFrameLowering
中定义的原因需要帧指针,则使用 SGPR33,并且始终在内核序言中设置为 0
。 如果不需要帧指针,则所有帧指针的使用都将替换为立即 0
偏移量。
平面暂存¶
初始化平面暂存有不同的方法
如果 AMDGPU 处理器 的“目标属性”列指定不支持通用地址空间
不支持平面暂存,并且没有平面暂存寄存器对。
如果 AMDGPU 处理器 的“目标属性”列指定偏移平面暂存
如果内核或其调用的任何函数可能使用平面操作访问暂存内存,则 prolog 代码必须设置 FLAT_SCRATCH 寄存器对 (FLAT_SCRATCH_LO/FLAT_SCRATCH_HI)。初始化使用 Flat Scratch Init 和 Scratch Wavefront Offset SGPR 寄存器(参见 初始内核执行状态)
Flat Scratch Init 的低字是从
SH_HIDDEN_PRIVATE_BASE_VIMID
到 SPI 为执行内核调度的队列管理的暂存后备内存基址的 32 位字节偏移量。这与 Scratch Segment Buffer V# 基地址中使用的值相同。CP 从运行时获取此值。(Scratch Segment Buffer 基地址是
SH_HIDDEN_PRIVATE_BASE_VIMID
加上此偏移量。)prolog 必须添加 Scratch Wavefront Offset 的值,以获得 wavefront 的字节暂存后备内存偏移量,该偏移量来自
SH_HIDDEN_PRIVATE_BASE_VIMID
。当使用 Scratch Segment Buffer 时,Scratch Wavefront Offset 也必须用作 Private 段地址的偏移量。
由于 FLAT_SCRATCH_LO 的单位为 256 字节,因此在移入 FLAT_SCRATCH_HI 之前,偏移量必须右移 8 位。
FLAT_SCRATCH_HI 对应于 GFX7 上的 SGPRn-4 和 GFX8 上的 SGPRn-6(其中 SGPRn 是分配给 wavefront 的最高编号的 SGPR)。FLAT_SCRATCH_HI 乘以 256(因为它以 256 字节为单位),并添加到
SH_HIDDEN_PRIVATE_BASE_VIMID
以计算平面内存指令中访问暂存孔径的每个 wavefront 的 FLAT SCRATCH BASE。Flat Scratch Init 的第二个字是单个工作项暂存内存使用量的 32 位字节大小。
CP 从运行时获取此值,并且它始终是 DWORD 的倍数。CP 检查内核调度包 Private Segment Byte Size 中的值是否不大,并在必要时请求运行时增加队列的暂存大小。
CP 直接从内核调度包 Private Segment Byte Size 字段加载,并向上舍入到 DWORD 的倍数。让 CP 加载一次可以避免在每个 wavefront 开始时都加载它。
内核 prolog 代码必须将其移至 FLAT_SCRATCH_LO,在 GFX7 上为 SGPRn-3,在 GFX8 上为 SGPRn-5。FLAT_SCRATCH_LO 用作平面内存指令中的 FLAT SCRATCH SIZE。
如果 AMDGPU 处理器 的 *目标属性* 列指定 *绝对平面暂存*
如果内核或其调用的任何函数可能使用平面操作访问暂存内存,则 prolog 代码必须设置 FLAT_SCRATCH 寄存器对(FLAT_SCRATCH_LO/FLAT_SCRATCH_HI,它们在 SGPRn-4/SGPRn-3 中)。初始化使用 Flat Scratch Init 和 Scratch Wavefront Offset SGPR 寄存器(参见 初始内核执行状态)
Flat Scratch Init 是 SPI 为执行内核调度的队列管理的暂存后备内存基址的 64 位地址。
CP 从运行时获取此值。
内核 prolog 必须添加 wave 的 Scratch Wavefront Offset 值,并将结果作为 64 位值移动到 FLAT_SCRATCH SGPR 寄存器对,即 SGPRn-6 和 SGPRn-5。它用作平面内存指令中的 FLAT SCRATCH BASE。
当使用 Scratch Segment Buffer 时,Scratch Wavefront Offset 也必须用作 Private 段地址的偏移量(参见 Private Segment Buffer)。
如果 AMDGPU 处理器 的 *目标属性* 列指定 *架构平面暂存*
如果在 GFX6-GFX12 的 compute_pgm_rsrc2 中启用了 ENABLE_PRIVATE_SEGMENT,则 FLAT_SCRATCH 寄存器对将被初始化为 SPI 为执行内核调度的队列管理的暂存后备内存基址的 64 位地址,加上 wave 的 Scratch Wavefront Offset 值,用作平面内存指令中的平面暂存基址。
Private Segment Buffer¶
如果 AMDGPU 处理器 的 *目标属性* 列指定 *架构平面暂存*,则不支持 Private Segment Buffer。而是使用平面 SCRATCH 指令。
否则,Private Segment Buffer SGPR 寄存器用于初始化 4 个 SGPR,这些 SGPR 用作 V# 以访问暂存。CP 使用运行时提供的值。它与 Scratch Wavefront Offset 一起用作偏移量,以使用段地址访问私有内存空间。参见 初始内核执行状态。
暂存 V# 是四对齐的 SGPR,并且始终为内核选择,如下所示
如果在指令选择期间已知存在堆栈使用,则保留 SGPR0-3 用作暂存 V#。如果禁用优化 (
-O0
),如果堆栈对象已存在(对于局部变量等),或者如果存在任何函数调用,则假定存在堆栈使用。否则,保留从四对齐的 SGPR 索引开始的四个高编号的 SGPR 用于临时暂存 V#。如果确定需要溢出,则将使用这些 SGPR。
如果未使用临时暂存 V#,则取消保留它,并且寄存器计数将在忽略它的情况下确定。
如果使用了临时暂存 V#,则其寄存器编号将移动到寄存器分配器分配的最高编号寄存器之后的第一个四对齐的 SGPR 索引,并且所有用途都将更新。寄存器计数包括移位后的位置中的它们。
在任何一种情况下,如果处理器存在 SGPR 分配错误,则临时分配不会移位或取消保留,以确保寄存器计数更高以解决该错误。
注意
这种使用临时暂存 V# 并在使用时移动寄存器编号的方法避免了在消除临时 V# 时必须执行第二次寄存器分配。这更有效,并避免了第二次寄存器分配可能执行溢出的问题,因为不再有暂存 V#。
当发出内核 prolog 代码时,已知上述描述的暂存 V# 是否实际使用。如果使用,则 prolog 代码必须通过将 Private Segment Buffer 复制到暂存 V# 寄存器,然后将 Private Segment Wavefront Offset 添加到 V# 中的队列基地址来设置它。结果是一个 V#,其基地址指向 wavefront 暂存后备内存的开头。
始终请求 Private Segment Buffer,但仅在使用 Private Segment Wavefront Offset 时才请求它(参见 初始内核执行状态)。
内存模型¶
本节介绍 LLVM 内存模型到 AMDGPU 机器代码的映射(参见 并发操作的内存模型)。
AMDGPU 后端支持 内存作用域 中指定的内存同步作用域。
用于实现内存模型的代码序列指定单个线程必须执行的指令顺序。s_waitcnt
和缓存管理指令(例如 buffer_wbinvl1_vol
)是相对于同一线程执行的其他内存指令定义的。这允许它们更早或更晚地移动,这允许它们与同一指令的其他实例组合,或者从循环中提升/下沉以提高性能。仅给出与内存模型相关的指令;需要额外的 s_waitcnt
指令以确保寄存器在使用前已定义。这些可能能够与如上所述的内存模型 s_waitcnt
指令组合。
AMDGPU 后端支持以下内存模型
- HSA 内存模型 [HSA]
HSA 内存模型对所有地址空间使用单个先发生关系(参见 地址空间)。
- OpenCL 内存模型 [OpenCL]
OpenCL 内存模型对全局和本地地址空间具有单独的先发生关系。只有指定全局和本地地址空间的 fence,以及 seq_cst 指令才加入这些关系。由于 LLVM
memfence
指令不允许指定地址空间,因此 OpenCL fence 必须保守地假设指定了本地和全局地址空间。但是,当没有访问相应地址空间的中间内存指令时,通常可以进行优化以消除额外的s_waitcnt
指令。表中的代码序列指示了对于 OpenCL 内存可以省略的内容。目标三元组环境用于确定源语言是否为 OpenCL(参见 OpenCL)。
到本地内存的 ds/flat_load/store/atomic
指令称为 LDS 操作。
到全局内存的 buffer/global/flat_load/store/atomic
指令称为向量内存操作。
私有地址空间使用 buffer_load/store
使用暂存 V# (GFX6-GFX8),或 scratch_load/store
(GFX9-GFX11)。由于只有一个线程访问内存,因此原子内存排序没有意义,并且所有访问都被视为非原子。
常量地址空间使用 buffer/global_load
指令(或等效的标量内存指令)。由于常量地址空间内容在内核调度执行期间不会更改,因此执行存储是非法的,并且原子内存排序没有意义,并且所有访问都被视为非原子。
对于组 (LDS) 地址空间,比工作组更宽的内存同步作用域没有意义,并且被视为工作组。
内存模型不支持区域地址空间,该地址空间被视为非原子。
Acquire 内存排序在 store atomic 指令上没有意义,并且被视为非原子。
Release 内存排序在 load atomic 指令上没有意义,并且被视为非原子。
Acquire-release 内存排序在 load 或 store atomic 指令上没有意义,并且分别被视为 acquire 和 release。
内存顺序还添加了表 AMDHSA 内存模型单线程优化约束 中定义的单线程优化约束。
表 78 AMDHSA 内存模型单线程优化约束¶ LLVM 内存
优化约束
排序
unordered (无序)
none
monotonic (单调)
none
acquire (获取)
如果 load atomic/atomicrmw,则任何后续的 load/load atomic/store/store atomic/atomicrmw/fence 指令都不能在 acquire 之前移动。
如果是 fence,则与 load atomic 相同,加上任何先前的关联 fence 配对原子操作都不能在 fence 之后移动。
release (释放)
如果 store atomic/atomicrmw,则任何先前的 load/load atomic/store/store atomic/atomicrmw/fence 指令都不能在 release 之后移动。
如果是 fence,则与 store atomic 相同,加上任何后续的关联 fence 配对原子操作都不能在 fence 之前移动。
acq_rel (获取-释放)
与 acquire 和 release 的约束相同。
seq_cst (顺序一致性)
如果是 load atomic,则与 acquire 的约束相同,加上任何先前的顺序一致性 load atomic/store atomic/atomicrmw/fence 指令都不能在 seq_cst 之后移动。
如果是 store atomic,则与 release 的约束相同,加上任何后续的顺序一致性 load atomic/store atomic/atomicrmw/fence 指令都不能在 seq_cst 之前移动。
如果是 atomicrmw/fence,则与 acq_rel 的约束相同。
用于实现内存模型的代码序列在以下部分中定义
Fence 和地址空间¶
LLVM fence 没有地址空间信息,因此,fence 代码生成通常需要保守地同步所有地址空间。
在 OpenCL 的情况下,fence 只需要同步用户指定的地址空间,这可能会导致额外的不必要的等待。例如,旨在仅同步本地内存的 fence 也必须等待所有全局内存操作,这是不必要的。
内存模型松弛注释 可以用作 fence 的优化提示,以解决此问题。AMDGPU 后端识别 fence 上的以下标签
amdgpu-as:local
- fence 仅本地地址空间amdgpu-as:global
- fence 仅全局地址空间
注意
作为优化提示,这些标签不能保证在代码生成之前幸存下来。优化可以自由地删除标签以允许更好的代码优化,但代价是同步额外的地址空间。
内存模型 GFX6-GFX9¶
对于 GFX6-GFX9
每个代理 (agent) 都有多个着色器阵列 (SA)。
每个 SA 都有多个计算单元 (CU)。
每个 CU 都有多个 SIMD,用于执行 wavefront。
单个工作组的 wavefront 在同一个 CU 中执行,但可能由不同的 SIMD 执行。
每个 CU 都有一个由在其上执行的工作组的 wavefront 共享的 LDS 内存。
CU 的所有 LDS 操作都作为 wavefront 范围的操作在全球顺序中执行,并且不涉及缓存。完成情况以执行顺序报告给 wavefront。
LDS 内存具有由 CU 的 SIMD 共享的多个请求队列。因此,同一工作组中不同 wavefront 执行的 LDS 操作可以相对于彼此重新排序,这可能导致向量内存操作的可见性相对于同一工作组中其他 wavefront 的 LDS 操作重新排序。
s_waitcnt lgkmcnt(0)
是确保工作组的 wavefront 之间的 LDS 操作和向量内存操作之间同步所必需的,但同一 wavefront 执行的操作之间则不需要。向量内存操作作为 wavefront 范围的操作执行,并且完成情况以执行顺序报告给 wavefront。例外情况是,对于 GFX7-GFX9,如果
flat_load/store/atomic
指令访问 LDS 内存,则它们可以报告向量内存顺序之外,如果它们访问全局内存,则可以报告 LDS 操作顺序之外。向量内存操作访问由 CU 的所有 SIMD 共享的单个向量 L1 缓存。因此,对于单个 wavefront 的 lane 之间的相干性,或同一工作组中 wavefront 之间的相干性,不需要特殊操作。对于在不同工作组中执行的 wavefront 之间的相干性,需要
buffer_wbinvl1_vol
,因为它们可能在不同的 CU 上执行。标量内存操作访问由一组 CU 上的所有 wavefront 共享的标量 L1 缓存。标量和向量 L1 缓存不相干。但是,标量操作以受限制的方式使用,因此不会影响内存模型。参见 内存空间。
向量和标量内存操作使用由同一代理上的所有 CU 共享的 L2 缓存。
L2 缓存具有独立的通道,用于服务于不相交的虚拟地址范围。
每个 CU 每个通道都有一个单独的请求队列。因此,代理的不同工作组(可能在不同的 CU 上执行)中执行的 wavefront 执行的向量和标量内存操作可以相对于彼此重新排序。
s_waitcnt vmcnt(0)
是确保不同 CU 的向量内存操作之间同步所必需的。它确保先前的向量内存操作已完成,然后再执行后续的向量内存或 LDS 操作,因此可以用于满足 acquire 和 release 的要求。L2 缓存可以与某些目标上的其他代理保持相干,或者可以设置虚拟地址范围以绕过它,以确保系统相干性。
标量内存操作仅用于访问在内核调度执行期间被证明不会更改的内存。这包括常量地址空间和程序作用域 const
变量的全局地址空间。因此,内核机器代码不必维护标量缓存以确保其与向量缓存相干。标量和向量缓存在内核调度之间由 CP 无效,因为常量地址空间数据可能在内核调度执行之间更改。参见 内存空间。
一个例外是,如果标量写入用于溢出 SGPR 寄存器。在这种情况下,AMDGPU 后端确保用于溢出的内存位置永远不会同时被向量内存操作访问。如果使用标量写入,则在 s_endpgm
之前和函数返回之前插入 s_dcache_wb
,因为这些位置可能被将来的 wavefront 用于向量内存指令,该 wavefront 使用相同的暂存区域,或者用于在同一地址创建帧的函数调用。不需要 s_dcache_inv
,因为所有标量写入在同一线程中都是写后读。
对于 kernarg 后备内存
CP 在每个内核调度开始时使 L1 缓存无效。
在 dGPU 上,kernarg 后备内存分配在主机内存中,作为 MTYPE UC (非缓存) 访问,以避免需要使 L2 缓存无效。这也使其被视为非易失性,因此不会被
*_vol
无效。在 APU 上,kernarg 后备内存作为 MTYPE CC (缓存相干) 访问,因此 L2 缓存将与 CPU 和其他代理相干。
暂存后备内存(用于私有地址空间)使用 MTYPE NC_NV (非相干非易失性) 访问。由于私有地址空间仅由单个线程访问,并且始终是写后读,因此永远不需要使 L1 缓存中的这些条目无效。因此,所有缓存无效都作为 *_vol
完成,仅使易失性缓存行无效。
用于实现 GFX6-GFX9 内存模型的代码序列在表 AMDHSA 内存模型代码序列 GFX6-GFX9 中定义。
表 79 AMDHSA 内存模型代码序列 GFX6-GFX9¶ LLVM 指令
LLVM 内存排序
LLVM 内存同步作用域
AMDGPU 地址空间
AMDGPU 机器代码 GFX6-GFX9
非原子
load (加载)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_load
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_load glc=1 slc=1
volatile (易失性)
buffer/global/flat_load glc=1
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
load (加载)
none
none
local (本地)
ds_load
store (存储)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_store
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_store glc=1 slc=1
volatile (易失性)
buffer/global/flat_store
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
store (存储)
none
none
local (本地)
ds_store
无序原子
load atomic (原子加载)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
store atomic (原子存储)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
atomicrmw (原子读-修改-写)
unordered (无序)
any (任何)
any (任何)
与单调原子相同.
单调原子
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
monotonic (单调)
agent
system
global
generic (通用)
buffer/global/flat_load glc=1
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_store
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_atomic
获取原子
load atomic (原子加载)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
acquire (获取)
workgroup
global
buffer/global_load
load atomic (原子加载)
acquire (获取)
workgroup
local (本地)
generic (通用)
ds/flat_load
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
load atomic (原子加载)
acquire (获取)
agent
system
global
buffer/global_load glc=1
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保加载在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
agent
system
generic (通用)
flat_load glc=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 flat_load 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
global
buffer/global_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
local (本地)
generic (通用)
ds/flat_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
global
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
generic (通用)
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acquire (获取)
singlethread
wavefront
none
none
fence (栅栏)
acquire (获取)
workgroup
none
s_waitcnt lgkmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比 fence 配对原子操作读取的值旧。
fence (栅栏)
acquire (获取)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
释放原子
store atomic (原子存储)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_store
store atomic (原子存储)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保在执行正在释放的存储之前,所有对本地内存的操作都已完成。
buffer/global/flat_store
store atomic (原子存储)
release (释放)
workgroup
local (本地)
ds_store
store atomic (原子存储)
release (释放)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保在执行正在释放的存储之前,所有对内存的操作都已完成。
buffer/global/flat_store
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
local (本地)
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局和本地内存的操作都已完成。
buffer/global/flat_atomic
fence (栅栏)
release (释放)
singlethread
wavefront
none
none
fence (栅栏)
release (释放)
workgroup
none
s_waitcnt lgkmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
必须在任何先前的本地/通用加载/原子加载/存储/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有对本地内存的操作都已完成。
fence (栅栏)
release (释放)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
获取-释放原子
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
global
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。
buffer/global_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
local (本地)
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
generic (通用)
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。
flat_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
global
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acq_rel (获取-释放)
singlethread
wavefront
none
none
fence (栅栏)
acq_rel (获取-释放)
workgroup
none
s_waitcnt lgkmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略。
但是,由于 LLVM 当前在 fence 上没有地址空间,因此需要保守地始终生成(参见先前 fence 的注释)。
必须在任何先前的本地/通用加载/原子加载/存储/原子存储/原子读-修改-写之后发生。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保在执行任何后续的全局内存操作之前,所有对本地内存的操作都已完成。
确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。
确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
fence (栅栏)
acq_rel (获取-释放)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保先前的全局/本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在使缓存无效之前已完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。
顺序一致性原子
load atomic (原子加载)
seq_cst (顺序一致性)
singlethread
wavefront
global
local (本地)
generic (通用)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0)
必须在先前的本地/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt lgkmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致性本地内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
local (本地)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt lgkmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt lgkmcnt(0),因此不需要考虑。)
s_waitcnt vmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt vmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致性全局内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
store atomic (原子存储)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的存储原子释放相同,除了即使对于 OpenCL 也必须生成所有指令。
atomicrmw (原子读-修改-写)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的 atomicrmw 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
fence (栅栏)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
none
与相应的 fence 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
内存模型 GFX90A¶
对于 GFX90A
每个代理 (agent) 都有多个着色器阵列 (SA)。
每个 SA 都有多个计算单元 (CU)。
每个 CU 都有多个 SIMD,用于执行 wavefront。
单个工作组的 wavefront 在同一个 CU 中执行,但可能由不同的 SIMD 执行。例外情况是在 tgsplit 执行模式下,wavefront 可能由不同 CU 中的不同 SIMD 执行。
每个 CU 都有一个由在其上执行的工作组的 wavefront 共享的 LDS 内存。例外情况是在 tgsplit 执行模式下,当同一工作组的 wavefront 可能位于不同的 CU 中时,不会分配 LDS。
CU 的所有 LDS 操作都作为 wavefront 范围的操作在全球顺序中执行,并且不涉及缓存。完成情况以执行顺序报告给 wavefront。
LDS 内存具有由 CU 的 SIMD 共享的多个请求队列。因此,同一工作组中不同 wavefront 执行的 LDS 操作可以相对于彼此重新排序,这可能导致向量内存操作的可见性相对于同一工作组中其他 wavefront 的 LDS 操作重新排序。
s_waitcnt lgkmcnt(0)
是确保工作组的 wavefront 之间的 LDS 操作和向量内存操作之间同步所必需的,但同一 wavefront 执行的操作之间则不需要。向量内存操作作为 wavefront 范围的操作执行,并且完成情况以执行顺序报告给 wavefront。例外情况是,如果
flat_load/store/atomic
指令访问 LDS 内存,则它们可以报告向量内存顺序之外,如果它们访问全局内存,则可以报告 LDS 操作顺序之外。向量内存操作访问由 CU 的所有 SIMD 共享的单个向量 L1 缓存。因此
对于单个 wavefront 的 lane 之间的相干性,不需要特殊操作。
对于同一工作组中 wavefront 之间的相干性,不需要特殊操作,因为它们在同一个 CU 上执行。例外情况是在 tgsplit 执行模式下,当同一工作组的 wavefront 可能位于不同的 CU 中时,因此需要
buffer_wbinvl1_vol
,如下一项所述。对于在不同工作组中执行的 wavefront 之间的相干性,需要
buffer_wbinvl1_vol
,因为它们可能在不同的 CU 上执行。
标量内存操作访问由一组 CU 上的所有 wavefront 共享的标量 L1 缓存。标量和向量 L1 缓存不相干。但是,标量操作以受限制的方式使用,因此不会影响内存模型。参见 内存空间。
向量和标量内存操作使用由同一代理上的所有 CU 共享的 L2 缓存。
L2 缓存具有独立的通道,用于服务于不相交的虚拟地址范围。
每个 CU 每个通道都有一个单独的请求队列。因此,代理的不同工作组(可能在不同的 CU 上执行)中执行的 wavefront 执行的向量和标量内存操作,或者在 tgsplit 模式下执行的同一工作组的向量和标量内存操作,可以相对于彼此重新排序。
s_waitcnt vmcnt(0)
是确保不同 CU 的向量内存操作之间同步所必需的。它确保先前的向量内存操作已完成,然后再执行后续的向量内存或 LDS 操作,因此可以用于满足 acquire 和 release 的要求。一个代理的 L2 缓存可以通过以下方式与其他代理保持相干:对于 L2 本地内存,使用 MTYPE RW (读写) 或 MTYPE CC (缓存相干) 和 PTE C 位;对于非 L2 本地内存,使用 MTYPE NC (非相干) 和 PTE C 位设置或 MTYPE UC (非缓存)。
任何本地内存缓存行都将由于相干请求引起的缓存探测而自动被与其他 L2 缓存关联的 CU 的写入或 CPU 的写入无效。相干请求是由 GPU 访问 PTE C 位设置的页面、CPU 通过 XGMI 访问以及配置为相干请求的 PCIe 请求引起的。
XGMI 从 CPU 对本地内存的访问可能在 CPU 上被缓存。由于 L2 探测过滤器和 PTE C 位已设置,后续来自 GPU 的访问将自动使 CPU 缓存失效或写回。
由于同一 Agent 上的所有工作组共享相同的 L2,因此,为保证一致性,不需要 L2 失效或写回。
为了确保不同 Agent 中工作组的本地和远程内存写入的一致性,需要
buffer_wbl2
。它将写回 MTYPE RW(用于本地粗粒度内存)和 MTYPE NC(用于远程粗粒度内存)的脏 L2 缓存行。请注意,MTYPE CC(用于本地细粒度内存)会导致直写到 DRAM,而 MTYPE UC(用于远程细粒度内存)会绕过 L2,因此两者都不会导致脏 L2 缓存行。为了确保不同 Agent 中工作组的本地和远程内存读取的一致性,需要
buffer_invl2
。它将使 MTYPE NC(用于远程粗粒度内存)的 L2 缓存行失效。请注意,MTYPE CC(用于本地细粒度内存)和 MTYPE RW(用于本地粗粒度内存)会导致本地读取因 PTE C 位设置而被远程写入失效,因此这些缓存行不会被失效。请注意,MTYPE UC(用于远程细粒度内存)会绕过 L2,因此永远不会导致需要失效的 L2 缓存行。
从 GPU 到 CPU 内存的 PCIe 访问通过使用 MTYPE UC(非缓存)来保持一致性,MTYPE UC 会绕过 L2。
标量内存操作仅用于访问在内核调度执行期间被证明不会更改的内存。这包括常量地址空间和程序作用域 const
变量的全局地址空间。因此,内核机器代码不必维护标量缓存以确保其与向量缓存相干。标量和向量缓存在内核调度之间由 CP 无效,因为常量地址空间数据可能在内核调度执行之间更改。参见 内存空间。
一个例外是,如果标量写入用于溢出 SGPR 寄存器。在这种情况下,AMDGPU 后端确保用于溢出的内存位置永远不会同时被向量内存操作访问。如果使用标量写入,则在 s_endpgm
之前和函数返回之前插入 s_dcache_wb
,因为这些位置可能被将来的 wavefront 用于向量内存指令,该 wavefront 使用相同的暂存区域,或者用于在同一地址创建帧的函数调用。不需要 s_dcache_inv
,因为所有标量写入在同一线程中都是写后读。
对于 kernarg 后备内存
CP 在每个内核调度开始时使 L1 缓存无效。
在通过 XGMI 或 PCIe 的 dGPU 上,kernarg 后备内存分配在主机内存中,并作为 MTYPE UC(非缓存)访问,以避免需要使 L2 缓存失效。这也使其被视为非易失性内存,因此不会被
*_vol
失效。在 APU 上,kernarg 后备内存作为 MTYPE CC(缓存一致性)访问,因此 L2 缓存将与 CPU 和其他 Agent 保持一致。
暂存后备内存(用于私有地址空间)使用 MTYPE NC_NV (非相干非易失性) 访问。由于私有地址空间仅由单个线程访问,并且始终是写后读,因此永远不需要使 L1 缓存中的这些条目无效。因此,所有缓存无效都作为 *_vol
完成,仅使易失性缓存行无效。
GFX90A 内存模型实现的代码序列在 AMDHSA 内存模型代码序列 GFX90A 表中定义。
表 80 AMDHSA 内存模型代码序列 GFX90A¶ LLVM 指令
LLVM 内存排序
LLVM 内存同步作用域
AMDGPU 地址空间
AMDGPU 机器代码 GFX90A
非原子
load (加载)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_load
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_load glc=1 slc=1
volatile (易失性)
buffer/global/flat_load glc=1
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
load (加载)
none
none
local (本地)
ds_load
store (存储)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_store
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_store glc=1 slc=1
volatile (易失性)
buffer/global/flat_store
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
store (存储)
none
none
local (本地)
ds_store
无序原子
load atomic (原子加载)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
store atomic (原子存储)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
atomicrmw (原子读-修改-写)
unordered (无序)
any (任何)
any (任何)
与单调原子相同.
单调原子
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_load
load atomic (原子加载)
monotonic (单调)
workgroup
global
generic (通用)
buffer/global/flat_load glc=1
如果不是 TgSplit 执行模式,则省略 glc=1。
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_load
load atomic (原子加载)
monotonic (单调)
agent
global
generic (通用)
buffer/global/flat_load glc=1
load atomic (原子加载)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_load glc=1
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
agent
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
agent
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
获取原子
load atomic (原子加载)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
acquire (获取)
workgroup
global
buffer/global_load glc=1
如果不是 TgSplit 执行模式,则省略 glc=1。
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在后续的 buffer_wbinvl1_vol 之前发生。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_load
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
load atomic (原子加载)
acquire (获取)
workgroup
generic (通用)
flat_load glc=1
如果不是 TgSplit 执行模式,则省略 glc=1。
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_wbinvl1_vol 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
agent
global
buffer/global_load glc=1
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保加载在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
system
global
buffer/global/flat_load glc=1
s_waitcnt vmcnt(0)
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保加载在使缓存无效之前已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
load atomic (原子加载)
acquire (获取)
agent
generic (通用)
flat_load glc=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 flat_load 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
system
generic (通用)
flat_load glc=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保在使缓存失效之前,flat_load 已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
global
buffer/global_atomic
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
generic (通用)
flat_atomic
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_wbinvl1_vol 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
global
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
system
global
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
generic (通用)
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
system
generic (通用)
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
fence (栅栏)
acquire (获取)
singlethread
wavefront
none
none
fence (栅栏)
acquire (获取)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load atomic/ atomicrmw(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_wbinvl1_vol 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比 fence 配对原子操作读取的值旧。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acquire (获取)
agent
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acquire (获取)
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
释放原子
store atomic (原子存储)
release (释放)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
release (释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
store atomic (原子存储)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作在执行正在释放的存储之前完成。
buffer/global/flat_store
store atomic (原子存储)
release (释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
store atomic (原子存储)
release (释放)
agent
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保在执行正在释放的存储之前,所有对内存的操作都已完成。
buffer/global/flat_store
store atomic (原子存储)
release (释放)
system
global
generic (通用)
buffer_wbl2
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作和 L2 写回在执行正在释放的存储之前完成。
buffer/global/flat_store
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
agent
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局和本地内存的操作都已完成。
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
system
global
generic (通用)
buffer_wbl2
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作和 L2 写回在执行正在释放的存储之前完成。
buffer/global/flat_atomic
fence (栅栏)
release (释放)
singlethread
wavefront
none
none
fence (栅栏)
release (释放)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/load atomic/store/store atomic/atomicrmw 之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
agent
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
system
none
buffer_wbl2
如果是 OpenCL 且地址空间是本地的,则省略。
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
获取-释放原子
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
global
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果不是 TgSplit 执行模式,则省略 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_wbinvl1_vol 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
global
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
system
global
buffer_wbl2
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保对全局内存和 L2 写回的所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
system
generic (通用)
buffer_wbl2
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保对全局内存和 L2 写回的所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
fence (栅栏)
acq_rel (获取-释放)
singlethread
wavefront
none
none
fence (栅栏)
acq_rel (获取-释放)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
但是,由于 LLVM 当前在 fence 上没有地址空间,因此需要保守地始终生成(参见先前 fence 的注释)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/load atomic/store/store atomic/atomicrmw 之后发生。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保所有内存操作在执行任何后续全局内存操作之前完成。
确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。
确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保 acquire-fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 acquire-fence-paired-atomic 读取的值。
buffer_wbinvl1_vol
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acq_rel (获取-释放)
agent
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 buffer_wbinvl1_vol 之前发生。
确保先前的全局/本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在使缓存无效之前已完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。
fence (栅栏)
acq_rel (获取-释放)
system
none
buffer_wbl2
如果是 OpenCL 且地址空间是本地的,则省略。
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。
确保先前的全局/本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在使缓存无效之前已完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_invl2; buffer_wbinvl1_vol
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。
顺序一致性原子
load atomic (原子加载)
seq_cst (顺序一致性)
singlethread
wavefront
global
local (本地)
generic (通用)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
s_waitcnt lgkmcnt(0) 必须在先前的 local/generic load atomic/store atomic/atomicrmw(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt lgkmcnt(0),因此无需考虑。)
s_waitcnt vmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt vmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致的全局/本地内存指令在执行此顺序一致的指令之前完成。这可以防止对 seq_cst store 之后跟随 seq_cst load 进行重新排序。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 可以防止 load acquire 后跟 store release 的重新排序,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择 load 以使 s_waitcnt 尽可能晚,以便 store 可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt lgkmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt lgkmcnt(0),因此不需要考虑。)
s_waitcnt vmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt vmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致性全局内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
store atomic (原子存储)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的存储原子释放相同,除了即使对于 OpenCL 也必须生成所有指令。
atomicrmw (原子读-修改-写)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的 atomicrmw 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
fence (栅栏)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
none
与相应的 fence 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
内存模型 GFX942¶
对于 GFX942
每个代理 (agent) 都有多个着色器阵列 (SA)。
每个 SA 都有多个计算单元 (CU)。
每个 CU 都有多个 SIMD,用于执行 wavefront。
单个工作组的 wavefront 在同一个 CU 中执行,但可能由不同的 SIMD 执行。例外情况是在 tgsplit 执行模式下,wavefront 可能由不同 CU 中的不同 SIMD 执行。
每个 CU 都有一个由在其上执行的工作组的 wavefront 共享的 LDS 内存。例外情况是在 tgsplit 执行模式下,当同一工作组的 wavefront 可能位于不同的 CU 中时,不会分配 LDS。
CU 的所有 LDS 操作都作为 wavefront 范围的操作在全球顺序中执行,并且不涉及缓存。完成情况以执行顺序报告给 wavefront。
LDS 内存具有由 CU 的 SIMD 共享的多个请求队列。因此,同一工作组中不同 wavefront 执行的 LDS 操作可以相对于彼此重新排序,这可能导致向量内存操作的可见性相对于同一工作组中其他 wavefront 的 LDS 操作重新排序。
s_waitcnt lgkmcnt(0)
是确保工作组的 wavefront 之间的 LDS 操作和向量内存操作之间同步所必需的,但同一 wavefront 执行的操作之间则不需要。向量内存操作作为 wavefront 范围的操作执行,并且完成情况以执行顺序报告给 wavefront。例外情况是,如果
flat_load/store/atomic
指令访问 LDS 内存,则它们可以报告向量内存顺序之外,如果它们访问全局内存,则可以报告 LDS 操作顺序之外。向量内存操作访问由 CU 的所有 SIMD 共享的单个向量 L1 缓存。因此
对于单个 wavefront 的 lane 之间的相干性,不需要特殊操作。
同一工作组中的 wavefront 之间不需要特殊操作来保证一致性,因为它们在同一 CU 上执行。例外情况是在 tgsplit 执行模式下,因为同一工作组的 wavefront 可能位于不同的 CU 中,因此需要
buffer_inv sc0
,这将使 L1 缓存失效。需要
buffer_inv sc0
来使 L1 缓存失效,以保证在不同工作组中执行的 wavefront 之间的一致性,因为它们可能在不同的 CU 上执行。原子读-修改-写指令隐式绕过 L1 缓存。因此,它们不使用 sc0 位来保证一致性,而是使用它来指示指令是否返回正在更新的原始值。它们使用 sc1 来指示系统或 Agent 范围的一致性。
标量内存操作访问由一组 CU 上的所有 wavefront 共享的标量 L1 缓存。标量和向量 L1 缓存不相干。但是,标量操作以受限制的方式使用,因此不会影响内存模型。参见 内存空间。
向量和标量内存操作使用 L2 缓存。
gfx942 可以配置为多个较小的 Agent,每个 Agent 只有一个 L2,由同一 Agent 上的所有 CU 共享;也可以配置为较少(可能只有一个)较大的 Agent,每个 Agent 上的 CU 组分别共享单独的 L2 缓存。
L2 缓存具有独立的通道,用于服务于不相交的虚拟地址范围。
每个 CU 的每个通道都有一个单独的请求队列,用于其关联的 L2。因此,由具有不同 L1 缓存和相同 L2 缓存的 wavefront 执行的向量和标量内存操作可以相对于彼此重新排序。
需要
s_waitcnt vmcnt(0)
来确保不同 CU 的向量内存操作之间的同步。它确保先前的向量内存操作在执行后续向量内存或 LDS 操作之前完成,因此可以用于满足 acquire 和 release 的要求。L2 缓存可以通过对 L2 本地内存使用 MTYPE RW(读写),以及对非 L2 本地内存使用设置了 PTE C 位的 MTYPE NC(非一致性)来与其他 L2 缓存保持一致。
任何本地内存缓存行都将由于 PTE C 位引起的缓存探测而被来自与其他 L2 缓存关联的 CU 的写入或来自 CPU 的写入自动失效。
XGMI 从 CPU 对本地内存的访问可能在 CPU 上被缓存。由于 L2 探测过滤器,后续来自 GPU 的访问将自动使 CPU 缓存失效或写回。
为了确保同一 Agent 中具有不同 L1 缓存的 CU 的本地内存写入的一致性,需要
buffer_wbl2
。如果 Agent 配置为只有一个 L2,则它不执行任何操作;如果配置为有多个 L2 缓存,则它将写回脏 L2 缓存行。为了确保不同 Agent 中 CU 的本地内存写入的一致性,需要
buffer_wbl2 sc1
。它将写回脏 L2 缓存行。为了确保同一 Agent 中具有不同 L1 缓存的 CU 的本地内存读取的一致性,需要
buffer_inv sc1
。如果 Agent 配置为只有一个 L2,则它不执行任何操作;如果配置为有多个 L2 缓存,则如果配置为有多个 L2 缓存,它将使非本地 L2 缓存行失效。为了确保不同 Agent 中 CU 的本地内存读取的一致性,需要
buffer_inv sc0 sc1
。如果配置为有多个 L2 缓存,它将使非本地 L2 缓存行失效。
从 GPU 到 CPU 的 PCIe 访问可以通过使用 MTYPE UC(非缓存)来保持一致性,MTYPE UC 会绕过 L2。
标量内存操作仅用于访问在内核调度执行期间被证明不会更改的内存。这包括常量地址空间和程序作用域 const
变量的全局地址空间。因此,内核机器代码不必维护标量缓存以确保其与向量缓存相干。标量和向量缓存在内核调度之间由 CP 无效,因为常量地址空间数据可能在内核调度执行之间更改。参见 内存空间。
一个例外是,如果标量写入用于溢出 SGPR 寄存器。在这种情况下,AMDGPU 后端确保用于溢出的内存位置永远不会同时被向量内存操作访问。如果使用标量写入,则在 s_endpgm
之前和函数返回之前插入 s_dcache_wb
,因为这些位置可能被将来的 wavefront 用于向量内存指令,该 wavefront 使用相同的暂存区域,或者用于在同一地址创建帧的函数调用。不需要 s_dcache_inv
,因为所有标量写入在同一线程中都是写后读。
对于 kernarg 后备内存
CP 在每个内核调度开始时使 L1 缓存无效。
在通过 XGMI 或 PCIe 的 dGPU 上,kernarg 后备内存分配在主机内存中,并作为 MTYPE UC(非缓存)访问,以避免需要使 L2 缓存失效。这也使其被视为非易失性内存,因此不会被
*_vol
失效。在 APU 上,kernarg 后备内存作为 MTYPE CC(缓存一致性)访问,因此 L2 缓存将与 CPU 和其他 Agent 保持一致。
暂存后备内存(用于私有地址空间)使用 MTYPE NC_NV (非相干非易失性) 访问。由于私有地址空间仅由单个线程访问,并且始终是写后读,因此永远不需要使 L1 缓存中的这些条目无效。因此,所有缓存无效都作为 *_vol
完成,仅使易失性缓存行无效。
GFX942 内存模型实现的代码序列在 AMDHSA 内存模型代码序列 GFX942 表中定义。
表 81 AMDHSA 内存模型代码序列 GFX942¶ LLVM 指令
LLVM 内存排序
LLVM 内存同步作用域
AMDGPU 地址空间
AMDGPU 机器代码 GFX942
非原子
load (加载)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_load
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_load nt=1
volatile (易失性)
buffer/global/flat_load sc0=1 sc1=1
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
load (加载)
none
none
local (本地)
ds_load
store (存储)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
- GFX942
buffer/global/flat_store
!volatile & nontemporal (非易失性 & 临时性)
- GFX942
buffer/global/flat_store nt=1
volatile (易失性)
buffer/global/flat_store sc0=1 sc1=1
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
store (存储)
none
none
local (本地)
ds_store
无序原子
load atomic (原子加载)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
store atomic (原子存储)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
atomicrmw (原子读-修改-写)
unordered (无序)
any (任何)
any (任何)
与单调原子相同.
单调原子
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_load
load atomic (原子加载)
monotonic (单调)
workgroup
global
generic (通用)
buffer/global/flat_load sc0=1
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_load
load atomic (原子加载)
monotonic (单调)
agent
global
generic (通用)
buffer/global/flat_load sc1=1
load atomic (原子加载)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_load sc0=1 sc1=1
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
workgroup
global
generic (通用)
buffer/global/flat_store sc0=1
store atomic (原子存储)
monotonic (单调)
agent
global
generic (通用)
buffer/global/flat_store sc1=1
store atomic (原子存储)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_store sc0=1 sc1=1
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
agent
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
system
global
generic (通用)
buffer/global/flat_atomic sc1=1
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
获取原子
load atomic (原子加载)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
acquire (获取)
workgroup
global
buffer/global_load sc0=1
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在以下 buffer_inv 之前发生。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_load
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
load atomic (原子加载)
acquire (获取)
workgroup
generic (通用)
flat_load sc0=1
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
agent
global
buffer/global_load sc1=1
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保加载在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
system
global
buffer/global/flat_load sc0=1 sc1=1
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保加载在使缓存无效之前已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
load atomic (原子加载)
acquire (获取)
agent
generic (通用)
flat_load sc1=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_inv 之前发生。
确保 flat_load 在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
system
generic (通用)
flat_load sc0=1 sc1=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_inv 之前发生。
确保在使缓存失效之前,flat_load 已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
global
buffer/global_atomic
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在以下 buffer_inv 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
generic (通用)
flat_atomic
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
global
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
system
global
buffer/global_atomic sc1=1
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
generic (通用)
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_inv 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
system
generic (通用)
flat_atomic sc1=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
fence (栅栏)
acquire (获取)
singlethread
wavefront
none
none
fence (栅栏)
acquire (获取)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load atomic/ atomicrmw(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比 fence 配对原子操作读取的值旧。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acquire (获取)
agent
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_inv 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acquire (获取)
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_inv 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
释放原子
store atomic (原子存储)
release (释放)
singlethread
wavefront
global
generic (通用)
- GFX942
buffer/global/flat_store
store atomic (原子存储)
release (释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
store atomic (原子存储)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作在执行正在释放的存储之前完成。
- GFX942
buffer/global/flat_store sc0=1
store atomic (原子存储)
release (释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_store
store atomic (原子存储)
release (释放)
agent
global
generic (通用)
buffer_wbl2 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保在执行正在释放的存储之前,所有对内存的操作都已完成。
- GFX942
buffer/global/flat_store sc1=1
store atomic (原子存储)
release (释放)
system
global
generic (通用)
buffer_wbl2 sc0=1 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作和 L2 写回在执行正在释放的存储之前完成。
buffer/global/flat_store sc0=1 sc1=1
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global/flat_atomic sc0=1
atomicrmw (原子读-修改-写)
release (释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
agent
global
generic (通用)
buffer_wbl2 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局和本地内存的操作都已完成。
buffer/global/flat_atomic sc1=1
atomicrmw (原子读-修改-写)
release (释放)
system
global
generic (通用)
buffer_wbl2 sc0=1 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作和 L2 写回在执行正在释放的存储之前完成。
buffer/global/flat_atomic sc0=1 sc1=1
fence (栅栏)
release (释放)
singlethread
wavefront
none
none
fence (栅栏)
release (释放)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/load atomic/store/store atomic/atomicrmw 之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
agent
none
buffer_wbl2 sc1=1
如果是 OpenCL 且地址空间是本地的,则省略。
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
system
none
buffer_wbl2 sc0=1 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
获取-释放原子
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
global
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
如果不是 TgSplit 执行模式,则省略。
必须在以下 buffer_inv 之前发生。
确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果不是 TgSplit 执行模式,则省略 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
global
buffer_wbl2 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
buffer/global_atomic
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
system
global
buffer_wbl2 sc0=1 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保对全局内存和 L2 写回的所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic sc1=1
s_waitcnt vmcnt(0)
必须在后续 buffer_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
generic (通用)
buffer_wbl2 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
flat_atomic
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_inv 之前发生。
确保 atomicrmw 在使缓存无效之前已完成。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
system
generic (通用)
buffer_wbl2 sc0=1 sc1=1
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保对全局内存和 L2 写回的所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic sc1=1
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
fence (栅栏)
acq_rel (获取-释放)
singlethread
wavefront
none
none
fence (栅栏)
acq_rel (获取-释放)
workgroup
none
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0)。
但是,由于 LLVM 当前在 fence 上没有地址空间,因此需要保守地始终生成(参见先前 fence 的注释)。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/store/ load atomic/store atomic/ atomicrmw 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/load atomic/store/store atomic/atomicrmw 之后发生。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保所有内存操作在执行任何后续全局内存操作之前完成。
确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。
确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
必须在以下 buffer_inv 之前发生。
确保 acquire-fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 acquire-fence-paired-atomic 读取的值。
buffer_inv sc0=1
如果不是 TgSplit 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acq_rel (获取-释放)
agent
none
buffer_wbl2 sc1=1
如果是 OpenCL 且地址空间是本地的,则省略。
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在以下 buffer_inv 之前发生。
确保先前的全局/本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在使缓存无效之前已完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_inv sc1=1
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。
fence (栅栏)
acq_rel (获取-释放)
system
none
buffer_wbl2 sc0=1 sc1=1
如果是 OpenCL 且地址空间是本地的,则省略。
必须在后续 s_waitcnt 之前发生。
执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt vmcnt(0) 必须在任何先前的全局/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在以下 buffer_inv 之前发生。
确保先前的全局/本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在使缓存无效之前已完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_inv sc0=1 sc1=1
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。
顺序一致性原子
load atomic (原子加载)
seq_cst (顺序一致性)
singlethread
wavefront
global
local (本地)
generic (通用)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
global
generic (通用)
s_waitcnt lgkm/vmcnt(0)
如果不是 TgSplit 执行模式,则使用 lgkmcnt(0),如果是 TgSplit 执行模式,则使用 vmcnt(0)。
s_waitcnt lgkmcnt(0) 必须在先前的 local/generic load atomic/store atomic/atomicrmw(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt lgkmcnt(0),因此无需考虑。)
s_waitcnt vmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt vmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致的全局/本地内存指令在执行此顺序一致的指令之前完成。这可以防止对 seq_cst store 之后跟随 seq_cst load 进行重新排序。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 可以防止 load acquire 后跟 store release 的重新排序,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择 load 以使 s_waitcnt 尽可能晚,以便 store 可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
local (本地)
如果是 TgSplit 执行模式,则无法使用本地地址空间。
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt lgkmcnt(0),以允许它们根据以下规则独立移动。
s_waitcnt lgkmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt lgkmcnt(0),因此不需要考虑。)
s_waitcnt vmcnt(0) 必须在先前的全局/通用原子加载/原子存储/原子读-修改-写之后发生,其内存排序为 seq_cst,并且同步作用域相等或更宽。(请注意,seq_cst fence 有自己的 s_waitcnt vmcnt(0),因此不需要考虑。)
确保任何先前的顺序一致性全局内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
store atomic (原子存储)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的存储原子释放相同,除了即使对于 OpenCL 也必须生成所有指令。
atomicrmw (原子读-修改-写)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的 atomicrmw 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
fence (栅栏)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
none
与相应的 fence 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
内存模型 GFX10-GFX11¶
对于 GFX10-GFX11
每个代理 (agent) 都有多个着色器阵列 (SA)。
每个 SA 都有多个工作组处理器 (WGP)。
每个 WGP 都有多个计算单元 (CU)。
每个 CU 都有多个 SIMD,用于执行 wavefront。
单个工作组的 wavefront 在同一 WGP 中执行。在 CU wavefront 执行模式下,wavefront 可能由同一 CU 中不同的 SIMD 执行。在 WGP wavefront 执行模式下,wavefront 可能由同一 WGP 中不同 CU 中不同的 SIMD 执行。
每个 WGP 都有一个由在其上执行的工作组的 wavefront 共享的 LDS 内存。
WGP 的所有 LDS 操作都作为 wavefront 范围的操作以全局顺序执行,并且不涉及缓存。完成情况按执行顺序报告给 wavefront。
LDS 内存有多个请求队列,由 WGP 的 SIMD 共享。因此,同一工作组中不同 wavefront 执行的 LDS 操作可以相对于彼此重新排序,这可能导致向量内存操作相对于同一工作组中其他 wavefront 的 LDS 操作的可见性重新排序。需要
s_waitcnt lgkmcnt(0)
来确保工作组的 wavefront 之间的 LDS 操作和向量内存操作之间的同步,但同一 wavefront 执行的操作之间不需要同步。向量内存操作作为 wavefront 范围的操作执行。加载/存储/采样操作的完成情况按该 wavefront 执行的其他加载/存储/采样操作的执行顺序报告给 wavefront。
向量内存操作访问向量 L0 缓存。每个 CU 都有一个 L0 缓存。CU 的每个 SIMD 访问相同的 L0 缓存。因此,单个 wavefront 的 lane 之间不需要特殊操作来保证一致性。但是,需要
buffer_gl0_inv
来保证在同一工作组中执行的 wavefront 之间的一致性,因为它们可能在访问不同 L0 的不同 CU 的 SIMD 上执行。也需要buffer_gl0_inv
来保证在不同工作组中执行的 wavefront 之间的一致性,因为它们可能在不同的 WGP 上执行。标量内存操作访问标量 L0 缓存,该缓存由 WGP 上的所有 wavefront 共享。标量和向量 L0 缓存不一致。但是,标量操作以受限制的方式使用,因此不会影响内存模型。请参阅 内存空间。
向量和标量内存 L0 缓存使用 L1 缓存,该缓存由同一 SA 上的所有 WGP 共享。因此,单个工作组的 wavefront 之间不需要特殊操作来保证一致性。但是,需要
buffer_gl1_inv
来保证在不同工作组中执行的 wavefront 之间的一致性,因为它们可能在访问不同 L1 的不同 SA 上执行。L1 缓存具有独立的象限,以服务于不相交的虚拟地址范围。
每个 L0 缓存的每个 L1 象限都有一个单独的请求队列。因此,由不同 wavefront 执行的向量和标量内存操作(无论是在同一工作组还是不同工作组中执行,都可能在访问不同 L0 的不同 CU 上执行)可以相对于彼此重新排序。需要
s_waitcnt vmcnt(0) & vscnt(0)
来确保不同 wavefront 的向量内存操作之间的同步。它确保先前的向量内存操作在执行后续向量内存或 LDS 操作之前完成,因此可以用于满足 acquire、release 和顺序一致性的要求。L1 缓存使用 L2 缓存,该缓存由同一 Agent 上的所有 SA 共享。
L2 缓存具有独立的通道,用于服务于不相交的虚拟地址范围。
单个 SA 的每个 L1 象限访问不同的 L2 通道。每个 L1 象限的每个 L2 通道都有一个单独的请求队列。因此,由不同工作组(可能在不同的 SA 上执行)中的 wavefront 执行的向量和标量内存操作可以相对于彼此重新排序。需要
s_waitcnt vmcnt(0) & vscnt(0)
来确保不同 SA 的向量内存操作之间的同步。它确保先前的向量内存操作在执行后续向量内存之前完成,因此可以用于满足 acquire、release 和顺序一致性的要求。L2 缓存可以与某些目标上的其他代理保持相干,或者可以设置虚拟地址范围以绕过它,以确保系统相干性。
在 GFX10.3 和 GFX11 上,GPU 内存存在内存附加末级 (MALL) 缓存。MALL 缓存与 GPU 内存完全一致,并且对系统一致性没有影响。所有 Agent(GPU 和 CPU)都通过 MALL 缓存访问 GPU 内存。
标量内存操作仅用于访问在内核调度执行期间被证明不会更改的内存。这包括常量地址空间和程序作用域 const
变量的全局地址空间。因此,内核机器代码不必维护标量缓存以确保其与向量缓存相干。标量和向量缓存在内核调度之间由 CP 无效,因为常量地址空间数据可能在内核调度执行之间更改。参见 内存空间。
一个例外是,如果标量写入用于溢出 SGPR 寄存器。在这种情况下,AMDGPU 后端确保用于溢出的内存位置永远不会同时被向量内存操作访问。如果使用标量写入,则在 s_endpgm
之前和函数返回之前插入 s_dcache_wb
,因为这些位置可能被将来的 wavefront 用于向量内存指令,该 wavefront 使用相同的暂存区域,或者用于在同一地址创建帧的函数调用。不需要 s_dcache_inv
,因为所有标量写入在同一线程中都是写后读。
对于 kernarg 后备内存
CP 在每次内核调度开始时使 L0 和 L1 缓存失效。
在 dGPU 上,kernarg 后备内存作为 MTYPE UC(非缓存)访问,以避免需要使 L2 缓存失效。
在 APU 上,kernarg 后备内存作为 MTYPE CC(缓存一致性)访问,因此 L2 缓存将与 CPU 和其他 Agent 保持一致。
Scratch 后备内存(用于私有地址空间)使用 MTYPE NC(非一致性)访问。由于私有地址空间仅由单个线程访问,并且始终是先写后读,因此永远不需要使 L0 或 L1 缓存中的这些条目失效。
Wavefront 以本机模式执行,并按顺序报告加载和采样指令。在此模式下,vmcnt 按顺序报告加载、带返回值的原子操作和采样指令的完成情况,vscnt 按顺序报告存储和不带返回值的原子操作的完成情况。请参阅 GFX6-GFX12 的 compute_pgm_rsrc1 中的 MEM_ORDERED
字段。
Wavefront 可以在 WGP 或 CU wavefront 执行模式下执行
在 WGP wavefront 执行模式下,工作组的 wavefront 在 WGP 的两个 CU 的 SIMD 上执行。因此,工作组同步需要显式管理每个 CU 的 L0 缓存。此外,对工作组范围内的 L1 的访问需要显式排序,因为来自不同 CU 的访问是无序的。
在 CU wavefront 执行模式下,工作组的 wavefront 在 WGP 的单个 CU 的 SIMD 上执行。因此,工作组的所有全局内存访问都访问相同的 L0,这反过来确保 L1 访问是有序的,因此不需要显式管理缓存以进行工作组同步。
请参阅 GFX6-GFX12 的 compute_pgm_rsrc1 和 目标特性 中的 WGP_MODE
字段。
GFX10-GFX11 内存模型实现的代码序列在 AMDHSA 内存模型代码序列 GFX10-GFX11 表中定义。
表 82 AMDHSA 内存模型代码序列 GFX10-GFX11¶ LLVM 指令
LLVM 内存排序
LLVM 内存同步作用域
AMDGPU 地址空间
AMDGPU 机器代码 GFX10-GFX11
非原子
load (加载)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_load
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_load slc=1 dlc=1
如果是 GFX10,则省略 dlc=1。
volatile (易失性)
buffer/global/flat_load glc=1 dlc=1
s_waitcnt vmcnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
load (加载)
none
none
local (本地)
ds_load
store (存储)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_store
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_store glc=1 slc=1 dlc=1
如果是 GFX10,则省略 dlc=1。
volatile (易失性)
buffer/global/flat_store dlc=1
如果是 GFX10,则省略 dlc=1。
s_waitcnt vscnt(0)
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
store (存储)
none
none
local (本地)
ds_store
无序原子
load atomic (原子加载)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
store atomic (原子存储)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
atomicrmw (原子读-修改-写)
unordered (无序)
any (任何)
any (任何)
与单调原子相同.
单调原子
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
global
generic (通用)
buffer/global/flat_load
load atomic (原子加载)
monotonic (单调)
workgroup
global
generic (通用)
buffer/global/flat_load glc=1
如果是 CU wavefront 执行模式,则省略 glc=1。
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_load
load atomic (原子加载)
monotonic (单调)
agent
system
global
generic (通用)
buffer/global/flat_load glc=1 dlc=1
如果是 GFX11,则省略 dlc=1。
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_store
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_atomic
获取原子
load atomic (原子加载)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
acquire (获取)
workgroup
global
buffer/global_load glc=1
如果是 CU wavefront 执行模式,则省略 glc=1。
s_waitcnt vmcnt(0)
如果是 CU wavefront 执行模式,则省略。
必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
local (本地)
ds_load
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
generic (通用)
flat_load glc=1
如果是 CU wavefront 执行模式,则省略 glc=1。
s_waitcnt lgkmcnt(0) & vmcnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
agent
system
global
buffer/global_load glc=1 dlc=1
如果是 GFX11,则省略 dlc=1。
s_waitcnt vmcnt(0)
必须在后续 buffer_gl*_inv 之前发生。
确保在使缓存失效之前,加载已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
agent
system
generic (通用)
flat_load glc=1 dlc=1
如果是 GFX11,则省略 dlc=1。
s_waitcnt vmcnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在后续 buffer_gl*_invl 之前发生。
确保在使缓存失效之前,flat_load 已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
global
buffer/global_atomic
s_waitcnt vm/vscnt(0)
如果是 CU wavefront 执行模式,则省略。
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
local (本地)
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在以下 buffer_gl0_inv 之前发生。
确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。
buffer_gl0_inv
如果是 OpenCL,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
generic (通用)
flat_atomic
s_waitcnt lgkmcnt(0) & vm/vscnt(0)
如果是 CU wavefront 执行模式,则省略 vm/vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在以下 buffer_gl0_inv 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
global
buffer/global_atomic
s_waitcnt vm/vscnt(0)
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在后续 buffer_gl*_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
generic (通用)
flat_atomic
s_waitcnt vm/vscnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在后续 buffer_gl*_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acquire (获取)
singlethread
wavefront
none
none
fence (栅栏)
acquire (获取)
workgroup
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load atomic/ atomicrmw-with-return-value(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic atomicrmw-no-return-value(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_gl0_inv 之前发生。
确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acquire (获取)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load atomic/ atomicrmw-with-return-value(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic atomicrmw-no-return-value(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 fence-paired-atomic)之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
必须在以下 buffer_gl*_inv 之前发生。
确保 fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 fence-paired-atomic 读取的值。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
释放原子
store atomic (原子存储)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_store
store atomic (原子存储)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作在执行正在释放的存储之前完成。
buffer/global/flat_store
store atomic (原子存储)
release (释放)
workgroup
local (本地)
s_waitcnt vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt vscnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
必须在后续的存储之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_store
store atomic (原子存储)
release (释放)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的存储之前发生。
确保所有内存操作在执行正在释放的存储之前完成。
buffer/global/flat_store
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
local (本地)
s_waitcnt vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt vscnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
必须在后续的存储之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
agent
system
global
generic (通用)
- s_waitcnt lgkmcnt(0) &
vmcnt(0) & vscnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局和本地内存的操作都已完成。
buffer/global/flat_atomic
fence (栅栏)
release (释放)
singlethread
wavefront
none
none
fence (栅栏)
release (释放)
workgroup
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/store/load atomic/store atomic/ atomicrmw 之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
获取-释放原子
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
global
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic
s_waitcnt vm/vscnt(0)
如果是 CU wavefront 执行模式,则省略。
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在以下 buffer_gl0_inv 之前发生。
确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
local (本地)
s_waitcnt vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt vscnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
必须在后续的存储之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_atomic
s_waitcnt lgkmcnt(0)
如果是 OpenCL,则省略。
必须在以下 buffer_gl0_inv 之前发生。
确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL,则省略 lgkmcnt(0)。
必须在以下 buffer_gl0_inv 之前发生。
确保任何后续全局数据读取都不早于正在获取的 load atomic 值。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
global
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
buffer/global_atomic
s_waitcnt vm/vscnt(0)
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在后续 buffer_gl*_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
s_waitcnt vm/vscnt(0) & lgkmcnt(0)
如果是 OpenCL,则省略 lgkmcnt(0)。
如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。
必须在后续 buffer_gl*_inv 之前发生。
确保在使缓存失效之前,atomicrmw 已完成。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acq_rel (获取-释放)
singlethread
wavefront
none
none
fence (栅栏)
acq_rel (获取-释放)
workgroup
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
但是,由于 LLVM 当前在 fence 上没有地址空间,因此需要保守地始终生成(参见先前 fence 的注释)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的 local/generic load/store/load atomic/store atomic/ atomicrmw 之后发生。
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保所有内存操作在执行任何后续全局内存操作之前完成。
确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。
确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
必须在以下 buffer_gl0_inv 之前发生。
确保 acquire-fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 acquire-fence-paired-atomic 读取的值。
buffer_gl0_inv
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acq_rel (获取-释放)
agent
system
none
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 OpenCL 且地址空间不是通用,则省略 lgkmcnt(0)。
如果是 OpenCL 且地址空间是本地的,则省略 vmcnt(0) 和 vscnt(0)。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_waitcnt vscnt(0) 必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_waitcnt lgkmcnt(0) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
必须在以下 buffer_gl*_inv 之前发生。
确保先前的 global/local/generic load atomic/atomicrmw(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 acquire-fence-paired-atomic)在使缓存失效之前完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
buffer_gl1_inv; buffer_gl0_inv
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。
顺序一致性原子
load atomic (原子加载)
seq_cst (顺序一致性)
singlethread
wavefront
global
local (本地)
generic (通用)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt lgkmcnt(0) 必须在先前的 local/generic load atomic/store atomic/atomicrmw(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt lgkmcnt(0),因此无需考虑。)
s_waitcnt vmcnt(0) 必须在先前的 global/generic load atomic/ atomicrmw-with-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vmcnt(0),因此无需考虑。)
s_waitcnt vscnt(0) 必须在先前的 global/generic store atomic/ atomicrmw-no-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vscnt(0),因此无需考虑。)
确保任何先前的顺序一致的全局/本地内存指令在执行此顺序一致的指令之前完成。这可以防止对 seq_cst store 之后跟随 seq_cst load 进行重新排序。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 可以防止 load acquire 后跟 store release 的重新排序,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择 load 以使 s_waitcnt 尽可能晚,以便 store 可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
local (本地)
s_waitcnt vmcnt(0) & vscnt(0)
如果是 CU wavefront 执行模式,则省略。
可以拆分为单独的 s_waitcnt vmcnt(0) 和 s_waitcnt vscnt(0),以允许根据以下规则独立移动它们。
s_waitcnt vmcnt(0) 必须在先前的 global/generic load atomic/ atomicrmw-with-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vmcnt(0),因此无需考虑。)
s_waitcnt vscnt(0) 必须在先前的 global/generic store atomic/ atomicrmw-no-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vscnt(0),因此无需考虑。)
确保任何先前的顺序一致性全局内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
agent
system
global
generic (通用)
s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)
可以拆分为单独的 s_waitcnt vmcnt(0)、s_waitcnt vscnt(0) 和 s_waitcnt lgkmcnt(0),以允许根据以下规则独立移动它们。
s_waitcnt lgkmcnt(0) 必须在先前的 local load atomic/store atomic/atomicrmw(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt lgkmcnt(0),因此无需考虑。)
s_waitcnt vmcnt(0) 必须在先前的 global/generic load atomic/ atomicrmw-with-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vmcnt(0),因此无需考虑。)
s_waitcnt vscnt(0) 必须在先前的 global/generic store atomic/ atomicrmw-no-return-value(具有 seq_cst 内存排序和相等或更宽的同步范围)之后发生。(请注意,seq_cst fences 有自己的 s_waitcnt vscnt(0),因此无需考虑。)
确保任何先前的顺序一致性全局内存指令在执行此顺序一致性指令之前已完成。这防止了重新排序 seq_cst 存储,然后是 seq_cst 加载。(请注意,seq_cst 比 acquire/release 更强,因为 release 的 s_waitcnt 阻止了加载 acquire 后跟存储 release 的重新排序,但是没有阻止存储 release 后跟加载 acquire 以乱序完成。s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。我们选择加载以使 s_waitcnt 尽可能晚,以便存储可能已经完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
store atomic (原子存储)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的存储原子释放相同,除了即使对于 OpenCL 也必须生成所有指令。
atomicrmw (原子读-修改-写)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的 atomicrmw 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
fence (栅栏)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
none
与相应的 fence 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
内存模型 GFX12¶
对于 GFX12
每个代理 (agent) 都有多个着色器阵列 (SA)。
每个 SA 都有多个工作组处理器 (WGP)。
每个 WGP 都有多个计算单元 (CU)。
每个 CU 都有多个 SIMD,用于执行 wavefront。
单个工作组的 wavefront 在同一 WGP 中执行。
在 CU wavefront 执行模式下,wavefront 可能由同一 CU 中不同的 SIMD 执行。
在 WGP wavefront 执行模式下,wavefront 可能由同一 WGP 中不同 CU 中不同的 SIMD 执行。
每个 WGP 都有一个由在其上执行的工作组的 wavefront 共享的 LDS 内存。
WGP 的所有 LDS 操作都作为 wavefront 范围的操作以全局顺序执行,并且不涉及缓存。完成情况按执行顺序报告给 wavefront。
LDS 内存有多个请求队列,由 WGP 的 SIMD 共享。因此,同一工作组中不同 wavefront 执行的 LDS 操作可以相对于彼此重新排序,这可能导致向量内存操作相对于同一工作组中其他 wavefront 的 LDS 操作的可见性重新排序。需要
s_wait_dscnt 0x0
来确保工作组的 wavefront 之间的 LDS 操作和向量内存操作之间的同步,但同一 wavefront 执行的操作之间不需要同步。向量内存操作作为 wavefront 范围的操作执行。向量内存操作分为不同类型。向量内存操作的完成情况在类型内按顺序报告给 wavefront,但在类型之间可能乱序。向量内存操作的类型(及其关联的
s_wait
指令)是LDS:
s_wait_dscnt
Load (global, scratch, flat, buffer and image):
s_wait_loadcnt
Store (global, scratch, flat, buffer and image):
s_wait_storecnt
Sample and Gather4:
s_wait_samplecnt
BVH:
s_wait_bvhcnt
向量和标量内存指令包含一个
SCOPE
字段,其值对应于每个缓存级别。SCOPE
确定缓存是否可以在本地完成操作,或者是否需要将操作转发到下一个缓存级别。SCOPE
值是SCOPE_CU
: 计算单元(注意:不受 CU/WGP 模式影响)SCOPE_SE
: 着色器引擎SCOPE_DEV
: 设备/AgentSCOPE_SYS
: 系统
当具有给定
SCOPE
的内存操作到达SCOPE
值较小的缓存时,它会转发到下一级缓存。当具有给定
SCOPE
的内存操作到达SCOPE
值大于或等于其自身的缓存时,操作可以继续读取可以命中缓存
写入可以发生在此缓存中,并且事务从此缓存级别确认。
RMW 操作可以在本地完成。
global_inv
、global_wb
和global_wbinv
指令用于使缓存失效、写回和写回+失效。受影响的缓存由指令的SCOPE:
控制。global_inv
使范围严格小于指令的缓存失效。失效请求不能与挂起或即将到来的内存操作重新排序。global_wb
是一个写回操作,它还确保在较低范围级别完成的先前内存操作已到达global_wb
的SCOPE:
。在 gfx120x 中,对于
SCOPE_SYS
以外的范围,可以省略global_wb
。
向量内存操作访问向量 L0 缓存。每个 CU 都有一个 L0 缓存。CU 的每个 SIMD 访问相同的 L0 缓存。因此,单个 wavefront 的 lane 之间不需要特殊操作来保证一致性。为了在同一工作组中执行的 wavefront 之间实现一致性
在 CU wavefront 执行模式下,不需要特殊操作。
在 WGP wavefront 执行模式下,需要
global_inv scope:SCOPE_SE
,因为 wavefront 可能在访问不同 L0 的不同 CU 的 SIMD 上执行。
标量内存操作访问标量 L0 缓存,该缓存由 WGP 上的所有 wavefront 共享。标量和向量 L0 缓存不一致。但是,标量操作以受限制的方式使用,因此不会影响内存模型。请参阅 内存空间。
向量和标量内存 L0 缓存使用 L1 缓冲区,该缓冲区由同一 SA 上的所有 WGP 共享。L1 缓冲区充当 SA 内客户端到 L2 的桥梁。
L1 缓冲区具有独立的象限,以服务于不相交的虚拟地址范围。
每个 L0 缓存的每个 L1 象限都有一个单独的请求队列。因此,由不同 wavefront 执行的向量和标量内存操作(无论是在同一工作组还是不同工作组中执行,都可能在访问不同 L0 的不同 CU 上执行)可以相对于彼此重新排序。需要以下部分或全部 wait 指令来确保不同 wavefront 的向量内存操作之间的同步。它确保先前的向量内存操作在执行后续向量内存或 LDS 操作之前完成,因此可以用于满足 acquire、release 和顺序一致性的要求。
s_wait_loadcnt 0x0
s_wait_samplecnt 0x0
s_wait_bvhcnt 0x0
s_wait_storecnt 0x0
L1 缓冲区使用 L2 缓存,该缓存由同一 Agent 上的所有 SA 共享。
L2 缓存具有独立的通道,用于服务于不相交的虚拟地址范围。
单个 SA 的每个 L1 象限访问不同的 L2 通道。每个 L1 象限的每个 L2 通道都有一个单独的请求队列。因此,由不同工作组(可能在不同的 SA 上执行)中的 wavefront 执行的向量和标量内存操作可以相对于彼此重新排序。需要以下部分或全部 wait 指令来确保不同 SA 的向量内存操作之间的同步。它确保先前的向量内存操作在执行后续向量内存之前完成,因此可以用于满足 acquire、release 和顺序一致性的要求。
s_wait_loadcnt 0x0
s_wait_samplecnt 0x0
s_wait_bvhcnt 0x0
s_wait_storecnt 0x0
L2 缓存可以与其他 Agent 保持一致,或者可以设置虚拟地址范围以绕过它,从而确保系统一致性。
GPU 内存存在内存附加末级 (MALL) 缓存。MALL 缓存与 GPU 内存完全一致,并且对系统一致性没有影响。所有 Agent(GPU 和 CPU)都通过 MALL 缓存访问 GPU 内存。
标量内存操作仅用于访问在内核调度执行期间被证明不会更改的内存。这包括常量地址空间和程序作用域 const
变量的全局地址空间。因此,内核机器代码不必维护标量缓存以确保其与向量缓存相干。标量和向量缓存在内核调度之间由 CP 无效,因为常量地址空间数据可能在内核调度执行之间更改。参见 内存空间。
对于 kernarg 后备内存
CP 在每次内核调度开始时使缓存失效。
在 dGPU 上,kernarg 后备内存作为 MTYPE UC(非缓存)访问,以避免需要使 L2 缓存失效。
在 APU 上,kernarg 后备内存作为 MTYPE CC(缓存一致性)访问,因此 L2 缓存将与 CPU 和其他 Agent 保持一致。
Scratch 后备内存(用于私有地址空间)使用 MTYPE NC(非一致性)访问。由于私有地址空间仅由单个线程访问,并且始终是先写后读,因此永远不需要使 L0 中的这些条目失效。
Wavefront 可以在 WGP 或 CU wavefront 执行模式下执行
在 WGP wavefront 执行模式下,工作组的 wavefront 在 WGP 的两个 CU 的 SIMD 上执行。因此,工作组同步需要显式管理每个 CU 的 L0 缓存。此外,对工作组范围内的 L1 的访问需要显式排序,因为来自不同 CU 的访问是无序的。
在 CU wavefront 执行模式下,工作组的 wavefront 在 WGP 的单个 CU 的 SIMD 上执行。因此,工作组的所有全局内存访问都访问相同的 L0,这反过来确保 L1 访问是有序的,因此不需要显式管理缓存以进行工作组同步。
请参阅 GFX6-GFX12 的 compute_pgm_rsrc1 和 目标特性 中的 WGP_MODE
字段。
GFX12 内存模型实现的代码序列在 AMDHSA 内存模型代码序列 GFX12 表中定义。
LLVM IR 同步范围到 GFX12 指令 scope
操作数的映射在 AMDHSA 内存模型代码序列 GFX12 - 指令范围 表中定义。
该表仅当且仅当被 AMDHSA 内存模型代码序列 GFX12 中的条目直接引用时才适用,并且仅适用于引用该表的代码序列中的指令。
表 83 AMDHSA 内存模型代码序列 GFX12 - 指令作用域¶ LLVM 同步作用域
CU 波前执行模式
WGP 波前执行模式
none
作用域:SCOPE_SYS
作用域:SCOPE_SYS
system
作用域:SCOPE_SYS
作用域:SCOPE_SYS
agent
作用域:SCOPE_DEV
作用域:SCOPE_DEV
workgroup
none
作用域:SCOPE_SE
wavefront
none
none
singlethread
none
none
one-as
作用域:SCOPE_SYS
作用域:SCOPE_SYS
system-one-as
作用域:SCOPE_SYS
作用域:SCOPE_SYS
agent-one-as
作用域:SCOPE_DEV
作用域:SCOPE_DEV
workgroup-one-as
none
作用域:SCOPE_SE
wavefront-one-as
none
none
singlethread-one-as
none
none
表 84 AMDHSA 内存模型代码序列 GFX12¶ LLVM 指令
LLVM 内存排序
LLVM 内存同步作用域
AMDGPU 地址空间
AMDGPU 机器码 GFX12
非原子
load (加载)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_load
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_load
th:TH_LOAD_NT
volatile (易失性)
buffer/global/flat_load
scope:SCOPE_SYS
s_wait_loadcnt 0x0
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
load (加载)
none
none
local (本地)
ds_load
store (存储)
none
none
global
generic (通用)
private
constant
!volatile & !nontemporal (非易失性 & 非临时性)
buffer/global/flat_store
!volatile & nontemporal (非易失性 & 临时性)
buffer/global/flat_store
th:TH_STORE_NT
volatile (易失性)
buffer/global/flat_store
scope:SCOPE_SYS
s_wait_storecnt 0x0
必须在任何后续的易失性全局/通用加载/存储之前发生。
确保对不同地址的易失性操作不会被硬件重新排序。
store (存储)
none
none
local (本地)
ds_store
无序原子
load atomic (原子加载)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
store atomic (原子存储)
unordered (无序)
any (任何)
any (任何)
与非原子相同.
atomicrmw (原子读-修改-写)
unordered (无序)
any (任何)
any (任何)
与单调原子相同.
单调原子
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_load
load atomic (原子加载)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_load
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_store
store atomic (原子存储)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_store
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
agent
system
global
generic (通用)
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
monotonic (单调)
singlethread
wavefront
workgroup
local (本地)
ds_atomic
获取原子
load atomic (原子加载)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_load
load atomic (原子加载)
acquire (获取)
workgroup
global
buffer/global_load
scope:SCOPE_SE
s_wait_loadcnt 0x0
如果是 CU wavefront 执行模式,则省略。
必须在以下
global_inv
之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
local (本地)
ds_load
s_wait_dscnt 0x0
如果是 OpenCL,则省略。
必须在以下
global_inv
之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
global_inv scope:SCOPE_SE
如果为 OpenCL 或 CU 波前执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
workgroup
generic (通用)
flat_load
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
必须在以下
global_inv
之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
load atomic (原子加载)
acquire (获取)
agent
system
global
buffer/global_load
s_wait_loadcnt 0x0
必须在以下
global_inv
之前发生。确保在使缓存失效之前,加载已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
load atomic (原子加载)
acquire (获取)
agent
system
generic (通用)
flat_load
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
必须在以下
global_inv
之前发生。确保在使缓存失效之前,flat_load 已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
global
buffer/global_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
带返回值的 Atomics_wait_loadcnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
如果是 CU wavefront 执行模式,则省略。
必须在以下
global_inv
之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
local (本地)
ds_atomic
s_wait_dscnt 0x0
如果是 OpenCL,则省略。
必须在以下
global_inv
之前发生。确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。
global_inv scope:SCOPE_SE
如果是 OpenCL,则省略。
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
workgroup
generic (通用)
flat_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
带返回值的 Atomics_wait_loadcnt 0x0
s_wait_dscnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
s_wait_dscnt 0x0
如果为 CU 波前执行模式,则对于不带返回值的 atomic,省略所有内容,对于带返回值的 atomic,仅发出
s_wait_dscnt 0x0
。如果为 OpenCL,则省略
s_wait_dscnt 0x0
必须在以下
global_inv
之前发生。确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
global
buffer/global_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
带返回值的 Atomics_wait_loadcnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
必须在以下
global_inv
之前发生。确保在使缓存失效之前,atomicrmw 已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acquire (获取)
agent
system
generic (通用)
flat_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
带返回值的 Atomics_wait_loadcnt 0x0
s_wait_dscnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略 dscnt
必须在以下 global_inv 之前发生
确保在使缓存失效之前,atomicrmw 已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acquire (获取)
singlethread
wavefront
none
none
fence (栅栏)
acquire (获取)
workgroup
none
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
如果为 OpenCL 且地址空间为 local,则省略所有内容。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
注意:我们不必使用
s_wait_samplecnt 0x0
或s_wait_bvhcnt 0x0
,因为没有栅栏可以与 atomic sample 或 BVH 指令配对。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
必须在任何先前的 global/generic load atomic/ atomicrmw-with-return-value 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。
s_wait_storecnt 0x0
必须在任何先前的 global/generic atomicrmw-no-return-value 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load atomic/atomicrmw 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。必须在以下
global_inv
之前发生。确保 fence 配对原子操作在使缓存无效之前已完成。因此,任何后续读取的位置都必须不比 fence 配对原子操作读取的值旧。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acquire (获取)
agent
none
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。如果为 OpenCL 且地址空间为 local,则省略所有内容。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
注意:我们不必使用
s_wait_samplecnt 0x0
或s_wait_bvhcnt 0x0
,因为没有栅栏可以与 atomic sample 或 BVH 指令配对。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
必须在任何先前的 global/generic load atomic/ atomicrmw-with-return-value 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。
s_wait_storecnt 0x0
必须在任何先前的 global/generic atomicrmw-no-return-value 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load atomic/atomicrmw 之后发生,且该指令具有相等或更宽的同步作用域以及强于 unordered 的内存排序(这称为栅栏配对原子操作)。必须在以下
global_inv
之前发生确保 fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 fence-paired-atomic 读取的值。
global_inv
确保后续加载不会看到过时的数据。
释放原子
store atomic (原子存储)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_store
store atomic (原子存储)
release (释放)
workgroup
global
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。确保所有内存操作在执行正在释放的存储之前完成。
buffer/global/flat_store
store atomic (原子存储)
release (释放)
workgroup
local (本地)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果是 OpenCL,则省略。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。必须在后续的存储之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_store
store atomic (原子存储)
release (释放)
agent
system
global
generic (通用)
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在后续的存储之前发生。
确保所有内存操作在执行正在释放的存储之前完成。
buffer/global/flat_store
atomicrmw (原子读-修改-写)
release (释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
global
generic (通用)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。如果为 OpenCL 和 CU 波前执行模式,则全部省略。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在以下 atomic 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global/flat_atomic
atomicrmw (原子读-修改-写)
release (释放)
workgroup
local (本地)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则全部省略。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。必须在以下 atomic 之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_atomic
atomicrmw (原子读-修改-写)
release (释放)
agent
system
global
generic (通用)
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在以下 atomic 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局和本地内存的操作都已完成。
buffer/global/flat_atomic
fence (栅栏)
release (释放)
singlethread
wavefront
none
none
fence (栅栏)
release (释放)
workgroup
none
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。如果为 OpenCL 且地址空间为 local,则省略所有内容。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/ atomicrmw 之后发生。必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
fence (栅栏)
release (释放)
agent
system
none
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
OpenCLs_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。如果为 OpenCL 且地址空间为 local,则省略所有内容。
有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。
确保在执行后续的 fence 配对原子操作之前,所有内存操作都已完成。
获取-释放原子
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
singlethread
wavefront
global
local (本地)
generic (通用)
buffer/global/ds/flat_atomic
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
global
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
buffer/global_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
。
带返回值的 Atomics_wait_loadcnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
如果是 CU wavefront 执行模式,则省略。
必须在以下
global_inv
之前发生。确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
local (本地)
- 1 |
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果是 OpenCL,则省略。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。必须在后续的存储之前发生。
确保所有全局内存操作在执行正在释放的存储之前完成。
ds_atomic
s_wait_dscnt 0x0
如果是 OpenCL,则省略。
必须在以下
global_inv
之前发生。确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
如果是 OpenCL,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
workgroup
generic (通用)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_loadcnt 0x0
。等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
。
不带返回值的 Atomics_wait_dscnt 0x0
s_wait_storecnt 0x0
带返回值的 Atomics_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
必须在以下
global_inv
之前发生。确保任何后续全局数据读取都不早于正在获取的 load atomic 值。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
global
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在后续的 atomicrmw 之前发生。
确保在执行正在释放的 atomicrmw 之前,所有对全局内存的操作都已完成。
buffer/global_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
。
带返回值的 Atomics_wait_loadcnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
必须在以下
global_inv
之前发生。确保在使缓存失效之前,atomicrmw 已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
atomicrmw (原子读-修改-写)
acq_rel (获取-释放)
agent
system
generic (通用)
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在后续的 atomicrmw 之前发生。
确保所有内存操作在执行正在释放的 atomicrmw 之前完成。
flat_atomic
如果为带返回值的 atomic,则使用
th:TH_ATOMIC_RETURN
。
带返回值的 Atomics_wait_loadcnt 0x0
s_wait_dscnt 0x0
不带返回值的 Atomics_wait_storecnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
。必须在以下
global_inv
之前发生。确保在使缓存失效之前,atomicrmw 已完成。
global_inv
必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。
fence (栅栏)
acq_rel (获取-释放)
singlethread
wavefront
none
none
fence (栅栏)
acq_rel (获取-释放)
workgroup
none
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL 且地址空间不是 generic,则省略
s_wait_dscnt 0x0
如果为 OpenCL 且地址空间为 local,则除了
s_wait_dscnt 0x0
之外全部省略。有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/ atomicrmw 之后发生。必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保所有内存操作在执行任何后续全局内存操作之前完成。
确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。
确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
必须在以下
global_inv
之前发生。确保 acquire-fence-paired atomic 在使缓存失效之前完成。因此,任何后续读取的位置都不能早于 acquire-fence-paired-atomic 读取的值。
global_inv scope:SCOPE_SE
如果是 CU wavefront 执行模式,则省略。
确保后续加载不会看到过时的数据。
fence (栅栏)
acq_rel (获取-释放)
agent
system
none
global_wb scope:SCOPE_SYS
如果是代理作用域,则省略。
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL 且地址空间不是 generic,则省略
s_wait_dscnt 0x0
如果为 OpenCL 且地址空间为 local,则除了
s_wait_dscnt 0x0
之外全部省略。有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在任何先前的 global/generic load/load atomic/ atomicrmw-with-return-value 之后发生。
s_wait_storecnt 0x0
必须在global_wb
(如果存在)之后或任何先前的 global/generic store/store atomic/ atomicrmw-no-return-value 之后发生。
s_wait_dscnt 0x0
必须在任何先前的 local/generic load/store/load atomic/store atomic/atomicrmw 之后发生。必须在以下
global_inv
之前发生确保先前的 global/local/generic load atomic/atomicrmw(具有相等或更宽的同步范围和比无序更强的内存排序,这称为 acquire-fence-paired-atomic)在使缓存失效之前完成。这满足了 acquire 的要求。
确保在后续的全局/本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。
global_inv scope:
必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。
确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。
顺序一致性原子
load atomic (原子加载)
seq_cst (顺序一致性)
singlethread
wavefront
global
local (本地)
generic (通用)
与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
global
generic (通用)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
等待可以根据以下规则独立移动
s_wait_dscnt 0x0
必须在先前的 local/generic load atomic/store atomic/atomicrmw 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait_dscnt 0x0
,因此无需考虑。)
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在先前的 global/generic load atomic/ atomicrmw-with-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的等待,因此无需考虑。)
s_wait_storecnt 0x0
必须在先前的 global/generic store atomic/ atomicrmw-no-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait_storecnt 0x0
,因此无需考虑。)确保任何先前的顺序一致的 global/local 内存指令在执行此顺序一致的指令之前完成。 这可以防止重新排序 seq_cst store,然后重新排序 seq_cst load。(请注意,seq_cst 比 acquire/release 更强,因为 load acquire 后跟 store release 的重新排序会被 release 的
s_wait
阻止,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。s_wait
可以放在 seq_store 之后或 seq_load 之前。 我们选择 load 是为了使s_wait
尽可能晚,以便 store 可能已完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
workgroup
local (本地)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
CU 波前执行模式s_wait_dscnt 0x0
如果为 OpenCL,则全部省略。
等待可以根据以下规则独立移动
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在先前的 global/generic load atomic/ atomicrmw-with-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait
,因此无需考虑。)
s_wait_storecnt 0x0
必须在先前的 global/generic store atomic/ atomicrmw-no-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait_storecnt 0x0
,因此无需考虑。)确保任何先前的顺序一致的 global 内存指令在执行此顺序一致的指令之前完成。 这可以防止重新排序 seq_cst store,然后重新排序 seq_cst load。(请注意,seq_cst 比 acquire/release 更强,因为 load acquire 后跟 store release 的重新排序会被 release 的
s_wait
阻止,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。 s_waitcnt 可以放在 seq_store 之后或 seq_load 之前。 我们选择 load 是为了使s_wait
尽可能晚,以便 store 可能已完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
load atomic (原子加载)
seq_cst (顺序一致性)
agent
system
global
generic (通用)
s_wait_bvhcnt 0x0
s_wait_samplecnt 0x0
s_wait_storecnt 0x0
s_wait_loadcnt 0x0
s_wait_dscnt 0x0
如果为 OpenCL,则省略
s_wait_dscnt 0x0
等待可以根据以下规则独立移动
s_wait_dscnt 0x0
必须在先前的 local load atomic/store atomic/atomicrmw 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait_dscnt 0x0
,因此无需考虑。)
s_wait_loadcnt 0x0
、s_wait_samplecnt 0x0
和s_wait_bvhcnt 0x0
必须在先前的 global/generic load atomic/ atomicrmw-with-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait
,因此无需考虑。)
s_wait_storecnt 0x0
必须在先前的 global/generic store atomic/ atomicrmw-no-return-value 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的s_wait_storecnt 0x0
,因此无需考虑。)确保任何先前的顺序一致的 global 内存指令在执行此顺序一致的指令之前完成。 这可以防止重新排序 seq_cst store,然后重新排序 seq_cst load。(请注意,seq_cst 比 acquire/release 更强,因为 load acquire 后跟 store release 的重新排序会被 release 的
s_wait
阻止,但没有任何东西可以阻止 store release 后跟 load acquire 乱序完成。s_wait
可以放在 seq_store 之后或 seq_load 之前。 我们选择 load 是为了使s_wait
尽可能晚,以便 store 可能已完成。)
后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。
store atomic (原子存储)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的存储原子释放相同,除了即使对于 OpenCL 也必须生成所有指令。
atomicrmw (原子读-修改-写)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
global
local (本地)
generic (通用)
与相应的 atomicrmw 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
fence (栅栏)
seq_cst (顺序一致性)
singlethread
wavefront
workgroup
agent
system
none
与相应的 fence 获取-释放相同,除了即使对于 OpenCL 也必须生成所有指令。
陷阱处理程序 ABI¶
对于 AMDGPU 后端为 HSA [HSA] 兼容运行时生成的代码对象(参见 AMDGPU 操作系统),运行时安装一个陷阱处理程序,该程序支持 s_trap
指令。 有关用法,请参见
AMDGPU AMDHSA OS 代码对象 V4 及更高版本的陷阱处理程序
表 85 AMDGPU AMDHSA OS 代码对象 V2 的陷阱处理程序¶ 用法
代码序列
陷阱处理程序输入
描述
保留
s_trap 0x00
硬件保留。
debugtrap(arg)
s_trap 0x01
SGPR0-1
:queue_ptr
VGPR0
:arg
为 Finalizer HSA
debugtrap
内联函数保留(未实现)。llvm.trap
s_trap 0x02
SGPR0-1
:queue_ptr
导致波前停止,PC 指向陷阱指令。 关联的队列会发出信号,使其进入错误状态。 当队列进入错误状态时,在该队列上执行调度的波前将被终止。
llvm.debugtrap
s_trap 0x03
none
如果未启用调试器,则行为类似于空操作。 进入陷阱处理程序并立即返回以继续执行波前。
如果启用了调试器,则会导致调试器报告 debug trap,并且波前进入暂停状态,PC 指向该指令。 调试器必须增加 PC 并恢复波前。
保留
s_trap 0x04
保留。
保留
s_trap 0x05
保留。
保留
s_trap 0x06
保留。
保留
s_trap 0x07
保留。
保留
s_trap 0x08
保留。
保留
s_trap 0xfe
保留。
保留
s_trap 0xff
保留。
表 86 AMDGPU AMDHSA OS 代码对象 V3 的陷阱处理程序¶ 用法
代码序列
陷阱处理程序输入
描述
保留
s_trap 0x00
硬件保留。
调试器断点
s_trap 0x01
none
保留供调试器用于断点。 导致波前停止,PC 指向陷阱指令。 调试器负责恢复波前,包括断点覆盖的指令。
llvm.trap
s_trap 0x02
SGPR0-1
:
queue_ptr
导致波前停止,PC 指向陷阱指令。 关联的队列会发出信号,使其进入错误状态。 当队列进入错误状态时,在该队列上执行调度的波前将被终止。
llvm.debugtrap
s_trap 0x03
none
如果未启用调试器,则行为类似于空操作。 进入陷阱处理程序并立即返回以继续执行波前。
如果启用了调试器,则会导致调试器报告 debug trap,并且波前进入暂停状态,PC 指向该指令。 调试器必须增加 PC 并恢复波前。
保留
s_trap 0x04
保留。
保留
s_trap 0x05
保留。
保留
s_trap 0x06
保留。
保留
s_trap 0x07
保留。
保留
s_trap 0x08
保留。
保留
s_trap 0xfe
保留。
保留
s_trap 0xff
保留。
表 87 AMDGPU AMDHSA OS 代码对象 V4 及更高版本的陷阱处理程序¶ 用法
代码序列
GFX6-GFX8 输入
GFX9-GFX11 输入
描述
保留
s_trap 0x00
硬件保留。
调试器断点
s_trap 0x01
none
none
保留供调试器用于断点。 导致波前停止,PC 指向陷阱指令。 调试器负责恢复波前,包括断点覆盖的指令。
llvm.trap
s_trap 0x02
SGPR0-1
:
queue_ptr
none
导致波前停止,PC 指向陷阱指令。 关联的队列会发出信号,使其进入错误状态。 当队列进入错误状态时,在该队列上执行调度的波前将被终止。
llvm.debugtrap
s_trap 0x03
none
none
如果未启用调试器,则行为类似于空操作。 进入陷阱处理程序并立即返回以继续执行波前。
如果启用了调试器,则会导致调试器报告 debug trap,并且波前进入暂停状态,PC 指向该指令。 调试器必须增加 PC 并恢复波前。
保留
s_trap 0x04
保留。
保留
s_trap 0x05
保留。
保留
s_trap 0x06
保留。
保留
s_trap 0x07
保留。
保留
s_trap 0x08
保留。
保留
s_trap 0xfe
保留。
保留
s_trap 0xff
保留。
调用约定¶
注意
本节目前尚不完整,并且存在不准确之处。 这是一个 WIP,将在确定信息后更新。
有关交错地址的信息,请参见 地址空间标识符。 非交错地址是正常的线性地址。
内核函数¶
本节介绍外部内核函数的调用约定 ABI。
有关内核调用约定,请参见 初始内核执行状态。
以下内容不是 AMDGPU 内核调用约定的一部分,而是描述 AMDGPU 如何实现函数调用
Clang 决定 kernarg 布局以匹配 HSA 程序员语言参考 [HSA]。
所有结构体都直接传递。
Lambda 值以 TBA 方式传递。
内核在其序言中执行某些设置,如 内核序言 中所述。
非内核函数¶
本节介绍除外部内核函数之外的函数的调用约定 ABI。
如果内核具有函数调用,则始终分配 scratch 并将其用于调用堆栈,该堆栈使用交错 scratch 地址空间从低地址增长到高地址。
进入函数时
SGPR0-3 包含具有以下属性的 V#(参见 私有段缓冲区)
指向波前 scratch 后备内存开头的基地址。
以 dword 元素大小和波前大小元素的步幅交错。
已设置 FLAT_SCRATCH 寄存器对。 参见 Flat Scratch。
GFX6-GFX8:M0 寄存器设置为 LDS 的大小(以字节为单位)。 参见 M0。
EXEC 寄存器设置为进入函数时处于活动状态的 lane。
MODE 寄存器:待定
VGPR0-31 和 SGPR4-29 用于传递函数输入参数,如下所述。
SGPR30-31 返回地址 (RA)。 函数完成时必须返回的代码地址。 如果函数为无返回,则该值未定义。
SGPR32 用于堆栈指针 (SP)。 它是相对于波前 scratch 后备内存开头的非交错 scratch 偏移量。
非交错 SP 可以与缓冲区指令一起使用,作为非交错 SGPR 偏移量,并使用 SGPR0-3 中的 scratch V# 以交错方式访问堆栈。
非交错 SP 值可以通过以下方式转换为交错 SP 值
交错 SP = 非交错 SP / 波前大小这可以用于获取堆栈对象的私有地址空间地址,并通过添加 flat scratch 光圈基地址将此地址转换为 flat 地址。
对于
r600
架构,交错 SP 值始终为 4 字节对齐,对于amdgcn
架构,交错 SP 值始终为 16 字节对齐。注意
选择
amdgcn
值是为了避免 OpenCL 语言的动态堆栈对齐,OpenCL 语言的最大基本类型定义为 16 字节。进入时,交错 SP 值是在堆栈上传递的第一个函数参数的地址。 其他堆栈传递的参数是从入口交错 SP 值的正偏移量。
该函数可以使用超出最后一个堆栈传递参数的正偏移量,用于堆栈分配的局部变量和寄存器溢出槽。 如果必要,该函数可以将这些对齐到大于 16 字节的对齐方式。 在这些之后,该函数可以动态分配空间,用于运行时大小的
alloca
局部分配等。如果函数调用另一个函数,它会将任何堆栈分配的参数放在最后一个局部分配之后,并将 SGPR32 调整为最后一个局部分配之后的地址。
所有其他寄存器均未指定。
已执行任何必要的
s_waitcnt
以确保函数可以使用内存。在 C ABI 中,对于结构体参数,使用按引用传递 (byref) 而不是按值传递 (byval)。 被调用者负责分配堆栈内存并在修改时复制结构体的值。 请注意,后端仍然支持结构体参数的 byval。
从函数退出时
VGPR0-31 和 SGPR4-29 用于传递函数结果参数,如下所述。 使用的任何寄存器都被视为被破坏的寄存器。
以下寄存器被保留,并且具有与进入时相同的值
FLAT_SCRATCH
EXEC
GFX6-GFX8:M0
除 SGPR4-31 的被破坏寄存器之外的所有 SGPR 寄存器。
VGPR40-47
VGPR56-63
VGPR72-79
VGPR88-95
VGPR104-111
VGPR120-127
VGPR136-143
VGPR152-159
VGPR168-175
VGPR184-191
VGPR200-207
VGPR216-223
VGPR232-239
VGPR248-255
注意
除了参数寄存器外,VGPR 被破坏寄存器和保留寄存器以规则的间隔混合,以便保持相似的比率,而与分配的 VGPR 数量无关。
GFX90A:除 AGPR0-31 的被破坏寄存器之外的所有 AGPR 寄存器。
在调用站点不活动的所有 VGPR 的 lane。
对于 AMDGPU 后端,如果可以确定被调用的函数未更改某些被破坏的 SGPR 和 VGPR 寄存器的值,则过程间寄存器分配 (IPRA) 优化可能会将它们标记为保留。
PC 设置为进入时提供的 RA。
MODE 寄存器:待定。
所有其他寄存器都被破坏。
已执行任何必要的
s_waitcnt
以确保调用方可以使用函数访问的内存。
函数输入参数由源语言函数显式声明的形式参数以及实现使用的隐式输入参数组成。
源语言输入参数是
任何源语言隐式
this
或self
参数首先作为指针类型出现。后跟函数形式参数,按从左到右的源顺序排列。
源语言结果参数是
函数结果参数。
小于或等于 16 字节的源语言输入或结果结构体类型参数被递归分解为其基本类型字段,并且每个字段都像单独的参数一样传递。 对于输入参数,如果被调用函数要求结构体在内存中(例如,因为它的地址被获取),则函数体负责分配堆栈位置并将字段参数复制到其中。 Clang 将此称为直接结构体。
大于 16 字节的源语言输入结构体类型参数按引用传递。 调用者负责分配堆栈位置以创建结构体值的副本,并将地址作为输入参数传递。 被调用函数负责在访问输入参数时执行解引用。 Clang 将此称为按值结构体。
大于 16 字节的源语言结果结构体类型参数按引用返回。 调用者负责分配堆栈位置以保存结果值,并将地址作为最后一个输入参数(在隐式输入参数之前)传递。 在这种情况下,没有结果参数。 被调用函数负责在存储结果值时执行解引用。 Clang 将此称为结构化返回 (sret)。
待办事项:更正 ``sret`` 定义。
Lambda 参数类型被视为结构体类型,具有实现定义的字段集。
对于 AMDGPU 后端,所有源语言参数(包括分解的结构体类型参数)都在 VGPR 中传递,除非标记为 inreg
,在这种情况下,它们在 SGPR 中传递。
AMDGPU 后端从叶节点开始遍历函数调用图,以确定使用了哪些隐式输入参数,并传播到函数的每个调用者。 使用的隐式参数按以下顺序附加到函数参数中,在源语言参数之后
工作项 ID (1 VGPR)
X、Y 和 Z 工作项 ID 被打包到单个 VGRP 中,布局如下。 仅设置函数实际使用的字段。 其他位未定义。
这些值来自初始内核执行状态。 参见 初始内核执行状态。
表 88 工作项隐式参数布局¶ 位
大小
字段名称
9:0
10 位
X 工作项 ID
19:10
10 位
Y 工作项 ID
29:20
10 位
Z 工作项 ID
31:30
2 位
未使用
调度程序指针 (2 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
队列指针 (2 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
Kernarg 段指针 (2 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
调度 ID (2 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
工作组 ID X (1 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
工作组 ID Y (1 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
工作组 ID Z (1 SGPR)
该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序。
隐式参数指针 (2 SGPR)
该值通过将偏移量添加到 Kernarg 段指针来计算,以获得指向第一个 kernarg 隐式参数的全局地址空间指针。
输入和结果参数按以下方式按顺序分配
注意
以下描述可能存在一些错误和遗漏,需要更正。
VGPR 参数分配给从 VGPR0 开始到 VGPR31 的连续 VGPR。
如果参数多于这些寄存器可以容纳的数量,则剩余的参数按顺序在堆栈上自然对齐的地址上分配。
SGPR 参数分配给从 SGPR0 开始到 SGPR29 的连续 SGPR。
如果参数多于这些寄存器可以容纳的数量,则剩余的参数按顺序在堆栈上自然对齐的地址上分配。
请注意,分解的结构体类型参数可能有一些字段在寄存器中传递,而另一些字段在内存中传递。
以下内容不是 AMDGPU 函数调用约定的一部分,而是描述 AMDGPU 如何实现函数调用
如果需要,SGPR33 用作帧指针 (FP)。 与 SP 类似,它是一个非交错 scratch 地址。 仅当使用运行时大小的
alloca
或出于SIFrameLowering
中定义的原因时才需要它。支持运行时堆栈对齐。 SGPR34 用作基指针 (BP) 以访问函数中的传入堆栈参数。 仅当函数需要运行时堆栈对齐时,才需要 BP。
不支持在堆栈上分配 SGPR 参数。
当前未生成 CFI。 参见 A.6.4 调用帧信息。
注意
将生成 CFI,其将 CFA 定义为相对于最低地址堆栈分配局部变量的非交错私有地址空间中 wave scratch 基地址的非交错地址。
DW_AT_frame_base
将定义为交错私有地址空间中的交错地址,方法是将 CFA 除以波前大小(因为 CFA 始终至少为 dword 对齐,这与 scratch 交错元素大小匹配)。如果未执行动态堆栈对齐,则堆栈分配的参数作为相对于
DW_AT_frame_base
的负偏移量访问,局部变量和寄存器溢出槽作为相对于DW_AT_frame_base
的正偏移量访问。函数参数传递通过在进入时将输入物理寄存器复制到虚拟寄存器来实现。 如果必要,寄存器分配器可以溢出。 这些在调用站点复制回物理寄存器。 最终效果是,每个函数调用都可以在完全不同的位置具有这些值。 IPRA 可以帮助避免改组参数寄存器。
调用站点通过在相对于 SP 的正偏移量处设置参数来实现。 然后,在调用之前增加 SP 以考虑已知的帧大小,并在调用之后减小 SP。
注意
CFI 将反映从 SP 计算 CFA 所需的更改后的计算。
堆栈帧中使用 4 字节溢出槽。 为紧急溢出槽分配一个槽。 缓冲区指令用于堆栈访问,而不是
flat_scratch
指令。
AMDPAL¶
本节提供当目标三元组 OS 为 amdpal
时使用的代码约定(参见 目标三元组)。
代码对象元数据¶
注意
元数据目前正在开发中,可能会发生重大更改。 仅支持当前版本。 生成本文档时,版本为 2.6。
代码对象元数据由 NT_AMDGPU_METADATA
注释记录指定(参见 代码对象 V3 及更高版本注释记录)。
元数据表示为 Message Pack 格式化的二进制数据(参见 [MsgPack])。 顶层是一个 Message Pack 映射,其中包括表 AMDPAL 代码对象元数据映射 和引用的表中定义的键。
可以将其他信息添加到映射中。为了避免冲突,任何键名都应以“vendor-name.”为前缀,其中 vendor-name
可以是供应商的名称和生成信息的特定供应商工具。当出现在由同一 vendor-name 添加的映射中时,前缀缩写为简单的“.”。
表 89 AMDPAL 代码对象元数据映射¶ 字符串键
值类型
必需?
描述
“amdpal.version”
2 个整数的序列
必需
PAL 代码对象元数据(主版本号,次版本号)版本。 当前值由 Util::Abi::PipelineMetadata(Major|Minor)Version 定义。
“amdpal.pipelines”
映射序列
必需
每个管道的元数据。 有关该映射中包含的键的定义,请参见 AMDPAL 代码对象管道元数据映射。
表 90 AMDPAL 代码对象管道元数据映射¶ 字符串键
值类型
必需?
描述
“.name”
字符串
管道的源名称。
“.type”
字符串
管道类型,例如 VsPs。 值包括
“VsPs”
“Gs”
“Cs”
“Ngg”
“Tess”
“GsTess”
“NggTess”
“.internal_pipeline_hash”
2 个整数的序列
必需
此管道的内部编译器哈希。 低 64 位是哈希的“稳定”部分,用于例如着色器替换查找。 高 64 位是哈希的“唯一”部分,用于例如管道缓存查找。 该值是实现定义的,不能在编译器的不同构建之间依赖。
“.shaders”
映射
每个 API 的着色器元数据。 有关该映射中包含的键的定义,请参见 AMDPAL 代码对象着色器映射。
“.hardware_stages”
映射
每个硬件阶段的元数据。 有关该映射中包含的键的定义,请参见 AMDPAL 代码对象硬件阶段映射。
“.shader_functions”
映射
每个着色器函数的元数据。 有关该映射中包含的键的定义,请参见 AMDPAL 代码对象着色器函数映射。
“.registers”
映射
必需
硬件寄存器配置。 有关该映射中包含的键的定义,请参阅 AMDPAL 代码对象寄存器映射。
“.user_data_limit”
整数
此管道访问的用户数据条目数。
“.spill_threshold”
整数
用户数据溢出阈值。 0xFFFF 表示 NoUserDataSpilling。
“.uses_viewport_array_index”
布尔值
指示管道是否使用视口数组索引功能。 使用此功能的管道可以渲染到所有 16 个视口中,而不使用此功能的管道则仅限于视口 #0。
“.es_gs_lds_size”
整数
内部用于处理 ES 和 GS 着色器阶段之间数据传递的 LDS 空间大小(字节为单位)。 如果数据是通过片外缓冲区传递的,则此值可以为零。 此值应用于编程所有标记为“UserDataMapping::EsGsLdsSize”的用户 SGPR(通常只有 GS 和 VS HW 阶段会具有如此标记的用户 SGPR)。
“.nggSubgroupSize”
整数
NGG 着色器的显式最大子组大小(子组中的最大线程数)。
“.num_interpolants”
整数
仅限图形。 PS 插值器数量。
“.mesh_scratch_memory_size”
整数
使用的最大网格着色器暂存内存。
“.api”
字符串
客户端图形 API 的名称。
“.api_create_info”
二进制
图形 API 着色器创建信息二进制 blob。 如果驱动程序希望能够在以后关联创建期间使用的特定于 API 的信息,则可以使用编译器定义。
表 91 AMDPAL 代码对象着色器映射¶ 字符串键
值类型
描述
“.compute”
“.vertex”
“.hull”
“.domain”
“.geometry”
“.pixel”
映射
有关该映射中包含的键的定义,请参阅 AMDPAL 代码对象 API 着色器元数据映射。
表 92 AMDPAL 代码对象 API 着色器元数据映射¶ 字符串键
值类型
必需?
描述
“.api_shader_hash”
2 个整数的序列
必需
输入着色器哈希,通常从客户端传入。 该值是实现定义的,不能在编译器的不同构建之间依赖。
“.hardware_mapping”
字符串序列
必需
指示此 API 着色器映射到的硬件阶段的标志。 值包括
“.ls”
“.hs”
“.es”
“.gs”
“.vs”
“.ps”
“.cs”
表 93 AMDPAL 代码对象硬件阶段映射¶ 字符串键
值类型
描述
“.ls”
“.hs”
“.es”
“.gs”
“.vs”
“.ps”
“.cs”
映射
有关该映射中包含的键的定义,请参阅 AMDPAL 代码对象硬件阶段元数据映射。
表 94 AMDPAL 代码对象硬件阶段元数据映射¶ 字符串键
值类型
必需?
描述
“.entry_point”
字符串
指向此管道阶段入口点的 ELF 符号。
“.scratch_memory_size”
整数
暂存内存大小(字节为单位)。
“.lds_size”
整数
本地数据共享大小(字节为单位)。
“.perf_data_buffer_size”
整数
性能数据缓冲区大小(字节为单位)。
“.vgpr_count”
整数
使用的 VGPR 数量。
“.agpr_count”
整数
使用的 AGPR 数量。
“.sgpr_count”
整数
使用的 SGPR 数量。
“.vgpr_limit”
整数
如果非零,则表示着色器在编译时带有指令,指示编译器将 VGPR 使用量限制为小于或等于指定值(仅当与硬件默认值不同时才设置)。
“.sgpr_limit”
整数
SGPR 计数上限(仅当与硬件默认值不同时才设置)。
“.threadgroup_dimensions”
3 个整数的序列
线程组 X/Y/Z 维度(仅限计算)。
“.wavefront_size”
整数
波前大小(仅当与硬件默认值不同时才设置)。
“.uses_uavs”
布尔值
着色器读取或写入 UAV。
“.uses_rovs”
布尔值
着色器读取或写入 ROV。
“.writes_uavs”
布尔值
着色器写入一个或多个 UAV。
“.writes_depth”
布尔值
着色器写出深度值。
“.uses_append_consume”
布尔值
着色器使用追加和/或消耗操作,无论是内存还是 GDS。
“.uses_prim_id”
布尔值
着色器使用 PrimID。
表 95 AMDPAL 代码对象着色器函数映射¶ 字符串键
值类型
描述
符号名称
映射
符号名称是着色器函数代码入口地址的 ELF 符号名称。 该值是函数的元数据。 请参阅 AMDPAL 代码对象着色器函数元数据映射。
表 96 AMDPAL 代码对象着色器函数元数据映射¶ 字符串键
值类型
描述
“.api_shader_hash”
2 个整数的序列
输入着色器哈希,通常从客户端传入。 该值是实现定义的,不能在编译器的不同构建之间依赖。
“.scratch_memory_size”
整数
着色器使用的暂存内存大小(字节为单位)。
“.lds_size”
整数
LDS 内存大小(字节为单位)。
“.vgpr_count”
整数
着色器使用的 VGPR 数量。
“.sgpr_count”
整数
着色器使用的 SGPR 数量。
“.stack_frame_size_in_bytes”
整数
着色器使用的堆栈大小量。
“.shader_subtype”
字符串
着色器子类型/种类。 值包括
“未知”
表 97 AMDPAL 代码对象寄存器映射¶ 32 位整数键
值类型
描述
reg offset
32 位整数
reg offset
是 GRBM 寄存器(即驱动程序可访问的 GPU 寄存器号,而不是着色器 GPR 寄存器号)的 GFXIP 寄存器空间中的 dword 偏移量。 驱动程序在执行此管道时,需要将每个指定的寄存器编程为相应的指定值。 通常,reg offsets
是硬件芯片标头定义的每个寄存器的uint16_t
偏移量。 寄存器设置为提供的值。 但是,指定用户数据寄存器(例如,COMPUTE_USER_DATA_0)的reg offset
需要特殊处理。 有关更多信息,请参阅 用户数据 部分。
用户数据¶
每个硬件阶段都有一组 32 位物理 SPI 用户数据寄存器(基于图形 IP 和阶段,可以是 16 或 32 个),这些寄存器可以从命令缓冲区写入,然后在通过后续调度或绘制操作启动波时加载到 SGPR 中。 这是大多数参数从应用程序/运行时传递到硬件着色器的方式。
PAL 通过公开每个管道 128 个用户数据条目来抽象此功能,客户端可以使用这些条目将参数从命令缓冲区传递到一个或多个着色器。 ELF 代码对象必须指定从虚拟化用户数据条目到物理用户数据寄存器的映射,PAL 负责实现该映射,包括在需要时将溢出用户数据条目溢出到内存。
由于用户数据寄存器是 GRBM 可访问的 SPI 寄存器,因此此映射实际上嵌入在 .registers
元数据条目中。 对于大多数寄存器,该映射中的值应为字面 32 位值,驱动程序应将其写入寄存器。 但是,当寄存器是用户数据寄存器(任何 USER_DATA 寄存器,例如 SPI_SHADER_USER_DATA_PS_5)时,该值是一个编码,告诉驱动程序将用户数据条目值或几个驱动程序内部值之一写入寄存器。 此编码在下表中描述
注意
目前,用户数据寄存器 0 和 1(例如,SPI_SHADER_USER_DATA_PS_0 和 SPI_SHADER_USER_DATA_PS_1)是保留的。 用户数据寄存器 0 必须始终编程为 GlobalTable 的地址,用户数据寄存器 1 必须始终编程为 PerShaderTable 的地址。
表 98 AMDPAL 用户数据映射¶ 值
名称
描述
0..127
用户数据条目
通过 CmdSetUserData() 指定的 user_data_entry[N] 的 32 位值
0x10000000
GlobalTable
指向 GPU 内存的 32 位指针,该内存包含全局内部表(应始终指向用户数据寄存器 0)。
0x10000001
PerShaderTable
指向 GPU 内存的 32 位指针,该内存包含每个着色器内部表。 有关更多详细信息,请参阅 每个着色器表(应始终指向用户数据寄存器 1)。
0x10000002
SpillTable
指向 GPU 内存的 32 位指针,该内存包含用户数据溢出表。 有关更多详细信息,请参阅 溢出表。
0x10000003
BaseVertex
顶点偏移量(32 位无符号整数)。 如果管道在顶点着色器中不引用绘制索引,则不需要。 仅图形管道中的第一个阶段支持。
0x10000004
BaseInstance
实例偏移量(32 位无符号整数)。 仅图形管道中的第一个阶段支持。
0x10000005
DrawIndex
绘制索引(32 位无符号整数)。 仅图形管道中的第一个阶段支持。
0x10000006
Workgroup
线程组计数(32 位无符号整数)。 包含计算调度操作的网格维度缓冲区的 64 位地址的低半部分。 地址的高半部分存储在下一个顺序用户 SGPR 中。 仅计算管道支持。
0x1000000A
EsGsLdsSize
指示 PAL 将编程此用户 SGPR 以包含用于 ES/GS 伪环形缓冲区(用于在着色器阶段之间传递数据)的 LDS 空间量。
0x1000000B
ViewId
视图 ID(32 位无符号整数)标识图形管道实例的视图。
0x1000000C
StreamOutTable
指向 GPU 内存的 32 位指针,该内存包含流输出目标 SRD 表。 每个管道只能为一个着色器阶段出现此项。
0x1000000D
PerShaderPerfData
指向 GPU 内存的 32 位指针,该内存包含每个着色器性能数据缓冲区。
0x1000000F
VertexBufferTable
指向 GPU 内存的 32 位指针,该内存包含顶点缓冲区 SRD 表。 每个管道只能为一个着色器阶段出现此项。
0x10000010
UavExportTable
指向 GPU 内存的 32 位指针,该内存包含 UAV 导出 SRD 表。 每个管道只能为一个着色器阶段(PS)出现此项。 这些取代了颜色目标,并且与着色器使用的任何 UAV 完全分离。 这是可选的,仅当使用 UAV 导出取代颜色目标导出以优化特定着色器时,PS 才使用它。
0x10000011
NggCullingData
指向 GPU 内存的 64 位指针,该内存包含某些 NGG 管道执行剔除所需的硬件寄存器数据。 此值包含提供完整 GPU 地址的两个连续寄存器中的第一个寄存器的地址。
0x10000015
FetchShaderPtr
指向 GPU 内存的 64 位指针,该内存包含获取着色器子例程。
每个着色器表¶
ELF 的 .data
部分中可选缓冲区的 GPU 地址的低 32 位。 地址的高 32 位与着色器程序计数器的高 32 位匹配。
缓冲区可以是着色器编译器需要的任何内容,并允许每个着色器拥有其自己的 .data
部分区域。 通常,这可以是缓冲区 SRD 表和缓冲区 SRD 指向的数据,但也可能是平坦地址内存区域。 其布局和用法由着色器编译器定义。
每个着色器在 .data
部分中的表都由符号 _amdgpu_
xs_shdr_intrl_data
引用,其中 xs 与数据所属的硬件着色器阶段相对应。 例如,计算着色器硬件阶段的 _amdgpu_cs_shdr_intrl_data
。
溢出表¶
硬件着色器可能需要访问比一个或多个硬件着色器阶段的用户数据寄存器中可用槽位更多的用户数据条目。 在这种情况下,PAL 运行时期望将必要的用户数据条目溢出到 GPU 内存,并使用一个用户数据寄存器指向溢出的用户数据内存。 然后,用户数据条目的值必须表示着色器期望读取表的 GPU 虚拟地址的低 32 位的位置。 溢出表本身表示 PAL 运行时在 GPU 可访问内存中管理的一组 32 位值,这些值可以间接供硬件着色器访问。
未指定的操作系统¶
本节提供当目标三元组操作系统为空时使用的代码约定(请参阅 目标三元组)。
陷阱处理程序 ABI¶
对于 AMDGPU 后端为非 amdhsa 操作系统生成的代码对象,运行时不安装陷阱处理程序。 llvm.trap
和 llvm.debugtrap
指令的处理方式如下
表 99 非 AMDHSA 操作系统的 AMDGPU 陷阱处理程序¶ 用法
代码序列
描述
llvm.trap
s_endpgm
导致波前终止。
llvm.debugtrap
none
编译器发出警告,指出未安装陷阱处理程序。
核心文件格式¶
本节介绍支持 AMDGPU 的核心文件格式。 AMDGPU 程序的 Core dump 可以分为 2 种类型:拆分或统一核心文件。
拆分布局由一个主机核心文件组成,其中包含重建主机进程映像的信息,以及一个 AMDGPU 核心文件,其中包含进程中使用的 AMDGPU 代理的信息。 AMDGPU 核心文件包括
统一核心文件是拆分布局的两个文件中包含的所有信息的并集(所有注释和加载段)。 它包含重建跨所有代理的进程映像所需的所有信息。
核心文件头¶
AMDGPU 核心文件是 ELF64
核心文件。 统一核心文件布局和 AMDGPU 核心文件布局中,标头的内容有所不同。
拆分文件¶
在拆分文件布局中,AMDGPU 核心文件是一个 ELF64
文件,其标头配置如 AMDGPU 核心文件标头 中所述
表 100 AMDGPU 核心文件标头¶ 字段
值
e_ident[EI_CLASS]
ELFCLASS64
(0x2
)
e_ident[EI_DATA]
ELFDATA2LSB
(0x1
)
e_ident[EI_OSABI]
ELFOSABI_AMDGPU_HSA
(0x40
)
e_type
ET_CORE``(``0x4
)
e_ident[EI_ABIVERSION]
ELFABIVERSION_AMDGPU_HSA_5
e_machine
EM_AMDGPU
(0xe0
)
统一文件¶
在统一核心文件模式下,ELF64
标头设置为描述主机架构和进程。
核心文件注释¶
AMDGPU 核心文件必须在 PT_NOTE
段中包含一个快照注释。 当使用拆分核心文件布局时,此注释位于 AMDGPU 文件中。
注释记录供应商字段为“AMDGPU
”,记录类型为“NT_AMDGPU_KFD_CORE_STATE
”(请参阅 代码对象 V3 及更高版本的注释记录)
注释的内容在表 AMDGPU 快照注释格式 V1 中定义
表 101 AMDGPU 快照注释格式 V1¶ 字段
类型
大小(字节)
字节对齐
注释
version_major
uint32
4
4
KFD_IOCTL_MAJOR_VERSION
version_minor
uint32
4
4
KFD_IOCTL_MINOR_VERSION
runtime_info_size
uint64
8
8
必须是 8 的倍数
n_agents
uint32
4
8
agent_info_entry_size
uint32
4
4
必须是 8 的倍数
n_queues
uint32
4
8
queue_info_entry_size
uint32
4
4
必须是 8 的倍数
runtime_info
kfd_runtime_info
runtime_info_size
8
agents_info
kfd_dbg_device_info_entry[n_agents]
n_agents * agent_info_entry_size
8
queues_info
kfd_queue_snapshot_entry[n_queues]
n_queues * queue_info_entry_size
8
所有 kfd_*
类型的定义都来自 KFD 存储库中的 include/uapi/linux/kfd_ioctl.h
头文件。 它通常安装在 /usr/include/linux/kfd_ioctl.h
中。 使用的 kfd_ioctl.h
文件的版本必须为 KFD_IOCTL_MAJOR_VERSION
和 KFD_IOCTL_MINOR_VERSION
定义值,这些值与注释中的 kfd_version_major
和 kfd_version_major
的值匹配。
内存段¶
AMDGPU 核心文件必须在加载段(类型为 PT_LOAD
)中包含 AMDGPU 代理内存的映像。 这些段必须与代理内存内容映射到主机进程的内存区域相对应(请注意,这些内存映射通常进程本身不可读)。
当使用拆分核心文件布局时,这些段必须包含在 AMDGPU 核心文件中。
源语言¶
OpenCL¶
当语言为 OpenCL 时,会发生以下差异
使用 OpenCL 内存模型(请参阅 内存模型)。
AMDGPU 后端为 AMDHSA 操作系统内核的显式参数附加了其他参数(请参阅 为 AMDHSA 操作系统附加的 OpenCL 内核隐式参数)。
生成其他元数据(请参阅 代码对象元数据)。
表 102 为 AMDHSA 操作系统附加的 OpenCL 内核隐式参数¶ 位置
字节大小
字节对齐
描述
1
8
8
OpenCL 全局偏移量 X
2
8
8
OpenCL 全局偏移量 Y
3
8
8
OpenCL 全局偏移量 Z
4
8
8
printf 缓冲区的 OpenCL 地址
5
8
8
enqueue_kernel 使用的虚拟队列的 OpenCL 地址。
6
8
8
enqueue_kernel 使用的 AqlWrap 结构的 OpenCL 地址。
7
8
8
用于多网格同步的指针参数。
HCC¶
当语言为 HCC 时,会发生以下差异
使用 HSA 内存模型(请参阅 内存模型)。
汇编器¶
AMDGPU 后端具有基于 LLVM-MC 的汇编器,目前正在开发中。 它支持 AMDGCN GFX6-GFX11。
本节介绍指令和操作数的一般语法。
指令¶
指令具有以下 语法
<
opcode> <
operand0>, <
operand1>,... <
modifier0> <
modifier1>...
操作数和修饰符的顺序是固定的。 大多数修饰符是可选的,可以省略。
详细指令语法描述的链接可以在下表中找到。 请注意,开发中的功能不包含在此描述中。
架构
核心 ISA
ISA 变体和扩展
GCN 2
-
GCN 3, GCN 4
-
GCN 5
CDNA 1
CDNA 2
CDNA 3
RDNA 1
RDNA 2
RDNA 3
有关指令、其语义以及操作数的支持组合的更多信息,请参阅指令集架构手册 [AMD-GCN-GFX6]、 [AMD-GCN-GFX7]、 [AMD-GCN-GFX8]、 [AMD-GCN-GFX900-GFX904-VEGA]、 [AMD-GCN-GFX906-VEGA7NM]、 [AMD-GCN-GFX908-CDNA1]、 [AMD-GCN-GFX90A-CDNA2]、 [AMD-GCN-GFX942-CDNA3]、 [AMD-GCN-GFX10-RDNA1]、 [AMD-GCN-GFX10-RDNA2]、 [AMD-GCN-GFX11-RDNA3] 和 [AMD-GCN-GFX11-RDNA3.5]。
操作数¶
操作数的详细描述可以在 此处 找到。
修饰符¶
修饰符的详细描述可以在 此处 找到。
指令示例¶
DS¶
ds_add_u32 v2, v4 offset:16
ds_write_src2_b64 v2 offset0:4 offset1:8
ds_cmpst_f32 v2, v4, v6
ds_min_rtn_f64 v[8:9], v2, v[4:5]
有关支持的指令的完整列表,请参阅 ISA 手册中的“LDS/GDS 指令”。
FLAT¶
flat_load_dword v1, v[3:4]
flat_store_dwordx3 v[3:4], v[5:7]
flat_atomic_swap v1, v[3:4], v5 glc
flat_atomic_cmpswap v1, v[3:4], v[5:6] glc slc
flat_atomic_fmax_x2 v[1:2], v[3:4], v[5:6] glc
有关支持的指令的完整列表,请参阅 ISA 手册中的“FLAT 指令”。
MUBUF¶
buffer_load_dword v1, off, s[4:7], s1
buffer_store_dwordx4 v[1:4], v2, ttmp[4:7], s1 offen offset:4 glc tfe
buffer_store_format_xy v[1:2], off, s[4:7], s1
buffer_wbinvl1
buffer_atomic_inc v1, v2, s[8:11], s4 idxen offset:4 slc
有关支持的指令的完整列表,请参阅 ISA 手册中的“MUBUF 指令”。
SMRD/SMEM¶
s_load_dword s1, s[2:3], 0xfc
s_load_dwordx8 s[8:15], s[2:3], s4
s_load_dwordx16 s[88:103], s[2:3], s4
s_dcache_inv_vol
s_memtime s[4:5]
有关支持的指令的完整列表,请参阅 ISA 手册中的“标量内存操作”。
SOP1¶
s_mov_b32 s1, s2
s_mov_b64 s[0:1], 0x80000000
s_cmov_b32 s1, 200
s_wqm_b64 s[2:3], s[4:5]
s_bcnt0_i32_b64 s1, s[2:3]
s_swappc_b64 s[2:3], s[4:5]
s_cbranch_join s[4:5]
有关支持的指令的完整列表,请参阅 ISA 手册中的“SOP1 指令”。
SOP2¶
s_add_u32 s1, s2, s3
s_and_b64 s[2:3], s[4:5], s[6:7]
s_cselect_b32 s1, s2, s3
s_andn2_b32 s2, s4, s6
s_lshr_b64 s[2:3], s[4:5], s6
s_ashr_i32 s2, s4, s6
s_bfm_b64 s[2:3], s4, s6
s_bfe_i64 s[2:3], s[4:5], s6
s_cbranch_g_fork s[4:5], s[6:7]
有关支持的指令的完整列表,请参阅 ISA 手册中的“SOP2 指令”。
SOPC¶
s_cmp_eq_i32 s1, s2
s_bitcmp1_b32 s1, s2
s_bitcmp0_b64 s[2:3], s4
s_setvskip s3, s5
有关支持的指令的完整列表,请参阅 ISA 手册中的“SOPC 指令”。
SOPP¶
s_barrier
s_nop 2
s_endpgm
s_waitcnt 0 ; Wait for all counters to be 0
s_waitcnt vmcnt(0) & expcnt(0) & lgkmcnt(0) ; Equivalent to above
s_waitcnt vmcnt(1) ; Wait for vmcnt counter to be 1.
s_sethalt 9
s_sleep 10
s_sendmsg 0x1
s_sendmsg sendmsg(MSG_INTERRUPT)
s_trap 1
有关支持的指令的完整列表,请参阅 ISA 手册中的“SOPP 指令”。
除非另有说明,否则对 SOPP 指令的操作数执行的验证很少,因此程序员有责任熟悉范围或可接受的值。
VALU¶
对于向量 ALU 指令操作码(VOP1、VOP2、VOP3、VOPC、VOP_DPP、VOP_SDWA),汇编器将根据其操作数自动使用最佳编码。 要强制使用特定编码,可以向指令的操作码添加后缀
_e32 用于 32 位 VOP1/VOP2/VOPC
_e64 用于 64 位 VOP3
_dpp 用于 VOP_DPP
_e64_dpp 用于带 DPP 的 VOP3
_sdwa 用于 VOP_SDWA
VOP1/VOP2/VOP3/VOPC 示例
v_mov_b32 v1, v2
v_mov_b32_e32 v1, v2
v_nop
v_cvt_f64_i32_e32 v[1:2], v2
v_floor_f32_e32 v1, v2
v_bfrev_b32_e32 v1, v2
v_add_f32_e32 v1, v2, v3
v_mul_i32_i24_e64 v1, v2, 3
v_mul_i32_i24_e32 v1, -3, v3
v_mul_i32_i24_e32 v1, -100, v3
v_addc_u32 v1, s[0:1], v2, v3, s[2:3]
v_max_f16_e32 v1, v2, v3
VOP_DPP 示例
v_mov_b32 v0, v0 quad_perm:[0,2,1,1]
v_sin_f32 v0, v0 row_shl:1 row_mask:0xa bank_mask:0x1 bound_ctrl:0
v_mov_b32 v0, v0 wave_shl:1
v_mov_b32 v0, v0 row_mirror
v_mov_b32 v0, v0 row_bcast:31
v_mov_b32 v0, v0 quad_perm:[1,3,0,1] row_mask:0xa bank_mask:0x1 bound_ctrl:0
v_add_f32 v0, v0, |v0| row_shl:1 row_mask:0xa bank_mask:0x1 bound_ctrl:0
v_max_f16 v1, v2, v3 row_shl:1 row_mask:0xa bank_mask:0x1 bound_ctrl:0
VOP3_DPP 示例(在 GFX11+ 上可用)
v_add_f32_e64_dpp v0, v1, v2 dpp8:[0,1,2,3,4,5,6,7]
v_sqrt_f32_e64_dpp v0, v1 row_shl:1 row_mask:0xa bank_mask:0x1 bound_ctrl:0
v_ldexp_f32 v0, v1, v2 dpp8:[0,1,2,3,4,5,6,7]
VOP_SDWA 示例
v_mov_b32 v1, v2 dst_sel:BYTE_0 dst_unused:UNUSED_PRESERVE src0_sel:DWORD
v_min_u32 v200, v200, v1 dst_sel:WORD_1 dst_unused:UNUSED_PAD src0_sel:BYTE_1 src1_sel:DWORD
v_sin_f32 v0, v0 dst_unused:UNUSED_PAD src0_sel:WORD_1
v_fract_f32 v0, |v0| dst_sel:DWORD dst_unused:UNUSED_PAD src0_sel:WORD_1
v_cmpx_le_u32 vcc, v1, v2 src0_sel:BYTE_2 src1_sel:WORD_0
有关支持的指令的完整列表,请参阅“向量 ALU 指令”。
代码对象 V2 预定义符号¶
警告
此版本的 LLVM 不再支持代码对象 V2 的生成。
AMDGPU 汇编器自动定义和更新一些符号。 这些符号不影响代码生成。
.option.machine_version_major¶
设置为正在汇编的目标的 GFX 主要代号。 例如,当为“GFX9”目标汇编时,这将设置为整数值“9”。 可能的 GFX 主要代号在 处理器 中介绍。
.option.machine_version_minor¶
设置为正在汇编的目标的 GFX 次要代号。 例如,当为“GFX810”目标汇编时,这将设置为整数值“1”。 可能的 GFX 次要代号在 处理器 中介绍。
.option.machine_version_stepping¶
设置为正在汇编的目标的 GFX 步进代号。 例如,当为“GFX704”目标汇编时,这将设置为整数值“4”。 可能的 GFX 步进代号在 处理器 中介绍。
.kernel.vgpr_count¶
每次遇到 .amdgpu_hsa_kernel (name) 指令时,都设置为零。 在每个指令处,如果此符号的当前值小于或等于在该指令中显式引用的最大 VGPR 编号,则符号值将更新为等于该 VGPR 编号加一。
.kernel.sgpr_count¶
每次遇到 .amdgpu_hsa_kernel (name) 指令时,都设置为零。 在每个指令处,如果此符号的当前值小于或等于在该指令中显式引用的最大 VGPR 编号,则符号值将更新为等于该 SGPR 编号加一。
代码对象 V2 指令¶
警告
此版本的 LLVM 不再支持代码对象 V2 的生成。
AMDGPU ABI 在输出代码对象中定义辅助数据。 在汇编源中,可以使用汇编器指令指定它们。
.hsa_code_object_version major, minor¶
major 和 minor 是整数,指定汇编器将生成的 HSA 代码对象的版本。
.hsa_code_object_isa [major, minor, stepping, vendor, arch]¶
major、minor 和 stepping 都是整数,描述汇编程序的指令集架构 (ISA) 版本。
vendor 和 arch 是带引号的字符串。 vendor 应始终等于“AMD”,arch 应始终等于“AMDGPU”。
默认情况下,汇编器将从传递给汇编器的 -mcpu 选项的值派生 ISA 版本、vendor 和 arch。
.amdgpu_hsa_kernel (name)¶
此指令指定具有给定名称的符号是内核入口点(标签),并且对象应包含类型为 STT_AMDGPU_HSA_KERNEL 的相应符号。
.amd_kernel_code_t¶
此指令标记用于指定汇编器将发出的 amd_kernel_code_t 对象的键/值对列表的开始。 该列表必须由 .end_amd_kernel_code_t 指令终止。 对于任何未指定的 amd_kernel_code_t 值,将使用默认值。 所有键的默认值为 0,以下情况除外
amd_code_version_major 默认为 1。
amd_kernel_code_version_minor 默认为 2。
amd_machine_kind 默认为 1。
amd_machine_version_major、machine_version_minor 和 amd_machine_version_stepping 从传递给汇编器的 -mcpu 选项的值派生。
kernel_code_entry_byte_offset 默认为 256。
wavefront_size 对于 GFX10 之前的目标,默认值为 6。对于 GFX10 及之后的版本,如果启用了目标特性
wavefrontsize64
,则默认值为 6,否则为 5。请注意,wavefront size 以 2 的幂指定,因此值 n 表示大小为 2^ n。call_convention 默认为 -1。
kernarg_segment_alignment、group_segment_alignment 和 private_segment_alignment 默认为 4。请注意,对齐方式以 2 的幂指定,因此值 n 表示对齐为 2^ n。
如果为 GFX90A 及更高版本启用了目标特性
tgsplit
,则 enable_tg_split 默认为 1。如果为 GFX10 及更高版本禁用了目标特性
cumode
,则 enable_wgp_mode 默认为 1。对于 GFX10 及更高版本,enable_mem_ordered 默认为 1。
.amd_kernel_code_t
指令必须紧跟在函数标签之后,并且在任何指令之前放置。
有关 amd_kernel_code_t 键的完整列表,请参阅 AMDGPU ABI 文档、lib/Target/AMDGPU/AmdKernelCodeT.h 中的注释以及 test/CodeGen/AMDGPU/hsa.s。
代码对象 V2 示例源代码¶
警告
此版本的 LLVM 不再支持代码对象 V2 的生成。
这是一个最小汇编源文件的示例,定义了一个 HSA 内核
1.hsa_code_object_version 1,0
2.hsa_code_object_isa
3
4.hsatext
5.globl hello_world
6.p2align 8
7.amdgpu_hsa_kernel hello_world
8
9hello_world:
10
11 .amd_kernel_code_t
12 enable_sgpr_kernarg_segment_ptr = 1
13 is_ptr64 = 1
14 compute_pgm_rsrc1_vgprs = 0
15 compute_pgm_rsrc1_sgprs = 0
16 compute_pgm_rsrc2_user_sgpr = 2
17 compute_pgm_rsrc1_wgp_mode = 0
18 compute_pgm_rsrc1_mem_ordered = 0
19 compute_pgm_rsrc1_fwd_progress = 1
20 .end_amd_kernel_code_t
21
22 s_load_dwordx2 s[0:1], s[0:1] 0x0
23 v_mov_b32 v0, 3.14159
24 s_waitcnt lgkmcnt(0)
25 v_mov_b32 v1, s0
26 v_mov_b32 v2, s1
27 flat_store_dword v[1:2], v0
28 s_endpgm
29.Lfunc_end0:
30 .size hello_world, .Lfunc_end0-hello_world
代码对象 V3 及更高版本的预定义符号¶
AMDGPU 汇编器自动定义和更新一些符号。 这些符号不影响代码生成。
.amdgcn.gfx_generation_number¶
设置为正在汇编的目标的 GFX 主要代号。 例如,当为“GFX9”目标汇编时,这将设置为整数值“9”。 可能的 GFX 主要代号在 处理器 中介绍。
.amdgcn.gfx_generation_minor¶
设置为正在汇编的目标的 GFX 次要代号。 例如,当为“GFX810”目标汇编时,这将设置为整数值“1”。 可能的 GFX 次要代号在 处理器 中介绍。
.amdgcn.gfx_generation_stepping¶
设置为正在汇编的目标的 GFX 步进代号。 例如,当为“GFX704”目标汇编时,这将设置为整数值“4”。 可能的 GFX 步进代号在 处理器 中介绍。
.amdgcn.next_free_vgpr¶
在汇编开始之前设置为零。在每条指令处,如果此符号的当前值小于或等于该指令中显式引用的最大 VGPR 编号,则符号值将更新为等于该 VGPR 编号加一。
可用于在 AMDHSA 内核汇编器指令中设置 .amdhsa_next_free_vgpr 指令。
可以随时设置,例如在每个内核开始时手动设置为零。
.amdgcn.next_free_sgpr¶
在汇编开始之前设置为零。在每条指令处,如果此符号的当前值小于或等于该指令中显式引用的最大 SGPR 编号,则符号值将更新为等于该 SGPR 编号加一。
可用于在 AMDHSA 内核汇编器指令中设置 .amdhsa_next_free_spgr 指令。
可以随时设置,例如在每个内核开始时手动设置为零。
代码对象 V3 及更高版本的指令¶
以 .amdgcn
开头的指令对所有 amdgcn
架构处理器均有效,并且与操作系统无关。以 .amdhsa
开头的指令特定于指定 amdhsa
操作系统时的 amdgcn
架构处理器。请参阅 目标三元组 和 处理器。
.amdgcn_target <target-triple> “-” <target-id>¶
可选指令,用于声明包含汇编器源文件支持的 <target-triple>-<target-id>
。汇编器使用它来验证命令行选项,例如 -triple
、-mcpu
和 --offload-arch=<target-id>
。允许使用非规范的目标 ID。请参阅 目标三元组 和 目标 ID。
注意
此指令的代码对象 V2 到 V3 使用的目标 ID 语法与在其他地方使用的语法不同。请参阅 代码对象 V2 到 V3 目标 ID。
.amdhsa_code_object_version <version>¶
可选指令,用于声明汇编器要生成的代码对象版本。如果不存在,将使用默认值。
.amdhsa_kernel <name>¶
在当前节的当前位置创建一个正确对齐的 AMDHSA 内核描述符和一个符号 <name>.kd
。仅当操作系统为 amdhsa
时有效。<name>
必须是标记要执行的第一条指令的符号,并且不需要预先定义。
标记用于生成内核描述符字节的指令列表的开始,如 内核描述符 中所述。此列表中可能出现的指令在 AMDHSA 内核汇编器指令 中描述。指令可以以任何顺序出现,必须对要汇编的目标有效,并且不能重复。指令支持 内核描述符 中它们引用的字段指定的取值范围。如果未指定指令,则假定它具有默认值,除非它被标记为“必需”,在这种情况下,省略该指令是错误的。此指令列表以 .end_amdhsa_kernel
指令终止。
表 103 AMDHSA 内核汇编器指令¶ 指令
默认值
支持的架构
描述
.amdhsa_group_segment_fixed_size
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 GROUP_SEGMENT_FIXED_SIZE。
.amdhsa_private_segment_fixed_size
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 PRIVATE_SEGMENT_FIXED_SIZE。
.amdhsa_kernarg_size
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 KERNARG_SIZE。
.amdhsa_user_sgpr_count
0
GFX6-GFX12
控制 COMPUTE_PGM_RSRC2 GFX6-GFX12 的 compute_pgm_rsrc2 中的 USER_SGPR_COUNT。
.amdhsa_user_sgpr_private_segment_buffer
0
GFX6-GFX10(GFX942 除外)
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_PRIVATE_SEGMENT_BUFFER。
.amdhsa_user_sgpr_dispatch_ptr
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_DISPATCH_PTR。
.amdhsa_user_sgpr_queue_ptr
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_QUEUE_PTR。
.amdhsa_user_sgpr_kernarg_segment_ptr
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_KERNARG_SEGMENT_PTR。
.amdhsa_user_sgpr_dispatch_id
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_DISPATCH_ID。
.amdhsa_user_sgpr_flat_scratch_init
0
GFX6-GFX10(GFX942 除外)
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_FLAT_SCRATCH_INIT。
.amdhsa_user_sgpr_private_segment_size
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_SGPR_PRIVATE_SEGMENT_SIZE。
.amdhsa_wavefront_size32
目标特性特定 (wavefrontsize64)
GFX10-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_WAVEFRONT_SIZE32。
.amdhsa_uses_dynamic_stack
0
GFX6-GFX12
控制 代码对象 V3 内核描述符 中的 USES_DYNAMIC_STACK。
.amdhsa_system_sgpr_private_segment_wavefront_offset
0
GFX6-GFX10(GFX942 除外)
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_PRIVATE_SEGMENT。
.amdhsa_enable_private_segment
0
GFX942, GFX11-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_PRIVATE_SEGMENT。
.amdhsa_system_sgpr_workgroup_id_x
1
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_SGPR_WORKGROUP_ID_X。
.amdhsa_system_sgpr_workgroup_id_y
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_SGPR_WORKGROUP_ID_Y。
.amdhsa_system_sgpr_workgroup_id_z
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_SGPR_WORKGROUP_ID_Z。
.amdhsa_system_sgpr_workgroup_info
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_SGPR_WORKGROUP_INFO。
.amdhsa_system_vgpr_workitem_id
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_VGPR_WORKITEM_ID。可能的值在 系统 VGPR 工作项 ID 枚举值 中定义。
.amdhsa_next_free_vgpr
必需
GFX6-GFX12
显式引用的最大 VGPR 编号加一。用于计算 GFX6-GFX12 的 compute_pgm_rsrc1 中的 GRANULATED_WORKITEM_VGPR_COUNT。
.amdhsa_next_free_sgpr
必需
GFX6-GFX12
显式引用的最大 SGPR 编号加一。用于计算 GFX6-GFX12 的 compute_pgm_rsrc1 中的 GRANULATED_WAVEFRONT_SGPR_COUNT。
.amdhsa_accum_offset
必需
GFX90A, GFX942
统一寄存器文件中第一个 AccVGPR 的偏移量。用于计算 GFX90A、GFX942 的 compute_pgm_rsrc3 中的 ACCUM_OFFSET。
.amdhsa_reserve_vcc
1
GFX6-GFX12
内核是否可以使用特殊的 VCC SGPR。用于计算 GFX6-GFX12 的 compute_pgm_rsrc1 中的 GRANULATED_WAVEFRONT_SGPR_COUNT。
.amdhsa_reserve_flat_scratch
1
GFX7-GFX10(GFX942 除外)
内核是否可以使用平面指令访问暂存内存。用于计算 GFX6-GFX12 的 compute_pgm_rsrc1 中的 GRANULATED_WAVEFRONT_SGPR_COUNT。
.amdhsa_reserve_xnack_mask
目标特性特定 (xnack)
GFX8-GFX10
内核是否可以触发 XNACK 重放。用于计算 GFX6-GFX12 的 compute_pgm_rsrc1 中的 GRANULATED_WAVEFRONT_SGPR_COUNT。
.amdhsa_float_round_mode_32
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FLOAT_ROUND_MODE_32。可能的值在 浮点舍入模式枚举值 中定义。
.amdhsa_float_round_mode_16_64
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FLOAT_ROUND_MODE_16_64。可能的值在 浮点舍入模式枚举值 中定义。
.amdhsa_float_denorm_mode_32
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FLOAT_DENORM_MODE_32。可能的值在 浮点反常值模式枚举值 中定义。
.amdhsa_float_denorm_mode_16_64
3
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FLOAT_DENORM_MODE_16_64。可能的值在 浮点反常值模式枚举值 中定义。
.amdhsa_dx10_clamp
1
GFX6-GFX11
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 ENABLE_DX10_CLAMP。
.amdhsa_ieee_mode
1
GFX6-GFX11
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 ENABLE_IEEE_MODE。
.amdhsa_round_robin_scheduling
0
GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 ENABLE_WG_RR_EN。
.amdhsa_fp16_overflow
0
GFX9-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FP16_OVFL。
.amdhsa_tg_split
目标特性特定 (tgsplit)
GFX90A, GFX942, GFX11-GFX12
控制 GFX90A、GFX942 的 compute_pgm_rsrc3 中的 TG_SPLIT。
.amdhsa_workgroup_processor_mode
目标特性特定 (cumode)
GFX10-GFX12
控制 代码对象 V3 内核描述符 中的 ENABLE_WGP_MODE。
.amdhsa_memory_ordered
1
GFX10-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 MEM_ORDERED。
.amdhsa_forward_progress
0
GFX10-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc1 中的 FWD_PROGRESS。
.amdhsa_shared_vgpr_count
0
GFX10-GFX11
控制 GFX10-GFX11 的 compute_pgm_rsrc3 中的 SHARED_VGPR_COUNT。
.amdhsa_inst_pref_size
0
GFX11-GFX12
控制 GFX10-GFX11 的 compute_pgm_rsrc3 或 GFX12 的 compute_pgm_rsrc3 中的 INST_PREF_SIZE。
.amdhsa_exception_fp_ieee_invalid_op
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_IEEE_754_FP_INVALID_OPERATION。
.amdhsa_exception_fp_denorm_src
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_FP_DENORMAL_SOURCE。
.amdhsa_exception_fp_ieee_div_zero
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_IEEE_754_FP_DIVISION_BY_ZERO。
.amdhsa_exception_fp_ieee_overflow
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_IEEE_754_FP_OVERFLOW。
.amdhsa_exception_fp_ieee_underflow
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_IEEE_754_FP_UNDERFLOW。
.amdhsa_exception_fp_ieee_inexact
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_IEEE_754_FP_INEXACT。
.amdhsa_exception_int_div_zero
0
GFX6-GFX12
控制 GFX6-GFX12 的 compute_pgm_rsrc2 中的 ENABLE_EXCEPTION_INT_DIVIDE_BY_ZERO。
.amdhsa_user_sgpr_kernarg_preload_length
0
GFX90A, GFX942
控制 代码对象 V3 内核描述符 中的 KERNARG_PRELOAD_SPEC_LENGTH。
.amdhsa_user_sgpr_kernarg_preload_offset
0
GFX90A, GFX942
控制 代码对象 V3 内核描述符 中的 KERNARG_PRELOAD_SPEC_OFFSET。
.amdgpu_metadata¶
可选指令,用于声明 NT_AMDGPU_METADATA
注释记录的内容(请参阅 AMDGPU 代码对象 V3 及更高版本的 ELF 注释记录)。
内容必须采用 [YAML] 标记格式,其结构和语义与 代码对象 V3 元数据、代码对象 V4 元数据 或 代码对象 V5 元数据 中描述的相同。
此指令以 .end_amdgpu_metadata
指令终止。
代码对象 V3 及更高版本的示例源代码¶
这是一个最小汇编源文件的示例,定义了一个 HSA 内核
1.amdgcn_target "amdgcn-amd-amdhsa--gfx900+xnack" // optional
2
3.text
4.globl hello_world
5.p2align 8
6.type hello_world,@function
7hello_world:
8 s_load_dwordx2 s[0:1], s[0:1] 0x0
9 v_mov_b32 v0, 3.14159
10 s_waitcnt lgkmcnt(0)
11 v_mov_b32 v1, s0
12 v_mov_b32 v2, s1
13 flat_store_dword v[1:2], v0
14 s_endpgm
15.Lfunc_end0:
16 .size hello_world, .Lfunc_end0-hello_world
17
18.rodata
19.p2align 6
20.amdhsa_kernel hello_world
21 .amdhsa_user_sgpr_kernarg_segment_ptr 1
22 .amdhsa_next_free_vgpr .amdgcn.next_free_vgpr
23 .amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
24.end_amdhsa_kernel
25
26.amdgpu_metadata
27---
28amdhsa.version:
29 - 1
30 - 0
31amdhsa.kernels:
32 - .name: hello_world
33 .symbol: hello_world.kd
34 .kernarg_segment_size: 48
35 .group_segment_fixed_size: 0
36 .private_segment_fixed_size: 0
37 .kernarg_segment_align: 4
38 .wavefront_size: 64
39 .sgpr_count: 2
40 .vgpr_count: 3
41 .max_flat_workgroup_size: 256
42 .args:
43 - .size: 8
44 .offset: 0
45 .value_kind: global_buffer
46 .address_space: global
47 .actual_access: write_only
48//...
49.end_amdgpu_metadata
此内核等效于以下 HIP 程序
1__global__ void hello_world(float *p) {
2 *p = 3.14159f;
3}
如果汇编源文件包含多个内核和/或函数,则可以使用 .set <symbol>, <expression>
指令重置 .amdgcn.next_free_vgpr 和 .amdgcn.next_free_sgpr 符号。例如,在两个内核的情况下,如果 function1
仅从 kernel1
调用,则将函数与调用它的内核分组,并在两个连接的组件之间重置符号就足够了
1.amdgcn_target "amdgcn-amd-amdhsa--gfx900+xnack" // optional
2
3// gpr tracking symbols are implicitly set to zero
4
5.text
6.globl kern0
7.p2align 8
8.type kern0,@function
9kern0:
10 // ...
11 s_endpgm
12.Lkern0_end:
13 .size kern0, .Lkern0_end-kern0
14
15.rodata
16.p2align 6
17.amdhsa_kernel kern0
18 // ...
19 .amdhsa_next_free_vgpr .amdgcn.next_free_vgpr
20 .amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
21.end_amdhsa_kernel
22
23// reset symbols to begin tracking usage in func1 and kern1
24.set .amdgcn.next_free_vgpr, 0
25.set .amdgcn.next_free_sgpr, 0
26
27.text
28.hidden func1
29.global func1
30.p2align 2
31.type func1,@function
32func1:
33 // ...
34 s_setpc_b64 s[30:31]
35.Lfunc1_end:
36.size func1, .Lfunc1_end-func1
37
38.globl kern1
39.p2align 8
40.type kern1,@function
41kern1:
42 // ...
43 s_getpc_b64 s[4:5]
44 s_add_u32 s4, s4, func1@rel32@lo+4
45 s_addc_u32 s5, s5, func1@rel32@lo+4
46 s_swappc_b64 s[30:31], s[4:5]
47 // ...
48 s_endpgm
49.Lkern1_end:
50 .size kern1, .Lkern1_end-kern1
51
52.rodata
53.p2align 6
54.amdhsa_kernel kern1
55 // ...
56 .amdhsa_next_free_vgpr .amdgcn.next_free_vgpr
57 .amdhsa_next_free_sgpr .amdgcn.next_free_sgpr
58.end_amdhsa_kernel
这些符号无法识别连接的组件,以便自动跟踪每个内核的使用情况。但是,在某些情况下,在源文件中仔细组织内核和函数意味着只需极少量的额外工作即可准确计算 GPR 使用量。