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 操作系统),但以下情况除外

  • amdhsar600 架构中不受支持(参见 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 指令在 gfx900gfx902gfx909gfx90c 上不可用

  • 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

  • 以下指令在 gfx1011gfx1012 上不可用

    • 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 指定的目标特性使用单独的选项指定。这些目标特性可以具有 onoff 值。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 则省略,如果为 OnAny 则存在。

注意

代码对象 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/LIMITSRC_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 除外),前提是其他操作的同步作用域为

  • systemagent 并且由同一 agent 上的线程执行。

  • workgroup 并且由同一工作组中的线程执行。

  • wavefront 并且由同一 wavefront 中的线程执行。

workgroup

与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为

  • systemagentworkgroup 并且由同一工作组中的线程执行。

  • wavefront 并且由同一 wavefront 中的线程执行。

wavefront

与其他操作(图像操作除外)同步,并参与修改和 seq_cst 完全排序,适用于所有地址空间(private 或访问 private 的 generic 除外),前提是其他操作的同步作用域为

  • systemagentworkgroupwavefront 并且由同一 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)。

表 27 AMDGPU LLVM IR Intrinsic

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 函数。

llvm.frexp

为 half、float 和 double 实现。

llvm.log2

为 float 和 half(以及 float 或 half 的向量)实现。未为 double 实现。硬件为 float 提供 1ULP 精度,为 half 提供 0.51ULP 精度。Float 指令本身不支持非正规输入。

llvm.sqrt

为 double、float 和 half(以及向量)实现。

llvm.log

为 float 和 half(以及向量)实现。

llvm.exp

为 float 和 half(以及向量)实现。

llvm.log10

为 float 和 half(以及向量)实现。

llvm.exp2

为 float 和 half(以及 float 或 half 的向量)实现。未为 double 实现。硬件为 float 提供 1ULP 精度,为 half 提供 0.51ULP 精度。Float 指令本身不支持非正规输入。

llvm.stacksave.p5

已实现,必须使用 alloca 地址空间。

llvm.stackrestore.p5

已实现,必须使用 alloca 地址空间。

llvm.get.fpmode.i32

自然的浮点模式类型是 i32。这是通过使用 s_getreg_b32 从 MODE 寄存器中提取相关位来实现的。前 10 位是核心浮点模式。位 12:18 是异常掩码。在 gfx9+ 上,位 23 是 FP16_OVFL。与浮点指令无关的位域为 0。

llvm.get.rounding

AMDGPU 支持两种可单独控制的舍入模式,具体取决于浮点类型。一种控制 float,另一种控制 double 和 half 操作。如果两种模式相同,则返回标准返回值之一。如果模式不同,则返回 12 个扩展值 之一,描述两种模式。

向最近舍入,远离零的 ties 不是受支持的模式。MODE 寄存器中的原始舍入模式值与 FLT_ROUNDS 值不完全匹配,因此会执行转换。

llvm.set.rounding

输入值应为 ‘llvm.get.rounding’ 的有效结果之一。如果未传递有效输入,则舍入模式未定义。这应该是一个 wavefront 一致值。如果输入值发散,将使用第一个活动 lane 的值。

llvm.get.fpenv

返回 AMDGPU 浮点环境的当前值。这存储与当前舍入模式、非正规化模式、启用的陷阱和浮点异常相关的信息。格式是 MODE 和 TRAPSTS 寄存器的 64 位串联。

llvm.set.fpenv

将浮点环境设置为指定状态。

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 调度的指令类型的掩码。

  • 0x0000:不允许任何指令跨越 sched_barrier 进行调度。

  • 0x0001:所有非内存、非副作用产生的指令都可以跨越 sched_barrier 进行调度,允许 ALU 指令通过。

  • 0x0002:VALU 指令可以跨越 sched_barrier 进行调度。

  • 0x0004:SALU 指令可以跨越 sched_barrier 进行调度。

  • 0x0008:MFMA/WMMA 指令可以跨越 sched_barrier 进行调度。

  • 0x0010:所有 VMEM 指令都可以跨越 sched_barrier 进行调度。

  • 0x0020:VMEM 读取指令可以跨越 sched_barrier 进行调度。

  • 0x0040:VMEM 写入指令可以跨越 sched_barrier 进行调度。

  • 0x0080:所有 DS 指令都可以跨越 sched_barrier 进行调度。

  • 0x0100:所有 DS 读取指令都可以跨越 sched_barrier 进行调度。

  • 0x0200:所有 DS 写入指令都可以跨越 sched_barrier 进行调度。

  • 0x0400:所有超越函数(例如 V_EXP)指令都可以跨越 sched_barrier 进行调度。

llvm.amdgcn.sched.group.barrier

创建具有特定属性的调度组,以创建自定义调度流水线。组之间的顺序由指令调度器强制执行。Intrinsic 应用于 intrinsic 之前的代码。Intrinsic 采用三个值来控制调度组的行为。

  • 掩码:使用 llvm.amdgcn.sched_barrier 掩码值对指令组进行分类。

  • 大小:组中指令的数量。

  • SyncID:在具有匹配值的组之间强制执行顺序。

掩码可以包括多种指令类型。设置超出有效掩码范围的值是未定义的行为。

组合多个 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 采用一个指定策略的值。编译器实现了两种策略。

  1. 交错 DS 和 MFMA 指令以用于小型 GEMM 内核。

  2. 交错 DS 和 MFMA 指令以用于单 wave 小型 GEMM 内核。

  3. 交错 TRANS 和 MFMA 指令,以及它们的 VALU 和 DS 前驱指令,以用于 attention 内核。

  4. 交错 TRANS 和 MFMA 指令,没有前驱指令交错,以用于 attention 内核。

在调度区域中只能使用一个 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 个操作数对应于比例输入。

  • 用于矩阵 A 的每个 lane 的 2 位字节索引

  • 矩阵 A 比例值

  • 用于矩阵 B 的每个 lane 的 2 位字节索引

  • 矩阵 B 比例值

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 函数中相同。

所有计数器(lgkmcntvmcntstorecnt 等)在函数入口处都被假定为未知状态。

一个函数可以有多个出口(例如,一个链式出口和一个普通的 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_vgprnum_agprnumbered_sgpr,这些符号可以被上述符号表达式引用和使用。这三个符号是 amdgcn.max_num_vgpramdgcn.max_num_agpramdgcn.max_num_sgpr

ELF 代码对象

AMDGPU 后端生成标准的 ELF [ELF] 可重定位代码对象,该对象可以由 lld 链接,以生成标准的 ELF 共享代码对象,该对象可以在 AMDGPU 目标上加载和执行。

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

.relaname

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 记录

.relaname.rela.dyn

对于可重定位的代码对象,name 是应用重定位记录的节的名称。例如,.rela.text 是与 .text 节关联的重定位记录的节名称。

对于链接的共享代码对象,.rela.dyn 包含来自每个可重定位代码对象的 .relaname 节的所有重定位记录。

有关 AMDGPU 后端支持的重定位记录,请参阅 重定位记录

.text

内核和它们调用的函数的可执行机器代码。生成为位置无关代码。有关 isa 生成中使用的约定的信息,请参阅 代码约定

.amdgpu.kernel.runtime.handle

用于设备排队的符号。

Note 记录

AMDGPU 后端代码对象在 .note 节中包含 ELF note 记录。生成的 note 集合及其语义取决于代码对象版本;请参阅 代码对象 V2 Note 记录代码对象 V3 及更高版本的 Note 记录

根据 ELFCLASS32ELFCLASS64 的要求,必须在 name 字段后生成最少的零字节填充,以确保 desc 字段是 4 字节对齐的。此外,必须生成最少的零字节填充,以确保 desc 字段大小是 4 字节的倍数。.note 节的 sh_addralign 字段必须至少为 4,以指示至少 8 字节对齐。

代码对象 V2 Note 记录

警告

此版本的 LLVM 不再支持代码对象 V2 的生成。

当为代码对象 V2 编译时,AMDGPU 后端代码对象在 .note 节中使用以下 ELF note 记录。

note 记录 vendor 字段为 “AMD”。

可能存在其他 note 记录,但此处未记录的任何 note 记录都已弃用,不应使用。

表 40 AMDGPU 代码对象 V2 ELF Note 记录

名称

类型

描述

“AMD”

NT_AMD_HSA_CODE_OBJECT_VERSION

代码对象版本。

“AMD”

NT_AMD_HSA_HSAIL

由 HSAIL Finalizer 而不是 LLVM 编译器生成的 HSAIL 属性。

“AMD”

NT_AMD_HSA_ISA_VERSION

目标 ISA 版本。

“AMD”

NT_AMD_HSA_METADATA

YAML [YAML] 文本格式的元数据空字符结尾字符串。

“AMD”

NT_AMD_HSA_ISA_NAME

目标 ISA 名称。

表 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_sizearchitecture_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 记录都已弃用,不应使用。

表 43 AMDGPU 代码对象 V3 及以上版本 ELF 注释记录

名称

类型

描述

“AMDGPU”

NT_AMDGPU_METADATA

Message Pack [MsgPack] 二进制格式的元数据。

“AMDGPU”

NT_AMDGPU_KFD_CORE_STATE

运行时、代理和队列状态的快照,用于核心转储。 请参阅 核心文件注释

表 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_LOR_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 目标都使用相同的映射。

表 47 AMDGPU DWARF 寄存器映射

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_offsetDW_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 内存空间映射 中定义。

表 48 AMDGPU DWARF 内存空间映射

DWARF

AMDGPU

内存空间名称

内存空间

DW_MSPACE_LLVM_none

0x0000

通用(平面)

DW_MSPACE_LLVM_global

0x0001

全局

DW_MSPACE_LLVM_constant

0x0002

全局

DW_MSPACE_LLVM_group

0x0003

本地(组/LDS)

DW_MSPACE_LLVM_private

0x0004

私有(暂存)

DW_MSPACE_AMDGPU_region

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 地址空间映射 中定义。

表 49 AMDGPU DWARF 地址空间映射

DWARF

AMDGPU

注释

地址空间名称

地址

位大小

LLVM IR 地址空间

64 位进程地址空间

32 位进程地址空间

DW_ASPACE_LLVM_none

0x00

64

32

全局

默认地址空间

DW_ASPACE_AMDGPU_generic

0x01

64

32

通用(平面)

DW_ASPACE_AMDGPU_region

0x02

32

32

区域 (GDS)

DW_ASPACE_AMDGPU_local

0x03

32

32

本地(组/LDS)

保留

0x04

DW_ASPACE_AMDGPU_private_lane

0x05

32

32

私有(暂存)

聚焦通道

DW_ASPACE_AMDGPU_private_wave

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_pieceDW_AT_LLVM_lane_pc 属性表达式使用,其中发散控制流由执行掩码控制。 未定义的位置描述与 DW_OP_LLVM_extend 一起使用,以指示通道在进入子程序时未处于活动状态。 有关示例,请参阅 DW_AT_LLVM_lane_pc

基本类型转换

对于 AMDGPU 表达式,DW_OP_convert 可用于在不同地址空间中 DW_ATE_address 编码的基本类型之间进行转换。

当满足此处描述的所有相关条件时,转换的定义如 地址空间 中所述,否则会导致评估错误。

注意

对于不支持特定地址空间的目标,与该地址空间之间进行转换始终是评估错误。

对于支持通用地址空间的目标,当通用地址位于全局地址空间中时,定义了从 DW_ASPACE_AMDGPU_genericDW_ASPACE_LLVM_none 的转换。 转换不需要更改地址的文字值。

当存在相关的硬件支持,已完成任何必需的硬件设置,并且通用地址位于相应的地址空间中时,定义了从 DW_ASPACE_AMDGPU_genericDW_ASPACE_AMDGPU_localDW_ASPACE_AMDGPU_private_waveDW_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) 字段具有以下值

  1. augmentation 字符串包含以下以 null 结尾的 UTF-8 字符串

    [amd:v0.0]
    

    vX.Y 指定此 CIE 中或使用它的 FDE 中使用的 AMDGPU 扩展的主版本号 X 和次版本号 Y。版本号符合 [SEMVER]

  2. address_size 对于 Global 地址空间在 地址空间标识符 中定义。

  3. segment_selector_size 为 0,因为 AMDGPU 不使用段选择器。

  4. code_alignment_factor 为 4 字节。

  5. data_alignment_factor 为 4 字节。

  6. return_address_register 对于 32 位进程为 PC_32,对于 64 位进程为 PC_64,在 寄存器标识符 中定义。

  7. 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 元数据映射 和引用的表定义的映射组成。

对于布尔值,字符串值 falsetrue 分别用于 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 元数据映射更改 中定义的更改和添加。

表 59 AMDHSA 代码对象 V4 元数据映射更改

字符串键

值类型

必需?

描述

“amdhsa.version”

2 个整数的序列

必需

  • 第一个整数是主版本。当前为 1。

  • 第二个整数是次版本号。当前为 1。

“amdhsa.target”

字符串

必需

使用以下语法表示代码的目标名称

<target-triple> [ "-" <target-id> ]

必须使用规范的目标 ID。参见 目标三元组目标 ID

代码对象 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 内核发生。

  1. 获取指向要在其上执行内核的内核代理的 AQL 队列的指针。

  2. 获取指向要执行的内核的内核描述符(参见 内核描述符)的指针。它必须用于包含在代码对象中的内核,该代码对象由与 AQL 队列关联的内核代理上的 HSA 兼容运行时加载。

  3. 使用 HSA 兼容运行时分配器为具有内核代理的 kernarg 属性的内存区域分配内核参数的空间,该内核代理将执行内核。它必须至少 16 字节对齐。

  4. 内核参数值被分配给内核参数内存分配。布局在 HSA 程序员语言参考 [HSA] 中定义。对于 AMDGPU,内核执行直接访问内核参数内存,方式与访问常量内存的方式相同。(请注意,HSA 规范允许实现将内核参数内容复制到内核访问的另一个位置。)

  5. 在 AQL 队列上创建 AQL 内核调度数据包。HSA 兼容运行时 API 使用 64 位原子操作来保留 AQL 队列中数据包的空间。必须设置数据包,并且最终写入必须使用原子存储释放来设置数据包种类,以确保数据包内容对内核代理可见。AQL 定义了门铃信号机制,以通知内核代理 AQL 队列已更新。这些规则以及 AQL 队列和内核调度数据包的布局在 HSA 系统架构规范 [HSA] 中定义。

  6. 内核调度数据包包括有关实际调度的信息(例如网格和工作组大小),以及来自代码对象的有关内核的信息(例如段大小)。可以使用 HSA 兼容运行时查询内核符号来获取代码对象值,这些值记录在 代码对象元数据 中。

  7. CP 执行微代码,并负责检测和设置 GPU 以执行内核调度的波前。

  8. CP 确保当波前开始执行内核机器代码时,标量通用寄存器 (SGPR) 和向量通用寄存器 (VGPR) 按照机器代码的要求进行设置。所需的设置在 内核描述符 中定义。初始寄存器状态在 初始内核执行状态 中定义。

  9. 内核机器代码的序言(参见 内核序言)根据需要设置机器状态,然后再继续执行与内核对应的机器代码。

  10. 当内核调度完成执行时,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/LIMITSRC_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

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 硬件按如下方式完成

  1. 工作组 ID 之前的 SGPR 由 CP 使用 16 个用户数据寄存器设置。

  2. 工作组 ID 寄存器 X、Y、Z 由 ADC 设置,ADC 支持任何组合,包括无。

  3. 暂存波前偏移量由 SPI 在每个波前的基础上设置,这就是为什么其值不能包含在每个队列的平面暂存初始化值中的原因(参见 平面暂存)。

  4. VGPR 由 SPI 设置,SPI 仅支持指定 (X)、(X, Y) 或 (X, Y, Z)。

  5. 平面暂存寄存器对初始化在 平面暂存 中描述。

可以使用缓冲区指令(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
  1. CFI 返回地址未定义。

  2. 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 寄存器(参见 初始内核执行状态

    1. 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。

    2. 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 (非易失性 & 非临时性)

    1. buffer/global/flat_load

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_load glc=1 slc=1

  • volatile (易失性)

    1. buffer/global/flat_load glc=1

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

load (加载)

none

none

  • local (本地)

  1. ds_load

store (存储)

none

none

  • global

  • generic (通用)

  • private

  • constant

  • !volatile & !nontemporal (非易失性 & 非临时性)

    1. buffer/global/flat_store

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_store glc=1 slc=1

  • volatile (易失性)

    1. buffer/global/flat_store

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

store (存储)

none

none

  • local (本地)

  1. 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 (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

monotonic (单调)

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_store

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_atomic

获取原子

load atomic (原子加载)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • local (本地)

  • generic (通用)

  1. ds/flat_load

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_load glc=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保加载在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_load glc=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 flat_load 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • local (本地)

  • generic (通用)

  1. ds/flat_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acquire (获取)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acquire (获取)

  • workgroup

none

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL 且地址空间不是通用,则省略。

  • 有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间

  • 必须在任何先前的本地/通用原子加载/原子读-修改-写之后发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比 fence 配对原子操作读取的值旧。

fence (栅栏)

acquire (获取)

  • agent

  • system

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

释放原子

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保在执行正在释放的存储之前,所有对本地内存的操作都已完成。

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • local (本地)

  1. ds_store

store atomic (原子存储)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保在执行正在释放的存储之前,所有对内存的操作都已完成。

  1. buffer/global/flat_store

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的 atomicrmw 之前发生。

  • 确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • local (本地)

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 之前,所有对全局和本地内存的操作都已完成。

  1. buffer/global/flat_atomic

fence (栅栏)

release (释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

release (释放)

  • workgroup

none

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL 且地址空间不是通用,则省略。

  • 有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间

  • 必须在任何先前的本地/通用加载/原子加载/存储/原子存储/原子读-修改-写之后发生。

  • 必须在任何后续的原子存储/原子读-修改-写之前发生,其同步作用域相等或更宽,并且内存排序强于无序(这被称为 fence 配对原子操作)。

  • 确保在执行后续的 fence 配对原子操作之前,所有对本地内存的操作都已完成。

fence (栅栏)

release (释放)

  • agent

  • system

none

  1. 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 (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • global

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的 atomicrmw 之前发生。

  • 确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。

  1. buffer/global_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • local (本地)

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • generic (通用)

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的 atomicrmw 之前发生。

  • 确保在执行正在释放的 atomicrmw 之前,所有对本地内存的操作都已完成。

  1. flat_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • global

  1. 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 之前,所有对全局内存的操作都已完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • generic (通用)

  1. 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 之前,所有对全局内存的操作都已完成。

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acq_rel (获取-释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acq_rel (获取-释放)

  • workgroup

none

  1. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL 且地址空间不是通用,则省略。

  • 但是,由于 LLVM 当前在 fence 上没有地址空间,因此需要保守地始终生成(参见先前 fence 的注释)。

  • 必须在任何先前的本地/通用加载/原子加载/存储/原子存储/原子读-修改-写之后发生。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保在执行任何后续的全局内存操作之前,所有对本地内存的操作都已完成。

  • 确保先前的本地/通用原子加载/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 acquire-fence 配对原子操作)在后续的全局内存操作之前已完成。这满足了 acquire 的要求。

  • 确保在后续的本地/通用原子存储/原子读-修改-写(其同步作用域相等或更宽,并且内存排序强于无序,这被称为 release-fence 配对原子操作)之前,所有先前的内存操作都已完成。这满足了 release 的要求。

fence (栅栏)

acq_rel (获取-释放)

  • agent

  • system

none

  1. 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 的要求。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。

顺序一致性原子

load atomic (原子加载)

seq_cst (顺序一致性)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • global

  • generic (通用)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • local (本地)

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 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 (非易失性 & 非临时性)

    1. buffer/global/flat_load

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_load glc=1 slc=1

  • volatile (易失性)

    1. buffer/global/flat_load glc=1

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

load (加载)

none

none

  • local (本地)

  1. ds_load

store (存储)

none

none

  • global

  • generic (通用)

  • private

  • constant

  • !volatile & !nontemporal (非易失性 & 非临时性)

    1. buffer/global/flat_store

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_store glc=1 slc=1

  • volatile (易失性)

    1. buffer/global/flat_store

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

store (存储)

none

none

  • local (本地)

  1. ds_store

无序原子

load atomic (原子加载)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

store atomic (原子存储)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

atomicrmw (原子读-修改-写)

unordered (无序)

any (任何)

any (任何)

与单调原子相同.

单调原子

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_load

load atomic (原子加载)

monotonic (单调)

  • workgroup

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1

  • 如果不是 TgSplit 执行模式,则省略 glc=1。

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_load

load atomic (原子加载)

monotonic (单调)

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1

load atomic (原子加载)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

获取原子

load atomic (原子加载)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_load glc=1

  • 如果不是 TgSplit 执行模式,则省略 glc=1。

  1. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_load

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_load glc=1

  • 如果不是 TgSplit 执行模式,则省略 glc=1。

  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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • global

  1. buffer/global_load glc=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保加载在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • system

  • global

  1. buffer/global/flat_load glc=1

  2. s_waitcnt vmcnt(0)

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保加载在使缓存无效之前已完成。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

load atomic (原子加载)

acquire (获取)

  • agent

  • generic (通用)

  1. flat_load glc=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 flat_load 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • system

  • generic (通用)

  1. flat_load glc=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保在使缓存失效之前,flat_load 已完成。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_atomic

  2. 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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • system

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • system

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

fence (栅栏)

acquire (获取)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acquire (获取)

  • workgroup

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acquire (获取)

  • agent

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acquire (获取)

  • system

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

释放原子

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

store atomic (原子存储)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

store atomic (原子存储)

release (释放)

  • agent

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保在执行正在释放的存储之前,所有对内存的操作都已完成。

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • system

  • global

  • generic (通用)

  1. buffer_wbl2

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 写回在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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 之前完成。

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • agent

  • global

  • generic (通用)

  1. 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 之前,所有对全局和本地内存的操作都已完成。

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • system

  • global

  • generic (通用)

  1. buffer_wbl2

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 写回在执行正在释放的存储之前完成。

  1. buffer/global/flat_atomic

fence (栅栏)

release (释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

release (释放)

  • workgroup

none

  1. 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

  1. 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

  1. buffer_wbl2

  • 如果是 OpenCL 且地址空间是本地的,则省略。

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • global

  1. 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 之前完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • generic (通用)

  1. 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 之前完成。

  1. flat_atomic

  2. s_waitcnt lgkmcnt(0) & vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略 vmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_wbinvl1_vol 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • global

  1. 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 之前,所有对全局内存的操作都已完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • system

  • global

  1. buffer_wbl2

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 之前完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_invl2; buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 L1 全局数据,也不会看到过时的 L2 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会在 L2 中过时。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • generic (通用)

  1. 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 之前,所有对全局内存的操作都已完成。

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续的 buffer_wbinvl1_vol 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • system

  • generic (通用)

  1. buffer_wbl2

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 之前完成。

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_invl2 和 buffer_wbinvl1_vol 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. 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

  1. 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 读取的值。

  1. buffer_wbinvl1_vol

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acq_rel (获取-释放)

  • agent

none

  1. 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 的要求。

  1. buffer_wbinvl1_vol

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。

fence (栅栏)

acq_rel (获取-释放)

  • system

none

  1. buffer_wbl2

  • 如果是 OpenCL 且地址空间是本地的,则省略。

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 的要求。

  1. 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 (通用)

  1. 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 可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 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 (非易失性 & 非临时性)

    1. buffer/global/flat_load

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_load nt=1

  • volatile (易失性)

    1. buffer/global/flat_load sc0=1 sc1=1

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

load (加载)

none

none

  • local (本地)

  1. ds_load

store (存储)

none

none

  • global

  • generic (通用)

  • private

  • constant

  • !volatile & !nontemporal (非易失性 & 非临时性)

    1. GFX942

      buffer/global/flat_store

  • !volatile & nontemporal (非易失性 & 临时性)

    1. GFX942

      buffer/global/flat_store nt=1

  • volatile (易失性)

    1. buffer/global/flat_store sc0=1 sc1=1

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

store (存储)

none

none

  • local (本地)

  1. ds_store

无序原子

load atomic (原子加载)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

store atomic (原子存储)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

atomicrmw (原子读-修改-写)

unordered (无序)

any (任何)

any (任何)

与单调原子相同.

单调原子

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_load

load atomic (原子加载)

monotonic (单调)

  • workgroup

  • global

  • generic (通用)

  1. buffer/global/flat_load sc0=1

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_load

load atomic (原子加载)

monotonic (单调)

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_load sc1=1

load atomic (原子加载)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_load sc0=1 sc1=1

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • workgroup

  • global

  • generic (通用)

  1. buffer/global/flat_store sc0=1

store atomic (原子存储)

monotonic (单调)

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_store sc1=1

store atomic (原子存储)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_store sc0=1 sc1=1

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_atomic sc1=1

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

获取原子

load atomic (原子加载)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_load sc0=1

  2. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在以下 buffer_inv 之前发生。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_load

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_load sc0=1

  2. 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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • global

  1. buffer/global_load sc1=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保加载在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • system

  • global

  1. buffer/global/flat_load sc0=1 sc1=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保加载在使缓存无效之前已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

load atomic (原子加载)

acquire (获取)

  • agent

  • generic (通用)

  1. flat_load sc1=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_inv 之前发生。

  • 确保 flat_load 在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • system

  • generic (通用)

  1. flat_load sc0=1 sc1=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_inv 之前发生。

  • 确保在使缓存失效之前,flat_load 已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在以下 buffer_inv 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_atomic

  2. 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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • global

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • system

  • global

  1. buffer/global_atomic sc1=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_inv 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • system

  • generic (通用)

  1. flat_atomic sc1=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

fence (栅栏)

acquire (获取)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acquire (获取)

  • workgroup

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acquire (获取)

  • agent

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acquire (获取)

  • system

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

释放原子

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. GFX942

    buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

store atomic (原子存储)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. GFX942

    buffer/global/flat_store sc0=1

store atomic (原子存储)

release (释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_store

store atomic (原子存储)

release (释放)

  • agent

  • global

  • generic (通用)

  1. buffer_wbl2 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保在执行正在释放的存储之前,所有对内存的操作都已完成。

  1. GFX942

    buffer/global/flat_store sc1=1

store atomic (原子存储)

release (释放)

  • system

  • global

  • generic (通用)

  1. buffer_wbl2 sc0=1 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 写回在执行正在释放的存储之前完成。

  1. buffer/global/flat_store sc0=1 sc1=1

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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 之前完成。

  1. buffer/global/flat_atomic sc0=1

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • agent

  • global

  • generic (通用)

  1. buffer_wbl2 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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 之前,所有对全局和本地内存的操作都已完成。

  1. buffer/global/flat_atomic sc1=1

atomicrmw (原子读-修改-写)

release (释放)

  • system

  • global

  • generic (通用)

  1. buffer_wbl2 sc0=1 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 写回在执行正在释放的存储之前完成。

  1. buffer/global/flat_atomic sc0=1 sc1=1

fence (栅栏)

release (释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

release (释放)

  • workgroup

none

  1. 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

  1. buffer_wbl2 sc1=1

  • 如果是 OpenCL 且地址空间是本地的,则省略。

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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

  1. buffer_wbl2 sc0=1 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • singlethread

  • wavefront

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • global

  1. 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 之前完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略。

  • 必须在以下 buffer_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • generic (通用)

  1. 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 之前完成。

  1. flat_atomic

  2. s_waitcnt lgkmcnt(0) & vmcnt(0)

  • 如果不是 TgSplit 执行模式,则省略 vmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • global

  1. buffer_wbl2 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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 之前,所有对全局内存的操作都已完成。

  1. buffer/global_atomic

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • system

  • global

  1. buffer_wbl2 sc0=1 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 之前完成。

  1. buffer/global_atomic sc1=1

  2. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • generic (通用)

  1. buffer_wbl2 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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 之前,所有对全局内存的操作都已完成。

  1. flat_atomic

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_inv 之前发生。

  • 确保 atomicrmw 在使缓存无效之前已完成。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • system

  • generic (通用)

  1. buffer_wbl2 sc0=1 sc1=1

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 之前完成。

  1. flat_atomic sc1=1

  2. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 TgSplit 执行模式,则省略 lgkmcnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_inv sc0=1 sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的 MTYPE NC 全局数据。由于内存探测,MTYPE RW 和 CC 内存永远不会过时。

fence (栅栏)

acq_rel (获取-释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acq_rel (获取-释放)

  • workgroup

none

  1. 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 读取的值。

  1. buffer_inv sc0=1

  • 如果不是 TgSplit 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acq_rel (获取-释放)

  • agent

none

  1. buffer_wbl2 sc1=1

  • 如果是 OpenCL 且地址空间是本地的,则省略。

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在 Agent 范围内可见。

  1. 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 的要求。

  1. buffer_inv sc1=1

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。

fence (栅栏)

acq_rel (获取-释放)

  • system

none

  1. buffer_wbl2 sc0=1 sc1=1

  • 如果是 OpenCL 且地址空间是本地的,则省略。

  • 必须在后续 s_waitcnt 之前发生。

  • 执行 L2 写回以确保先前的 global/generic store/atomicrmw 在系统范围内可见。

  1. 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 的要求。

  1. 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 (通用)

  1. 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 可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • local (本地)

如果是 TgSplit 执行模式,则无法使用本地地址空间。

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 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 (非易失性 & 非临时性)

    1. buffer/global/flat_load

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_load slc=1 dlc=1

    • 如果是 GFX10,则省略 dlc=1。

  • volatile (易失性)

    1. buffer/global/flat_load glc=1 dlc=1

    2. s_waitcnt vmcnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

load (加载)

none

none

  • local (本地)

  1. ds_load

store (存储)

none

none

  • global

  • generic (通用)

  • private

  • constant

  • !volatile & !nontemporal (非易失性 & 非临时性)

    1. buffer/global/flat_store

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_store glc=1 slc=1 dlc=1

    • 如果是 GFX10,则省略 dlc=1。

  • volatile (易失性)

    1. buffer/global/flat_store dlc=1

    • 如果是 GFX10,则省略 dlc=1。

    1. s_waitcnt vscnt(0)

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

store (存储)

none

none

  • local (本地)

  1. ds_store

无序原子

load atomic (原子加载)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

store atomic (原子存储)

unordered (无序)

any (任何)

any (任何)

与非原子相同.

atomicrmw (原子读-修改-写)

unordered (无序)

any (任何)

any (任何)

与单调原子相同.

单调原子

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • global

  • generic (通用)

  1. buffer/global/flat_load

load atomic (原子加载)

monotonic (单调)

  • workgroup

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1

  • 如果是 CU wavefront 执行模式,则省略 glc=1。

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_load

load atomic (原子加载)

monotonic (单调)

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_load glc=1 dlc=1

  • 如果是 GFX11,则省略 dlc=1。

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_store

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_atomic

获取原子

load atomic (原子加载)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_load glc=1

  • 如果是 CU wavefront 执行模式,则省略 glc=1。

  1. s_waitcnt vmcnt(0)

  • 如果是 CU wavefront 执行模式,则省略。

  • 必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • local (本地)

  1. ds_load

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 如果是 OpenCL,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_load glc=1

  • 如果是 CU wavefront 执行模式,则省略 glc=1。

  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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_load glc=1 dlc=1

  • 如果是 GFX11,则省略 dlc=1。

  1. s_waitcnt vmcnt(0)

  • 必须在后续 buffer_gl*_inv 之前发生。

  • 确保在使缓存失效之前,加载已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_load glc=1 dlc=1

  • 如果是 GFX11,则省略 dlc=1。

  1. s_waitcnt vmcnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在后续 buffer_gl*_invl 之前发生。

  • 确保在使缓存失效之前,flat_load 已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_atomic

  2. s_waitcnt vm/vscnt(0)

  • 如果是 CU wavefront 执行模式,则省略。

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在以下 buffer_gl0_inv 以及任何后续 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • local (本地)

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在以下 buffer_gl0_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。

  1. buffer_gl0_inv

  • 如果是 OpenCL,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt lgkmcnt(0) & vm/vscnt(0)

  • 如果是 CU wavefront 执行模式,则省略 vm/vscnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在以下 buffer_gl0_inv 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_atomic

  2. s_waitcnt vm/vscnt(0)

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在后续 buffer_gl*_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_atomic

  2. s_waitcnt vm/vscnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在后续 buffer_gl*_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acquire (获取)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acquire (获取)

  • workgroup

none

  1. 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 配对原子操作读取的值旧。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acquire (获取)

  • agent

  • system

none

  1. 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 读取的值。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

释放原子

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • local (本地)

  1. 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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_store

store atomic (原子存储)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. 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) 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • global

  • generic (通用)

  1. 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 之前完成。

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • local (本地)

  1. 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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 之前,所有对全局和本地内存的操作都已完成。

  1. buffer/global/flat_atomic

fence (栅栏)

release (释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

release (释放)

  • workgroup

none

  1. 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

  1. 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 (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • global

  1. 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 之前完成。

  1. buffer/global_atomic

  2. s_waitcnt vm/vscnt(0)

  • 如果是 CU wavefront 执行模式,则省略。

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在以下 buffer_gl0_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • local (本地)

  1. 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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_atomic

  2. s_waitcnt lgkmcnt(0)

  • 如果是 OpenCL,则省略。

  • 必须在以下 buffer_gl0_inv 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 如果是 OpenCL,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • generic (通用)

  1. 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 之前完成。

  1. flat_atomic

  2. s_waitcnt lgkmcnt(0) & vmcnt(0) & vscnt(0)

  • 如果是 CU wavefront 执行模式,则省略 vmcnt(0) 和 vscnt(0)。

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 必须在以下 buffer_gl0_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 load atomic 值。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • global

  1. 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 之前,所有对全局内存的操作都已完成。

  1. buffer/global_atomic

  2. s_waitcnt vm/vscnt(0)

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在后续 buffer_gl*_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • generic (通用)

  1. 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 之前完成。

  1. flat_atomic

  2. s_waitcnt vm/vscnt(0) & lgkmcnt(0)

  • 如果是 OpenCL,则省略 lgkmcnt(0)。

  • 如果是有返回值的原子操作,则使用 vmcnt(0),如果是无返回值的原子操作,则使用 vscnt(0)。

  • 必须在后续 buffer_gl*_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。

fence (栅栏)

acq_rel (获取-释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acq_rel (获取-释放)

  • workgroup

none

  1. 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 读取的值。

  1. buffer_gl0_inv

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acq_rel (获取-释放)

  • agent

  • system

none

  1. 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 的要求。

  1. buffer_gl1_inv; buffer_gl0_inv

  • 必须在任何后续的全局/通用加载/原子加载/存储/原子存储/原子读-修改-写之前发生。

  • 确保后续加载不会看到过时的全局数据。这满足了 acquire 的要求。

顺序一致性原子

load atomic (原子加载)

seq_cst (顺序一致性)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • global

  • generic (通用)

  1. 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 可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • local (本地)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 尽可能晚,以便存储可能已经完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 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: 设备/Agent

    • SCOPE_SYS: 系统

  • 当具有给定 SCOPE 的内存操作到达 SCOPE 值较小的缓存时,它会转发到下一级缓存。

  • 当具有给定 SCOPE 的内存操作到达 SCOPE 值大于或等于其自身的缓存时,操作可以继续

    • 读取可以命中缓存

    • 写入可以发生在此缓存中,并且事务从此缓存级别确认。

    • RMW 操作可以在本地完成。

  • global_invglobal_wbglobal_wbinv 指令用于使缓存失效、写回和写回+失效。受影响的缓存由指令的 SCOPE: 控制。

  • global_inv 使范围严格小于指令的缓存失效。失效请求不能与挂起或即将到来的内存操作重新排序。

  • global_wb 是一个写回操作,它还确保在较低范围级别完成的先前内存操作已到达 global_wbSCOPE:

    • 在 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 (非易失性 & 非临时性)

    1. buffer/global/flat_load

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_load th:TH_LOAD_NT

  • volatile (易失性)

    1. buffer/global/flat_load scope:SCOPE_SYS

    2. s_wait_loadcnt 0x0

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

load (加载)

none

none

  • local (本地)

  1. ds_load

store (存储)

none

none

  • global

  • generic (通用)

  • private

  • constant

  • !volatile & !nontemporal (非易失性 & 非临时性)

    1. buffer/global/flat_store

  • !volatile & nontemporal (非易失性 & 临时性)

    1. buffer/global/flat_store th:TH_STORE_NT

  • volatile (易失性)

    1. buffer/global/flat_store scope:SCOPE_SYS

    2. s_wait_storecnt 0x0

    • 必须在任何后续的易失性全局/通用加载/存储之前发生。

    • 确保对不同地址的易失性操作不会被硬件重新排序。

store (存储)

none

none

  • local (本地)

  1. 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 (通用)

  1. buffer/global/flat_load

load atomic (原子加载)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_load

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_store

store atomic (原子存储)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_store

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • agent

  • system

  • global

  • generic (通用)

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

monotonic (单调)

  • singlethread

  • wavefront

  • workgroup

  • local (本地)

  1. ds_atomic

获取原子

load atomic (原子加载)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_load

load atomic (原子加载)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_load scope:SCOPE_SE

  1. s_wait_loadcnt 0x0

  • 如果是 CU wavefront 执行模式,则省略。

  • 必须在以下 global_inv 之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • local (本地)

  1. ds_load

  2. s_wait_dscnt 0x0

  • 如果是 OpenCL,则省略。

  • 必须在以下 global_inv 之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. global_inv scope:SCOPE_SE

  • 如果为 OpenCL 或 CU 波前执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_load

  1. 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 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_load

  1. s_wait_loadcnt 0x0

  • 必须在以下 global_inv 之前发生。

  • 确保在使缓存失效之前,加载已完成。

  1. global_inv

load atomic (原子加载)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_load

  1. s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
  • 如果为 OpenCL,则省略 s_wait_dscnt 0x0

  • 必须在以下 global_inv 之前发生。

  • 确保在使缓存失效之前,flat_load 已完成。

  1. global_inv

atomicrmw (原子读-修改-写)

acquire (获取)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • global

  1. buffer/global_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
  • 如果是 CU wavefront 执行模式,则省略。

  • 必须在以下 global_inv 之前以及任何后续的 global/generic load/load atomic/store/store atomic/atomicrmw 之前发生。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • local (本地)

  1. ds_atomic

  2. s_wait_dscnt 0x0

  • 如果是 OpenCL,则省略。

  • 必须在以下 global_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的本地 atomicrmw 值。

  1. global_inv scope:SCOPE_SE

  • 如果是 OpenCL,则省略。

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • workgroup

  • generic (通用)

  1. flat_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
    s_wait_dscnt 0x0
  • 如果为 CU 波前执行模式,则对于不带返回值的 atomic,省略所有内容,对于带返回值的 atomic,仅发出 s_wait_dscnt 0x0

  • 如果为 OpenCL,则省略 s_wait_dscnt 0x0

  • 必须在以下 global_inv 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子读-修改-写值旧。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • global

  1. buffer/global_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
  • 必须在以下 global_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. global_inv

atomicrmw (原子读-修改-写)

acquire (获取)

  • agent

  • system

  • generic (通用)

  1. flat_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
    s_wait_dscnt 0x0
  • 如果为 OpenCL,则省略 dscnt

  • 必须在以下 global_inv 之前发生

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. global_inv

fence (栅栏)

acquire (获取)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acquire (获取)

  • workgroup

none

  1. 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 0x0s_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 配对原子操作读取的值旧。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acquire (获取)

  • agent

none

  1. s_wait_storecnt 0x0
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
  • 如果为 OpenCL,则省略 s_wait_dscnt 0x0

  • 如果为 OpenCL 且地址空间为 local,则省略所有内容。

  • 有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间

  • 注意:我们不必使用 s_wait_samplecnt 0x0s_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 读取的值。

  1. global_inv

释放原子

store atomic (原子存储)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_store

store atomic (原子存储)

release (释放)

  • workgroup

  • global

  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_dscnt 0x0

  • 等待可以根据以下规则独立移动

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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 之后发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

store atomic (原子存储)

release (释放)

  • 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 0x0s_wait_samplecnt 0x0s_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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_store

store atomic (原子存储)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. global_wb scope:SCOPE_SYS

    • 如果是代理作用域,则省略。

  2. 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 0x0s_wait_samplecnt 0x0s_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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有内存操作在执行正在释放的存储之前完成。

  1. buffer/global/flat_store

atomicrmw (原子读-修改-写)

release (释放)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • workgroup

  • global

  • generic (通用)

  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_dscnt 0x0

  • 如果为 OpenCL 和 CU 波前执行模式,则全部省略。

  • 等待可以根据以下规则独立移动

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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 之前完成。

  1. buffer/global/flat_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • 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 0x0s_wait_samplecnt 0x0s_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 之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_atomic

atomicrmw (原子读-修改-写)

release (释放)

  • agent

  • system

  • global

  • generic (通用)

  1. global_wb scope:SCOPE_SYS

  • 如果是代理作用域,则省略。

  1. 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 0x0s_wait_samplecnt 0x0s_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 之前,所有对全局和本地内存的操作都已完成。

  1. buffer/global/flat_atomic

fence (栅栏)

release (释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

release (释放)

  • workgroup

none

  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_dscnt 0x0

  • 如果为 OpenCL 且地址空间为 local,则省略所有内容。

  • 有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间

  • 等待可以根据以下规则独立移动

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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

  1. global_wb scope:SCOPE_SYS

  • 如果是代理作用域,则省略。

  1. s_wait_bvhcnt 0x0
    s_wait_samplecnt 0x0
    s_wait_storecnt 0x0
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
    OpenCL
    s_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 0x0s_wait_samplecnt 0x0s_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 (通用)

  1. buffer/global/ds/flat_atomic

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • global

  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_dscnt 0x0

  • 必须在任何先前的本地/通用加载/存储/原子加载/原子存储/原子读-修改-写之后发生。

  • 等待可以根据以下规则独立移动

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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 之前完成。

  1. buffer/global_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
  • 如果是 CU wavefront 执行模式,则省略。

  • 必须在以下 global_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 atomicrmw 值。

  1. 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 0x0s_wait_samplecnt 0x0s_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 之后发生。

  • 必须在后续的存储之前发生。

  • 确保所有全局内存操作在执行正在释放的存储之前完成。

  1. ds_atomic

  2. s_wait_dscnt 0x0

  • 如果是 OpenCL,则省略。

  • 必须在以下 global_inv 之前发生。

  • 确保任何后续的全局数据读取都不比正在获取的本地原子加载值旧。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 如果是 OpenCL,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • workgroup

  • generic (通用)

  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_loadcnt 0x0s_wait_samplecnt 0x0s_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 之前完成。

  1. flat_atomic

  1. 不带返回值的 Atomic
    s_wait_dscnt 0x0
    s_wait_storecnt 0x0
    带返回值的 Atomic
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
    CU 波前执行模式
    s_wait_dscnt 0x0
  • 如果为 OpenCL,则省略 s_wait_dscnt 0x0

  • 必须在以下 global_inv 之前发生。

  • 确保任何后续全局数据读取都不早于正在获取的 load atomic 值。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • global

  1. global_wb scope:SCOPE_SYS

  • 如果是代理作用域,则省略。

  1. 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 0x0s_wait_samplecnt 0x0s_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 之前,所有对全局内存的操作都已完成。

  1. buffer/global_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
  • 必须在以下 global_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. global_inv

atomicrmw (原子读-修改-写)

acq_rel (获取-释放)

  • agent

  • system

  • generic (通用)

  1. global_wb scope:SCOPE_SYS

  • 如果是代理作用域,则省略。

  1. 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 0x0s_wait_samplecnt 0x0s_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 之前完成。

  1. flat_atomic

  1. 带返回值的 Atomic
    s_wait_loadcnt 0x0
    s_wait_dscnt 0x0
    不带返回值的 Atomic
    s_wait_storecnt 0x0
    s_wait_dscnt 0x0
  • 如果为 OpenCL,则省略 s_wait_dscnt 0x0

  • 必须在以下 global_inv 之前发生。

  • 确保在使缓存失效之前,atomicrmw 已完成。

  1. global_inv

fence (栅栏)

acq_rel (获取-释放)

  • singlethread

  • wavefront

none

none

fence (栅栏)

acq_rel (获取-释放)

  • workgroup

none

  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 且地址空间不是 generic,则省略 s_wait_dscnt 0x0

  • 如果为 OpenCL 且地址空间为 local,则除了 s_wait_dscnt 0x0 之外全部省略。

  • 有关 fence 特定地址空间的更多详细信息,请参见 Fence 和地址空间

  • 等待可以根据以下规则独立移动

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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 读取的值。

  1. global_inv scope:SCOPE_SE

  • 如果是 CU wavefront 执行模式,则省略。

  • 确保后续加载不会看到过时的数据。

fence (栅栏)

acq_rel (获取-释放)

  • agent

  • system

none

  1. global_wb scope:SCOPE_SYS

  • 如果是代理作用域,则省略。

  1. 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 0x0s_wait_samplecnt 0x0s_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 的要求。

  1. global_inv scope:

顺序一致性原子

load atomic (原子加载)

seq_cst (顺序一致性)

  • singlethread

  • wavefront

  • global

  • local (本地)

  • generic (通用)

与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • workgroup

  • global

  • generic (通用)

  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_dscnt 0x0

  • 等待可以根据以下规则独立移动

  • s_wait_dscnt 0x0 必须在先前的 local/generic load atomic/store atomic/atomicrmw 之后发生,且该指令的内存排序为 seq_cst,并具有相等或更宽的同步作用域。(请注意,seq_cst 栅栏有其自己的 s_wait_dscnt 0x0,因此无需考虑。)

  • s_wait_loadcnt 0x0s_wait_samplecnt 0x0s_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 可能已完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • 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 0x0s_wait_samplecnt 0x0s_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 可能已完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 OpenCL 也必须生成所有指令。

load atomic (原子加载)

seq_cst (顺序一致性)

  • agent

  • system

  • global

  • generic (通用)

  1. 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 0x0s_wait_samplecnt 0x0s_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 可能已完成。)

  1. 后续指令与相应的加载原子获取相同,除了即使对于 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 代码对象 V2 的陷阱处理程序

  • AMDGPU AMDHSA OS 代码对象 V3 的陷阱处理程序

  • 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 如何实现函数调用

  1. Clang 决定 kernarg 布局以匹配 HSA 程序员语言参考 [HSA]

    • 所有结构体都直接传递。

    • Lambda 值以 TBA 方式传递。

  1. 内核在其序言中执行某些设置,如 内核序言 中所述。

非内核函数

本节介绍除外部内核函数之外的函数的调用约定 ABI。

如果内核具有函数调用,则始终分配 scratch 并将其用于调用堆栈,该堆栈使用交错 scratch 地址空间从低地址增长到高地址。

进入函数时

  1. SGPR0-3 包含具有以下属性的 V#(参见 私有段缓冲区

    • 指向波前 scratch 后备内存开头的基地址。

    • 以 dword 元素大小和波前大小元素的步幅交错。

  2. 已设置 FLAT_SCRATCH 寄存器对。 参见 Flat Scratch

  3. GFX6-GFX8:M0 寄存器设置为 LDS 的大小(以字节为单位)。 参见 M0

  4. EXEC 寄存器设置为进入函数时处于活动状态的 lane。

  5. MODE 寄存器:待定

  6. VGPR0-31 和 SGPR4-29 用于传递函数输入参数,如下所述。

  7. SGPR30-31 返回地址 (RA)。 函数完成时必须返回的代码地址。 如果函数为无返回,则该值未定义。

  8. 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 调整为最后一个局部分配之后的地址。

  9. 所有其他寄存器均未指定。

  10. 已执行任何必要的 s_waitcnt 以确保函数可以使用内存。

  11. 在 C ABI 中,对于结构体参数,使用按引用传递 (byref) 而不是按值传递 (byval)。 被调用者负责分配堆栈内存并在修改时复制结构体的值。 请注意,后端仍然支持结构体参数的 byval。

从函数退出时

  1. VGPR0-31 和 SGPR4-29 用于传递函数结果参数,如下所述。 使用的任何寄存器都被视为被破坏的寄存器。

  2. 以下寄存器被保留,并且具有与进入时相同的值

    • 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) 优化可能会将它们标记为保留。

  1. PC 设置为进入时提供的 RA。

  2. MODE 寄存器:待定

  3. 所有其他寄存器都被破坏。

  4. 已执行任何必要的 s_waitcnt 以确保调用方可以使用函数访问的内存。

函数输入参数由源语言函数显式声明的形式参数以及实现使用的隐式输入参数组成。

源语言输入参数是

  1. 任何源语言隐式 thisself 参数首先作为指针类型出现。

  2. 后跟函数形式参数,按从左到右的源顺序排列。

源语言结果参数是

  1. 函数结果参数。

小于或等于 16 字节的源语言输入或结果结构体类型参数被递归分解为其基本类型字段,并且每个字段都像单独的参数一样传递。 对于输入参数,如果被调用函数要求结构体在内存中(例如,因为它的地址被获取),则函数体负责分配堆栈位置并将字段参数复制到其中。 Clang 将此称为直接结构体

大于 16 字节的源语言输入结构体类型参数按引用传递。 调用者负责分配堆栈位置以创建结构体值的副本,并将地址作为输入参数传递。 被调用函数负责在访问输入参数时执行解引用。 Clang 将此称为按值结构体

大于 16 字节的源语言结果结构体类型参数按引用返回。 调用者负责分配堆栈位置以保存结果值,并将地址作为最后一个输入参数(在隐式输入参数之前)传递。 在这种情况下,没有结果参数。 被调用函数负责在存储结果值时执行解引用。 Clang 将此称为结构化返回 (sret)

待办事项:更正 ``sret`` 定义。

Lambda 参数类型被视为结构体类型,具有实现定义的字段集。

对于 AMDGPU 后端,所有源语言参数(包括分解的结构体类型参数)都在 VGPR 中传递,除非标记为 inreg,在这种情况下,它们在 SGPR 中传递。

AMDGPU 后端从叶节点开始遍历函数调用图,以确定使用了哪些隐式输入参数,并传播到函数的每个调用者。 使用的隐式参数按以下顺序附加到函数参数中,在源语言参数之后

  1. 工作项 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. 调度程序指针 (2 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  3. 队列指针 (2 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  4. Kernarg 段指针 (2 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  5. 调度 ID (2 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  6. 工作组 ID X (1 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  7. 工作组 ID Y (1 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  8. 工作组 ID Z (1 SGPR)

    该值来自初始内核执行状态。 参见 SGPR 寄存器设置顺序

  9. 隐式参数指针 (2 SGPR)

    该值通过将偏移量添加到 Kernarg 段指针来计算,以获得指向第一个 kernarg 隐式参数的全局地址空间指针。

输入和结果参数按以下方式按顺序分配

注意

以下描述可能存在一些错误和遗漏,需要更正。

  • VGPR 参数分配给从 VGPR0 开始到 VGPR31 的连续 VGPR。

    如果参数多于这些寄存器可以容纳的数量,则剩余的参数按顺序在堆栈上自然对齐的地址上分配。

  • SGPR 参数分配给从 SGPR0 开始到 SGPR29 的连续 SGPR。

    如果参数多于这些寄存器可以容纳的数量,则剩余的参数按顺序在堆栈上自然对齐的地址上分配。

请注意,分解的结构体类型参数可能有一些字段在寄存器中传递,而另一些字段在内存中传递。

以下内容不是 AMDGPU 函数调用约定的一部分,而是描述 AMDGPU 如何实现函数调用

  1. 如果需要,SGPR33 用作帧指针 (FP)。 与 SP 类似,它是一个非交错 scratch 地址。 仅当使用运行时大小的 alloca 或出于 SIFrameLowering 中定义的原因时才需要它。

  2. 支持运行时堆栈对齐。 SGPR34 用作基指针 (BP) 以访问函数中的传入堆栈参数。 仅当函数需要运行时堆栈对齐时,才需要 BP。

  3. 不支持在堆栈上分配 SGPR 参数。

  4. 当前未生成 CFI。 参见 A.6.4 调用帧信息

    注意

    将生成 CFI,其将 CFA 定义为相对于最低地址堆栈分配局部变量的非交错私有地址空间中 wave scratch 基地址的非交错地址。

    DW_AT_frame_base 将定义为交错私有地址空间中的交错地址,方法是将 CFA 除以波前大小(因为 CFA 始终至少为 dword 对齐,这与 scratch 交错元素大小匹配)。

    如果未执行动态堆栈对齐,则堆栈分配的参数作为相对于 DW_AT_frame_base 的负偏移量访问,局部变量和寄存器溢出槽作为相对于 DW_AT_frame_base 的正偏移量访问。

  5. 函数参数传递通过在进入时将输入物理寄存器复制到虚拟寄存器来实现。 如果必要,寄存器分配器可以溢出。 这些在调用站点复制回物理寄存器。 最终效果是,每个函数调用都可以在完全不同的位置具有这些值。 IPRA 可以帮助避免改组参数寄存器。

  6. 调用站点通过在相对于 SP 的正偏移量处设置参数来实现。 然后,在调用之前增加 SP 以考虑已知的帧大小,并在调用之后减小 SP。

    注意

    CFI 将反映从 SP 计算 CFA 所需的更改后的计算。

  7. 堆栈帧中使用 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.trapllvm.debugtrap 指令的处理方式如下

表 99 非 AMDHSA 操作系统的 AMDGPU 陷阱处理程序

用法

代码序列

描述

llvm.trap

s_endpgm

导致波前终止。

llvm.debugtrap

none

编译器发出警告,指出未安装陷阱处理程序。

核心文件格式

本节介绍支持 AMDGPU 的核心文件格式。 AMDGPU 程序的 Core dump 可以分为 2 种类型:拆分或统一核心文件。

拆分布局由一个主机核心文件组成,其中包含重建主机进程映像的信息,以及一个 AMDGPU 核心文件,其中包含进程中使用的 AMDGPU 代理的信息。 AMDGPU 核心文件包括

  • 描述进程的 AMDGPU 代理、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_VERSIONKFD_IOCTL_MINOR_VERSION 定义值,这些值与注释中的 kfd_version_majorkfd_version_major 的值匹配。

内存段

AMDGPU 核心文件必须在加载段(类型为 PT_LOAD)中包含 AMDGPU 代理内存的映像。 这些段必须与代理内存内容映射到主机进程的内存区域相对应(请注意,这些内存映射通常进程本身不可读)。

当使用拆分核心文件布局时,这些段必须包含在 AMDGPU 核心文件中。

源语言

OpenCL

当语言为 OpenCL 时,会发生以下差异

  1. 使用 OpenCL 内存模型(请参阅 内存模型)。

  2. AMDGPU 后端为 AMDHSA 操作系统内核的显式参数附加了其他参数(请参阅 为 AMDHSA 操作系统附加的 OpenCL 内核隐式参数)。

  3. 生成其他元数据(请参阅 代码对象元数据)。

表 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 时,会发生以下差异

  1. 使用 HSA 内存模型(请参阅 内存模型)。

汇编器

AMDGPU 后端具有基于 LLVM-MC 的汇编器,目前正在开发中。 它支持 AMDGCN GFX6-GFX11。

本节介绍指令和操作数的一般语法。

指令

指令具有以下 语法

<opcode> <operand0>, <operand1>,... <modifier0> <modifier1>...

操作数 用逗号分隔,而 修饰符 用空格分隔。

操作数和修饰符的顺序是固定的。 大多数修饰符是可选的,可以省略。

详细指令语法描述的链接可以在下表中找到。 请注意,开发中的功能不包含在此描述中。

架构

核心 ISA

ISA 变体和扩展

GCN 2

GFX7

-

GCN 3, GCN 4

GFX8

-

GCN 5

GFX9

gfx900

gfx902

gfx904

gfx906

gfx909

gfx90c

CDNA 1

GFX9

gfx908

CDNA 2

GFX9

gfx90a

CDNA 3

GFX9

gfx942

RDNA 1

GFX10 RDNA1

gfx1010

gfx1011

gfx1012

gfx1013

RDNA 2

GFX10 RDNA2

gfx1030

gfx1031

gfx1032

gfx1033

gfx1034

gfx1035

gfx1036

RDNA 3

GFX11

gfx1100

gfx1101

gfx1102

gfx1103

有关指令、其语义以及操作数的支持组合的更多信息,请参阅指令集架构手册 [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

majorminor 是整数,指定汇编器将生成的 HSA 代码对象的版本。

.hsa_code_object_isa [major, minor, stepping, vendor, arch]

majorminorstepping 都是整数,描述汇编程序的指令集架构 (ISA) 版本。

vendorarch 是带引号的字符串。 vendor 应始终等于“AMD”,arch 应始终等于“AMDGPU”。

默认情况下,汇编器将从传递给汇编器的 -mcpu 选项的值派生 ISA 版本、vendorarch

.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_majormachine_version_minoramd_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_alignmentgroup_segment_alignmentprivate_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_rsrc3GFX12 的 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 使用量。

其他文档

[AMD-GCN-GFX6] (1,2)AMD Southern Islands 系列 ISA

AMD Southern Islands 系列 ISA

[AMD-GCN-GFX7] (1,2)AMD Sea Islands 系列 ISA

AMD Sea Islands 系列 ISA

[AMD-GCN-GFX8] (1,2)AMD GCN3 指令集架构

AMD GCN3 指令集架构

[AMD-GCN-GFX900-GFX904-VEGA] (1,2)AMD Vega 指令集架构

AMD Vega 指令集架构

[AMD-GCN-GFX906-VEGA7NM] (1,2)AMD Vega 7nm 指令集架构

AMD Vega 7nm 指令集架构

[AMD-GCN-GFX908-CDNA1] (1,2)AMD Instinct MI100 指令集架构

AMD Instinct MI100 指令集架构

[AMD-GCN-GFX90A-CDNA2] (1,2)AMD Instinct MI200 指令集架构

AMD Instinct MI200 指令集架构

[AMD-GCN-GFX942-CDNA3] (1,2)AMD Instinct MI300 指令集架构

AMD Instinct MI300 指令集架构

[AMD-GCN-GFX10-RDNA1] (1,2)AMD RDNA 1.0 指令集架构

AMD RDNA 1.0 指令集架构

[AMD-GCN-GFX10-RDNA2] (1,2)AMD RDNA 2 指令集架构

AMD RDNA 2 指令集架构

[AMD-GCN-GFX11-RDNA3] (1,2)AMD RDNA 3 指令集架构

AMD RDNA 3 指令集架构

[AMD-GCN-GFX11-RDNA3.5] (1,2)AMD RDNA 3.5 指令集架构

AMD RDNA 3.5 指令集架构

[AMD-ROCm-github] (1,2)AMD ROCm™ github

AMD ROCm™ github

[CLANG-ATTR] (1,2,3,4,5)Clang 中的属性

Clang 中的属性

[DWARF] (1,2)DWARF 调试信息格式

DWARF 调试信息格式

[ELF] (1,2)可执行和可链接格式 (ELF)

可执行和可链接格式 (ELF)

[HSA] (1,2,3,4,5,6,7,8,9,10)异构系统架构 (HSA) 基金会

异构系统架构 (HSA) 基金会

[MsgPack] (1,2,3,4)Message Pack

Message Pack

[OpenCL] (1,2)OpenCL 规范版本 2.0

OpenCL 规范版本 2.0

[SEMVER] (1,2,3)语义化版本

语义化版本

[YAML] (1,2,3)YAML 不是标记语言 (YAML™) 版本 1.2

YAML 不是标记语言 (YAML™) 版本 1.2