Sunky Blog

Back

11.1. PTX 模块指示符#

以下指示符声明模块中代码的 PTX ISA 版本、该代码生成时所面向的目标架构, 以及 PTX 模块内地址的大小。

  • .version

  • .target

  • .address_size

11.1.1. PTX 模块指示符:.version#

.version

PTX ISA 版本号。

.version  major.minor    // major, minor are integers

指定 PTX 语言版本号。

当 PTX 语言出现不兼容的变更(例如语法或语义方面的变更)时,major 号会递增。PTX 编译器使用 版本号的 major 部分来确保遗留 PTX 代码的正确执行。

当 PTX 新增特性时,minor 号会递增。

表明该模块必须由支持相同或更高版本号的工具编译。

每个 PTX 模块都必须以 .version 指示符开头,并且模块内的其他任何位置 都不允许再出现 .version 指示符。

.version 3.1
.version 3.0
.version 2.3

11.1.2. PTX 模块指示符:.target#

.target

架构与平台目标。

.target stringlist         // comma separated list of target specifiers
string = { sm_120a, sm_120f, sm_120,          // sm_12x target architectures
           sm_121a, sm_121f, sm_121,          // sm_12x target architectures
           sm_110a, sm_110f, sm_110,          // sm_11x target architectures
           sm_100a, sm_100f, sm_100,          // sm_10x target architectures
           sm_101a, sm_101f, sm_101,          // sm_10x target architectures
           sm_103a, sm_103f, sm_103           // sm_10x target architectures
           sm_107a, sm_107f, sm_107           // sm_10x target architectures
           sm_90a, sm_90,                     // sm_9x target architectures
           sm_80, sm_86, sm_87, sm_88, sm_89, // sm_8x target architectures
           sm_70, sm_72, sm_75,               // sm_7x target architectures
           sm_60, sm_61, sm_62,               // sm_6x target architectures
           sm_50, sm_52, sm_53,               // sm_5x target architectures
           sm_30, sm_32, sm_35, sm_37,        // sm_3x target architectures
           sm_20,                             // sm_2x target architectures
           sm_10, sm_11, sm_12, sm_13,        // sm_1x target architectures
           texmode_unified, texmode_independent,   // texturing mode
           debug,                                  // platform option
           map_f64_to_f32 };                       // platform option

指定生成当前 PTX 代码所面向的目标架构的特性集合。一般而言,SM 架构的各代之间遵循洋葱层模型, 即每一代都在保留之前各代所有特性的基础上新增特性。洋葱层模型使得为某一目标生成的 PTX 代码 能够在更后续代的设备上运行。

带后缀“a”的目标架构(例如 sm_90a)包含架构专用特性, 这些特性仅在指定架构上受支持,因此这类目标不遵循洋葱层模型。所以,为这类目标生成的 PTX 代码 无法在更后续代的设备上运行。架构专用特性只能与支持这些特性的目标一起使用。

带后缀“f”的目标架构(例如 sm_100f)包含家族专用特性,这些特性仅在 同一架构家族内受支持。因此,为这类目标生成的 PTX 代码只能运行在同一家族内更后续代的设备上。 家族专用特性既可用于 f 目标,也可用于同一家族内更后续代设备的 a 目标。

表 70 定义了各个架构家族。

表 70 架构家族

家族

所包含的目标 SM 架构

sm_10x 家族

sm_100f、sm_103f、sm_107f,以及 sm_10x 家族的后续目标

sm_11x 家族

sm_110f、sm_101f,sm_11x 家族 的后续目标

sm_12x 家族

sm_120f、sm_121f,以及 sm_12x 家族的后续目标

每个 PTX 模块都必须以 .version 指示符开头,其后紧跟一个 .target 指示符,其中包含目标架构和可选的平台选项。.target 指示符指定单一目标架构, 但后续的 .target 指示符可用于更改解析期间所允许的目标特性集合。含有多个 .target 指示符的程序只能在支持程序所列最高编号架构全部特性的设备上编译和运行。

PTX 特性会对照指定的目标架构进行检查,若使用了不受支持的特性则生成错误。下表汇总了 PTX 中 随目标架构而异的特性。

目标

说明

sm_120

sm_120 架构的基线特性集。

sm_120f

新增对 sm_120f 家族专用特性的支持。

sm_120a

新增对 sm_120a 架构专用特性的支持。

sm_121

sm_121 架构的基线特性集。

sm_121f

新增对 sm_121f 家族专用特性的支持。

sm_121a

新增对 sm_121a 架构专用特性的支持。

目标

说明

sm_110

sm_110 架构的基线特性集。

sm_110f

新增对 sm_110f 家族专用特性的支持。

sm_110a

新增对 sm_110a 架构专用特性的支持。

目标

说明

sm_100

sm_100 架构的基线特性集。

sm_100f

新增对 sm_100f 家族专用特性的支持。

sm_100a

新增对 sm_100a 架构专用特性的支持。

sm_101

sm_101 架构的基线特性集。(已重命名为 sm_110)

sm_101f

新增对 sm_101f 家族专用特性的支持。(已重命名为 sm_110f)

sm_101a

新增对 sm_101a 架构专用特性的支持。(已重命名为 sm_110a)

sm_103

sm_103 架构的基线特性集。

sm_103f

新增对 sm_103f 家族专用特性的支持。

sm_103a

新增对 sm_103a 架构专用特性的支持。

sm_107

sm_107 架构的基线特性集。

sm_107f

新增对 sm_107f 家族专用特性的支持。

sm_107a

新增对 sm_107a 架构专用特性的支持。

目标

说明

sm_90

sm_90 架构的基线特性集。

sm_90a

新增对 sm_90a 架构专用特性的支持。

目标

说明

sm_80

sm_80 架构的基线特性集。

sm_86

新增在 min 和 max 指令上对 .xorsign 修饰符的支持。

sm_87

sm_87 架构的基线特性集。

sm_88

sm_88 架构的基线特性集。

sm_89

sm_89 架构的基线特性集。

目标

说明

sm_70

sm_70 架构的基线特性集。

sm_72

新增对 wmma 指令中整数乘数与累加器矩阵的支持。

新增对 cvt.pack 指令的支持。

sm_75

新增对 wmma 指令中子字节整数与单位乘数矩阵的支持。

新增对 ldmatrix 指令的支持。

新增对 movmatrix 指令的支持。

新增对 tanh 指令的支持。

目标

说明

sm_60

sm_60 架构的基线特性集。

sm_61

新增对 dp2a 和 dp4a 指令的支持。

sm_62

sm_61 架构的基线特性集。

目标

说明

sm_50

sm_50 架构的基线特性集。

sm_52

sm_50 架构的基线特性集。

sm_53

新增针对 .f16 和 .f16x2 类型的算术、比较与纹理指令的支持。

目标

说明

sm_30

sm_30 架构的基线特性集。

sm_32

新增 64 位 {atom,red}.{and,or,xor,min,max} 指令。

新增 shf 指令。

新增 ld.global.nc 指令。

sm_35

新增对 CUDA 动态并行的支持。

sm_37

sm_35 架构的基线特性集。

目标

说明

sm_20

sm_20 架构的基线特性集。

目标

说明

sm_10

sm_10 架构的基线特性集。

若使用任何 .f64 指令,则要求 map_f64_to_f32。

sm_11

新增 64 位 {atom,red}.{and,or,xor,min,max} 指令。

若使用任何 .f64 指令,则要求 map_f64_to_f32。

sm_12

新增 {atom,red}.shared、64 位 {atom,red}.global、vote 指令。

若使用任何 .f64 指令,则要求 map_f64_to_f32。

sm_13

新增双精度支持,包括扩展的舍入修饰符。

禁止使用 map_f64_to_f32。

纹理模式针对整个模块指定,在模块内部不能更改。

.target 的 debug 选项声明该 PTX 文件包含 DWARF 调试信息,后续对 PTX 的 编译将保留源码级调试所需的信息。若声明了 debug 选项,而文件中未找到 DWARF 信息,则会生成 错误消息。debug 选项要求 PTX ISA 3.0 或更高版本。

map_f64_to_f32 表示无论目标架构如何,所有双精度指令都映射为单精度。这使得 高级语言编译器能够将包含 double 类型的程序编译到不支持双精度运算的目标设备。注意, .f64 存储仍为 64 位,由从 .f64 转换为 .f32 的指令使用的仅为其一半。

.target sm_10       // baseline target architecture
.target sm_13       // supports double-precision
.target sm_20, texmode_independent
.target sm_90       // baseline target architecture
.target sm_90a      // PTX using architecture-specific features
.target sm_100f     // PTX using family-specific features

11.1.3. PTX 模块指示符:.address_size#

.address_size

整个 PTX 模块使用的地址大小。

.address_size  address-size
address-size = { 32, 64 };

指定 PTX 代码以及 PTX 中的二进制 DWARF 信息在整个模块内所假定的地址大小。

不允许在模块内重新定义该指示符。在存在分离编译时,所有模块必须指定(或默认取)相同的 地址大小。

.address_size 指示符是可选的,但若在模块内出现,则必须紧跟在 .target 指示符之后。

若省略 .address_size 指示符,地址大小默认为 32。

// example directives
   .address_size 32       // addresses are 32 bit
   .address_size 64       // addresses are 64 bit

// example of directive placement within a module .version 2.3 .target sm_20 .address_size 64 … .entry foo () { … }

11.2. 指定 Kernel 入口点与函数#

以下指示符用于指定 kernel 入口点与函数。

  • .entry

  • .func

11.2.1. Kernel 与函数指示符:.entry#

.entry

kernel 入口点与函数体,可带有可选参数。

.entry kernel-name ( param-list )  kernel-body
.entry kernel-name  kernel-body

为 kernel 函数定义入口点名称、参数与函数体。

参数经由 .param 空间内存传递,并列在可选的圆括号参数列表中。在 kernel 函数体内可按名称引用参数,并使用 ld.param{::entry} 指令将其载入寄存器。

除常规参数外,不透明的 .texref、.samplerref 与 .surfref 变量也可作为参数传递。这类参数只能在纹理与表面的加载、存储及查询指令中按名称引用,无法通过 ld.param 指令访问。

执行该 kernel 的 CTA 的形状与大小可在特殊寄存器中获取。

指定 kernel 程序的入口点。

在 kernel 启动时,kernel 的维度与属性被确定,并通过特殊寄存器提供,例如 %ntid、%nctaid 等。

.entry cta_fft
.entry filter ( .param .b32 x, .param .b32 y, .param .b32 z )
{
    .reg .b32 %r<99>;
    ld.param.b32  %r1, [x];
    ld.param.b32  %r2, [y];
    ld.param.b32  %r3, [z];
    ...
}

.entry prefix_sum ( .param .align 4 .s32 pitch[8000] ) { .reg .s32 %t; ld.param::entry.s32 %t, [pitch]; … }

11.2.2. Kernel 与函数指示符:.func#

.func

函数定义。

.func {.attribute(attr-list)} fname {.noreturn} {.abi_preserve N} {.abi_preserve_control N} function-body
.func {.attribute(attr-list)} fname (param-list) {.noreturn} {.abi_preserve N} {.abi_preserve_control N} function-body
.func {.attribute(attr-list)} (ret-param) fname (param-list) {.abi_preserve N} {.abi_preserve_control N} function-body

定义一个函数,包括输入参数、返回参数以及可选的函数体。

可选的 .noreturn 指示符表示该函数不会返回到调用方函数。.noreturn 指示符不能用于带有返回参数的函数。有关 .noreturn 指示符的说明,见性能调优指示符:.noreturn。

可选的 .attribute 指示符给出与该函数关联的附加信息。允许的属性见变量与函数属性指示符:.attribute的说明。

可选的 .abi_preserve 与 .abi_preserve_control 指示符用于指定通用寄存器和控制寄存器的数量。更多细节见性能调优指示符:.abi_preserve与性能调优指示符:.abi_preserve_control的说明。

若为函数指定了指示符(例如 .noreturn),则在函数声明与函数定义之间必须保持一致。

不含函数体的 .func 定义即为函数原型。

参数列表在函数体内定义局部作用域变量。参数必须为寄存器状态空间或参数状态空间中的基本类型。寄存器状态空间中的参数可在函数体内的指令中直接引用。.param 空间中的参数在函数体内使用 ld.param{::func} 与 st.param{::func} 指令访问。参数传递采用按值传递。

参数列表中的最后一个参数可以是不指定大小的 .b8 类型 .param 数组。它用于把任意多个参数打包进单个数组对象后传给函数。

调用带这种未定大小末位参数的函数时,若没有参数经由该参数传递,可以在 call 指令中省略最后一个参数。对此数组参数的访问必须落在数组范围内。若未传入数组,或访问超出实际传入数组的范围,则访问结果未定义。

PTX 语法隐藏了底层调用约定与 ABI 的全部细节。

参数传递的实现交由优化翻译器决定,它可能组合使用寄存器与栈位置来传递参数。

发布说明

对于 PTX ISA 1.x 版代码,参数必须位于寄存器状态空间,没有栈,且不允许递归。

目标为 sm_20 或更高的 PTX ISA 2.0 及后续版本允许参数位于 .param 状态空间,实现了带栈的 ABI,并支持递归。

目标为 sm_20 或更高的 PTX ISA 2.0 及后续版本最多支持一个返回值。

.func (.reg .b32 rval) foo (.reg .b32 N, .reg .f64 dbl)
{
.reg .b32 localVar;

… use N, dbl; other code;

mov.b32 rval,result; ret; }

… call (fooval), foo, (val0, val1); // return value in fooval …

.func foo (.reg .b32 N, .reg .f64 dbl) .noreturn { .reg .b32 localVar; … use N, dbl; other code; mov.b32 rval, result; ret; } … call foo, (val0, val1); …

.func (.param .u32 rval) bar(.param .u32 N, .param .align 4 .b8 numbers[]) { .reg .b32 input0, input1; ld.param.b32 input0, [numbers + 0]; ld.param.b32 input1, [numbers + 4]; … other code; ret; } …

.param .u32 N; .param .align 4 .b8 numbers[8]; st.param.u32 [N], 2; st.param.b32 [numbers + 0], 5; st.param.b32 [numbers + 4], 10; call (rval), bar, (N, numbers); …

11.2.3. Kernel 与函数指示符:.alias#

.alias

为已有函数符号定义别名。

.alias fAlias, fAliasee;

.alias 是模块作用域指示符,它把标识符 fAlias 定义为 fAliasee 所指函数的别名。

fAlias 与 fAliasee 都是非入口函数符号。

标识符 fAlias 是不带函数体的函数声明。

标识符 fAliasee 是一个函数符号,必须与 .alias 声明定义在同一模块中。函数 fAliasee 不能具有 .weak 链接。

fAlias 与 fAliasee 的原型必须一致。

程序可使用 fAlias 或 fAlisee 标识符来引用以 fAliasee 定义的函数。

.visible .func foo(.param .u32 p) {
   ...
}
.visible .func bar(.param .u32 p);
.alias bar, foo;
.entry test()
{
      .param .u32 p;
      ...
      call foo, (p);       // call foo directly
       ...
       .param .u32 p;
       call bar, (p);        // call foo through alias
}
.entry filter ( .param .b32 x, .param .b32 y, .param .b32 z )
{
    .reg .b32 %r1, %r2, %r3;
    ld.param.b32  %r1, [x];
    ld.param.b32  %r2, [y];
    ld.param.b32  %r3, [z];
    ...
}

11.3. 控制流指示符#

PTX 提供了用于指定 brx.idx 与 call 指令潜在目标的指示符。更多信息见 brx.idx 与 call 的说明。

  • .branchtargets

  • .calltargets

  • .callprototype

11.3.1. 控制流指示符:.branchtargets#

.branchtargets

声明潜在分支目标列表。

Label:   .branchtargets  list-of-labels ;

为后续的 brx.idx 声明潜在分支目标列表,并把该列表与行首的标签关联。

列表中的所有控制流标签必须与声明位于同一函数内。

标签列表可以使用紧凑的简写语法来枚举具有共同前缀的一段标签,类似于参数化变量名中说明的语法。

  .function foo () {
      .reg .u32 %r0;
      ...
      L1:
      ...
      L2:
      ...
      L3:
      ...
      ts: .branchtargets L1, L2, L3;
      @p brx.idx %r0, ts;
      ...

.function bar() { .reg .u32 %r0; … N0: … N1: … N2: … N3: … N4: … ts: .branchtargets N<5>; @p brx.idx %r0, ts; …

11.3.2. 控制流指示符:.calltargets#

.calltargets

声明潜在调用目标列表。

Label:   .calltargets  list-of-functions ;

为后续的间接调用声明潜在调用目标列表,并把该列表与行首的标签关联。

列表中提到的所有函数都必须在 .calltargets 指示符之前声明,并且所有函数必须具有相同的类型签名。

calltgt:  .calltargets  fastsin, fastcos;
...
@p   call  (%f1), %r0, (%x), calltgt;
...

11.3.3. 控制流指示符:.callprototype#

.callprototype

声明用于间接调用的原型。

 // no input or return parameters
label: .callprototype _ .noreturn {.abi_preserve N} {.abi_preserve_control N};
// input params, no return params
label: .callprototype _ (param-list) .noreturn {.abi_preserve N} {.abi_preserve_control N};
// no input params, // return params
label: .callprototype (ret-param) _ {.abi_preserve N} {.abi_preserve_control N};
// input, return parameters
label: .callprototype (ret-param) _ (param-list) {.abi_preserve N} {.abi_preserve_control N};

定义一个不带具体函数名的原型,并把该原型与某个标签关联。在无法完整知晓可能调用目标的情况下,间接调用指令即可使用该原型。

参数可以是寄存器状态空间或参数状态空间中的基本类型,也可以是参数状态空间中的数组类型。可用 sink 符号 '_' 避免出现无用的参数名。

可选的 .noreturn 指示符表示该函数不会返回到调用方函数。.noreturn 指示符不能用于带有返回参数的函数。.noreturn 指示符的说明见性能调优指示符:.noreturn。

可选的 .abi_preserve 与 .abi_preserve_control 指示符用于指定通用寄存器和控制寄存器的数量。更多细节见性能调优指示符:.abi_preserve与性能调优指示符:.abi_preserve_control的说明。

Fproto1: .callprototype  _ ;
Fproto2: .callprototype  _ (.param .f32 _);
Fproto3: .callprototype  (.param .u32 _) _ ;
Fproto4: .callprototype  (.param .u32 _) _ (.param .f32 _);
...
@p   call  (%val), %r0, (%f1), Fproto4;
...

// example of array parameter Fproto5: .callprototype _ (.param .b8 _[12]);

Fproto6: .callprototype _ (.param .f32 _) .noreturn; … @p call %r0, (%f1), Fproto6; …

// example of .abi_preserve Fproto7: .callprototype _ (.param .b32 _) .abi_preserve 10; … @p call %r0, (%r1), Fproto7; …

11.4. 性能调优指示符#

为了提供底层性能调优的手段,PTX 支持以下指示符,它们把信息传递给优化后端编译器。

  • .maxnreg

  • .maxntid

  • .reqntid

  • .minnctapersm

  • .maxnctapersm(已弃用)

  • .pragma

  • .abi_preserve

  • .abi_preserve_control

  • .minperctamemory

.maxnreg 指示符指定分配给单个线程的最大寄存器数;.maxntid 指示符指定线程块(CTA)中的最大线程数;.reqntid 指示符指定线程块(CTA)中要求的线程数;.minnctapersm 指示符指定单个多处理器(SM)上被调度的线程块数的最小值。这些指示符可用于(例如)限制资源需求(如寄存器),以提高线程总数并获得更大隐藏内存延迟的机会。.minnctapersm 指示符可与 .maxntid 或 .reqntid 指示符一起使用,从而不必直接指定寄存器数上限,就能在每线程寄存器数与多处理器利用率之间权衡。为每 SM 寄存器数不同的多个设备编译 PTX 时,这样做可能取得更好的性能。

设备函数指示符 .abi_preserve 与 .abi_preserve_control 指定函数必须为其调用方保留的、来自 callee save 寄存器的数据寄存器与控制寄存器数量。这可以理解为函数被调用时在调用方中仍然存活的通用寄存器与控制寄存器数量。控制寄存器指的是通往当前函数调用的调用树中出现的发散程序点的数量。

目前,.maxnreg、.maxntid、.reqntid 与 .minnctapersm 指示符按 entry 应用,且必须出现在 .entry 指示符与其函数体之间。这些指示符优先于传给优化后端的任何模块级约束。若这些指示符的约束彼此不一致,或在指定的目标设备上无法满足,则会生成警告信息。

为向 PTX 后端传递信息,支持通用的 .pragma 指示符。该指示符把一串字符串传给后端,这些字符串在 PTX 虚拟机模型内没有语义。.pragma 取值的解释由后端实现决定,超出 PTX ISA 的范围。注意,.pragma 指示符可以出现在模块(文件)作用域、entry 作用域,或作为语句出现在 kernel 与设备函数体内。

11.4.1. 性能调优指示符:.maxnreg#

.maxnreg

每个线程可分配的最大寄存器数。

.maxnreg n

声明 CTA 中每个线程的最大寄存器数。

编译器保证不会超过该上限。实际使用的寄存器数可能更少;例如后端可能编译到更少的寄存器,或者最大寄存器数还会受到 .maxntid 与 .maxctapersm 的进一步约束。

.entry foo .maxnreg 16 { ... }  // max regs per thread = 16

11.4.2. 性能调优指示符:.maxntid#

.maxntid

线程块(CTA)中的最大线程数。

.maxntid nx
.maxntid nx, ny
.maxntid nx, ny, nz

声明线程块(CTA)中的最大线程数。该上限通过给出 1D、2D 或 3D CTA 中每一维的最大范围来指定。最大线程数是各维最大范围的乘积。

以各维所指定最大范围之乘积计算得到的线程块最大线程数,保证不会在该指示符所出现 kernel 的任何一次调用中被超过。超出最大线程数会导致运行时错误或 kernel 启动失败。

注意,该指示符保证线程的总数不超过上限,但并不保证某一特定维度上的限制不会被超过。

.entry foo .maxntid 256       { ... }  // max threads = 256
.entry bar .maxntid 16,16,4   { ... }  // max threads = 1024

11.4.3. 性能调优指示符:.reqntid#

.reqntid

线程块(CTA)中的线程数量。

.reqntid nx
.reqntid nx, ny
.reqntid nx, ny, nz

通过指定 1D、2D 或 3D CTA 每个维度的范围,声明线程块(CTA)中的线程数量。 线程总数为各维度线程数的乘积。

该 kernel 的任意一次调用中指定的每个 CTA 维度大小都必须与本指示符中指定的值相等。 在启动时指定不同的 CTA 维度会导致运行时错误或 kernel 启动失败。

.entry foo .reqntid 256       { ... }  // num threads = 256
.entry bar .reqntid 16,16,4   { ... }  // num threads = 1024

11.4.4. 性能调优指示符:.minnctapersm#

.minnctapersm

每个 SM 上 CTA 的最小数量。

.minnctapersm ncta

声明映射到单个多处理器(SM)上的 kernel grid 中 CTA 的最小数量。

.entry foo .maxntid 256 .minnctapersm 4 { ... }

11.4.5. 性能调优指示符:.maxnctapersm(已弃用)#

.maxnctapersm

每个 SM 上 CTA 的最大数量。

.maxnctapersm ncta

声明可以映射到单个多处理器(SM)上的 kernel grid 中 CTA 的最大数量。

.entry foo .maxntid 256 .maxnctapersm 4 { ... }

11.4.6. 性能调优指示符:.noreturn#

.noreturn

指示该函数不会返回到其调用方函数。

.noreturn

指示该函数不会返回到其调用方函数。

可选的 .noreturn 指示符表示该函数不会返回到调用方函数。 .noreturn 指示符只能指定给设备函数,且必须出现在 .func 指示符与其函数体之间。

该指示符不能用于带有返回参数的函数。

若带有 .noreturn 指示符的函数在运行时返回到调用方函数,则行为未定义。

.func foo .noreturn { ... }

11.4.7. 性能调优指示符:.pragma#

.pragma

向 PTX 后端编译器传递指示符。

.pragma list-of-strings ;

向 PTX 后端编译器传递模块作用域、入口作用域或语句级的指示符。

.pragma 指示符可以出现在模块作用域、入口作用域或语句级。

.pragma 指示符字符串的解释取决于具体实现,且对 PTX 语义没有影响。 关于 ptxas 中定义的 pragma 字符串的说明,参见 .pragma 字符串说明。

.pragma "nounroll";    // disable unrolling in backend

// disable unrolling for current kernel .entry foo .pragma “nounroll”; { … }

11.4.8. 性能调优指示符:.abi_preserve#

.abi_preserve

指定应由该函数的调用方保留的通用寄存器数量。

.abi_preserve N

这是一个与架构无关的值,指定通用寄存器的实际数量。 ABI 内部将部分通用寄存器定义为保留(callee save)寄存器。 整数 N 指定该函数应保留的通用寄存器的实际数量。

.abi_preserve 指示符只能指定给设备函数,且必须出现在 .func 指示符与其函数体之间。

指定该指示符时,编译器后端会修改底层 ABI 组成部分,以确保该函数调用方中 存放在 callee save 寄存器里的活跃数据变量数量少于指定的值。

.func bar() .abi_preserve 8

// Indirect call via call prototype .func (.param .b32 out[30]) foo (.param .b32 in[30]) .abi_preserve 10 { … } … mov.b64 lpfoo, foo; prot: .callprototype (.param .b32 out[30]) _ (.param .b32 in[30]) .abi_preserve 10; call (out), lpfoo, (in), prot;

11.4.9. 性能调优指示符:.abi_preserve_control#

.abi_preserve_control

指定应由该函数的调用方保留的控制寄存器数量。

.abi_preserve_control N

这是一个与架构无关的值,指定在通往当前函数调用的调用树中出现的发散程序点数量。 ABI 内部将部分控制寄存器定义为保留(callee save)寄存器。 整数 N 指定该函数应保留的控制寄存器的实际数量。

.abi_preserve_control 指示符只能指定给设备函数,且必须出现在 .func 指示符与其函数体之间。

指定该指示符时,编译器后端会修改底层 ABI 组成部分,以确保该函数调用方中 存放在 callee save 控制寄存器里的活跃控制变量数量少于指定的值。

.func foo() .abi_preserve_control 14

// Indirect call via call prototype .func (.param .b32 out[30]) bar (.param .b32 in[30]) .abi_preserve_control 10 { … } … mov.b64 lpbar, bar; prot: .callprototype (.param .b32 out[30]) _ (.param .b32 in[30]) .abi_preserve_control 10; call (out), lpbar, (in), prot;

11.4.10. 性能调优指示符:.minperctamemory#

.minperctamemory

声明 kernel 所需的每 CTA 全局内存最小量。

.minperctamemory size;

size = 32-bit unsigned integer (bytes)

.minperctamemory 指示符指定启动该 kernel 时必须可用的 每 CTA 全局内存的最小字节数。

该指示符只允许用于 .entry 函数,且必须出现在 .entry 指示符与其函数体之间。

只有指定了该指示符的入口函数才允许使用特殊寄存器 %perctamemoryoffset 或 %perctamemorysize。

.entry foo .minperctamemory 2048 {
    ...
}

11.5. 调试指示符#

DWARF 格式的调试信息通过以下指示符在 PTX 模块中传递:

  • @@DWARF

  • .section

  • .file

  • .loc

  • .loc_intermediate

  • .nv_intermediate_source_section

.section 指示符自 PTX ISA 2.0 版引入,用于替代 @@DWARF 语法。@@DWARF 语法在 PTX ISA 2.0 版中被弃用,但仍为遗留的 PTX ISA 1.x 版代码所支持。

自 PTX ISA 3.0 版起,包含 DWARF 调试信息的 PTX 文件应当包含 .target debug 平台选项。这一前向声明指示 PTX 编译保留用于源码级调试的映射关系。

11.5.1. 调试指示符:@@dwarf#

@@dwarf

DWARF 格式的信息。

@@DWARF dwarf-string

dwarf-string may have one of the .byte byte-list // comma-separated hexadecimal byte values .4byte int32-list // comma-separated hexadecimal integers in range [0..2^32-1] .quad int64-list // comma-separated hexadecimal integers in range [0..2^64-1] .4byte label .quad label

@@DWARF .section .debug_pubnames, "", @progbits
@@DWARF .byte   0x2b, 0x00, 0x00, 0x00, 0x02, 0x00
@@DWARF .4byte  .debug_info
@@DWARF .4byte  0x000006b5, 0x00000364, 0x61395a5f, 0x5f736f63
@@DWARF .4byte  0x6e69616d, 0x63613031, 0x6150736f, 0x736d6172
@@DWARF .byte   0x00, 0x00, 0x00, 0x00, 0x00

11.5.2. 调试指示符:.section#

.section

PTX 段定义。

.section section_name { dwarf-lines }

dwarf-lines have the following formats: .b8 byte-list // comma-separated list of integers // in range [-128..255] .b16 int16-list // comma-separated list of integers // in range [-2^15..2^16-1] .b32 int32-list // comma-separated list of integers // in range [-2^31..2^32-1] label: // Define label inside the debug section .b64 int64-list // comma-separated list of integers // in range [-2^63..2^64-1] .b32 label .b64 label .b32 label+imm // a sum of label address plus a constant integer byte // offset(signed, 32bit) .b64 label+imm // a sum of label address plus a constant integer byte // offset(signed, 64bit) .b32 label1-label2 // a difference in label addresses between labels in // the same dwarf section (32bit) .b64 label3-label4 // a difference in label addresses between labels in // the same dwarf section (64bit)

.section .debug_pubnames
{
    .b32    LpubNames_end0-LpubNames_begin0
  LpubNames_begin0:
    .b8     0x2b, 0x00, 0x00, 0x00, 0x02, 0x00
    .b32    .debug_info
  info_label1:
    .b32    0x000006b5, 0x00000364, 0x61395a5f, 0x5f736f63
    .b32    0x6e69616d, 0x63613031, 0x6150736f, 0x736d6172
    .b8     0x00, 0x00, 0x00, 0x00, 0x00
  LpubNames_end0:
}

.section .debug_info { .b32 11430 .b8 2, 0 .b32 .debug_abbrev .b8 8, 1, 108, 103, 101, 110, 102, 101, 58, 32, 69, 68, 71, 32, 52, 46, 49 .b8 0 .b32 3, 37, 176, -99 .b32 info_label1 .b32 .debug_loc+0x4 .b8 -11, 11, 112, 97 .b32 info_label1+12 .b64 -1 .b16 -5, -65535 }

11.5.3. 调试指示符:.file#

.file

源文件名。

.file file_index "filename" {, timestamp, file_size}

将源文件名与一个整数索引关联。.loc 指示符通过索引引用源文件。

.file 指示符允许可选地指定一个表示最后修改时间的无符号数, 以及一个表示源文件字节大小的无符号整数。timestamp 与 file_size 的取值可以为 0,表示该信息不可用。

timestamp 的值采用 C 与 C++ 的数据类型 time_t 的格式。

file_size 是一个无符号 64 位整数。

.file 指示符仅允许出现在最外层作用域,即与 kernel 和设备函数声明 同级的层次。

若未指定时间戳与文件大小,二者默认为 0。

.file 1 "example.cu"
.file 2 "kernel.cu"
.file 1 "kernel.cu", 1339013327, 64118

11.5.4. 调试指示符:.loc#

.loc

源文件位置。

.loc file_index line_number column_position
.loc file_index line_number column_position,function_name label {+ immediate }, inlined_at file_index2 line_number2 column_position2

声明与词法上后续的 PTX 指令相关联的源文件位置(源文件、行号和列位置)。 .loc 引用由 .file 指示符定义的 file_index。

为表示由被内联的函数生成的 PTX 指令,可以在 .loc 指示符中额外指定 .inlined_at 属性。.inlined_at 属性指定所述函数被内联处的源位置。 file_index2、line_number2 与 column_position2 指定函数被内联的位置。.inlined_at 指示符中指定的源位置 在词法上必须先于 .loc 指示符中的源位置。

function_name 属性指定名为 .debug_str 的 DWARF 段中的偏移。 该偏移以 label 表达式或 label + immediate 表达式给出, 其中 label 定义在 .debug_str 段中。DWARF 段 .debug_str 包含以 null 结尾的 ASCII 字符串,用于指定被内联函数的名称。

注意,一条 PTX 指令可能只有一个相关联的源位置,由最近的词法上先行的 .loc 指示符决定; 若不存在先行的 .loc 指示符,则没有相关联的源位置。PTX 中的标号继承词法上紧随其后的最近指令的位置。 其后没有 PTX 指令的标号没有相关联的源位置。

    .loc 2 4237 0
L1:                        // line 4237, col 0 of file #2,
                           // inherited from mov
    mov.u32  %r1,%r2;      // line 4237, col 0 of file #2
    add.u32  %r2,%r1,%r3;  // line 4237, col 0 of file #2
...
L2:                        // line 4239, col 5 of file #2,
                           // inherited from sub
    .loc 2 4239 5
    sub.u32  %r2,%r1,%r3;  // line 4239, col 5 of file #2
    .loc 1 21 3
    .loc 1 9 3, function_name info_string0, inlined_at 1 21 3
    ld.global.u32   %r1, [gg]; // Function at line 9
    setp.lt.s32 %p1, %r1, 8;   // inlined at line 21
    .loc 1 27 3
    .loc 1 10 5, function_name info_string1, inlined_at 1 27 3
    .loc 1 15 3, function_name .debug_str+16, inlined_at 1 10 5
    setp.ne.s32 %p2, %r1, 18;
    @%p2 bra    BB2_3;

11.5.5. 调试指示符:.loc_intermediate#

.loc_intermediate

中间语言源文件位置。

.loc_intermediate file_index line_number column_position

声明与紧随其后的 PTX 指令相关联的中间语言源位置(源文件、行号和列 位置)。 .loc_intermediate 引用 file_index,该索引必须由指明某个中间 IR 源文件的 .file 指示符定义。

在程序顺序上,.loc_intermediate 指示符之前必须紧跟着 针对同一条指令的 .loc 指示符。 每条指令只能出现一个 .loc_intermediate。与 .loc 不同, .loc_intermediate 指示符不支持 inlined_at 属性。

中间源位置记录在辅助调试行表中,该表基于 .nv_intermediate_source_section 中的 ir-name,不会被合并进主要的 DWARF .debug_line 或 .nv_debug_line_ptx 节。.loc_intermediate 指示符 只允许出现在 kernel 或设备函数体内部。

.func (.param .b32 func_retval0) _Z17noinline_multiplyii(
    .param .b32 _Z17noinline_multiplyii_param_0,
    .param .b32 _Z17noinline_multiplyii_param_1
) {
    .reg .b32 %r<4>;
    .loc 1 71 0
    ld.param.b32  %r1, [_Z17noinline_multiplyii_param_0];
    ld.param.b32  %r2, [_Z17noinline_multiplyii_param_1];
    .loc 1 72 5
    .loc_intermediate 2 23 6               // tileIR_source.ir:23:6
    mul.lo.s32    %r3, %r2, %r1;
    .loc 1 73 5
    .loc_intermediate 2 24 0               // tileIR_source.ir:24:0
    add.lo.s32    %r3, %r3, %r1;
}
.file 1 "/path/to/vectorAdd.cu"
.file 2 "./tileIR_source.ir"

11.5.6. 调试指示符:.nv_intermediate_source_section#

.nv_intermediate_source_section

将中间 IR 源代码嵌入 PTX 模块。

.nv_intermediate_source_section {
    .code_block {
        .ir_name:        ir-name-string
        .sourceFileName: file-index
        .source_begin
            source-text
        .source_end
    }
    [ .code_block { ... } ]*
}

ir-name-string = quoted string identifier (e.g., “TileIR”) file-index = unsigned integer matching a .file directive source-text = raw text of the intermediate IR source (heredoc)

.nv_intermediate_source_section 构造将一个或多个 中间 IR 编译单元的源代码直接嵌入 PTX 模块。每个 .code_block 条目把一个 ir_name 字符串 (用于标识中间语言,例如 "TileIR")与一个 .file 索引以及该源文件的原始文本关联起来。

.sourceFileName 字段必须引用同一 PTX 模块中已由 .file 指示符声明的 file_index。.source_begin 与 .source_end 构造 指示源代码文本的起点和终点,其间可以包含包括换行在内的任意文本。

每个 .code_block 生成一个独立的 ELF 节,节名基于所指定的 ir_name。 目前 ir-name 仅支持 "TileIR"。.nv_intermediate_source_section 只允许出现在模块作用域。单个 PTX 模块中可能出现多个 .nv_intermediate_source_section 块; 它们的 .code_block 条目按顺序拼接。

.visible .entry _Z9vectorAddPf(
    .param .u64 .ptr .align 1 _Z9vectorAddPf_param_0
) {
    .loc 1 11 15
    .loc_intermediate 2 137 23
    ld.b64 %rd2, [%SP];
    .loc 1 12 15
    .loc_intermediate 3 24 23
    ld.b64 %rd3, [%SP];
}
.file 1 "./vectorAdd.cu"
.file 2 "./tileIR_source_A.ir"
.file 3 "./tileIR_source_B.ir"
.nv_intermediate_source_section {
    .code_block {
        .ir_name:        "TileIR"
        .sourceFileName: 2
        .source_begin
            __global__ void vectorAdd(float *v) { float a, b; *v = a + b; }
        .source_end
    }
    .code_block {
        .ir_name:        "TileIR"
        .sourceFileName: 3
        .source_begin
            __global__ void vectorSub(float *v) { float a, b; *v = a - b; }
        .source_end
    }
}

11.6. 链接指示符#

  • .extern

  • .visible

  • .weak

11.6.1. 链接指示符:.extern#

.extern

外部符号声明。

.extern identifier

声明 identifier 在当前模块之外定义。定义该标识符的模块必须在单个目标文件中仅以 .weak 或 .visible 定义它一次。符号的 extern 声明可以出现多次,对它的引用会针对该符号的唯一定义完成解析。

.extern .global .b32 foo;  // foo is defined in another module

11.6.2. 链接指示符:.visible#

.visible

可见(外部)符号声明。

.visible identifier

声明 identifier 全局可见。与 C 不同(C 中标识符除非声明为 static 否则全局可见),PTX 标识符除非用 .visible 声明为在当前模块之外可见,否则仅在当前模块内可见。

.visible .global .b32 foo;  // foo will be externally visible

11.6.3. 链接指示符:.weak#

.weak

可见(外部)符号声明。

.weak identifier

声明 identifier 全局可见但为弱符号。弱符号与全局可见符号类似,不同之处在于链接期间进行 符号解析时,弱符号只在全局可见符号之后才被选取。与全局可见符号不同,多个目标文件可以声明同一个弱 符号,并且只有当没有同名的全局符号时,对符号的引用才会解析到弱符号上。

.weak .func (.reg .b32 val) foo;  // foo will be externally visible

11.6.4. 链接指示符:.common#

.common

可见(外部)符号声明。

.common identifier

声明 identifier 全局可见但为「通用」(common)。

通用符号与全局可见符号类似。不过多个目标文件可以声明同一个通用符号,它们的类型和大小 可以各不相同,并且对符号的引用会解析到大小最大的那个通用符号上。

只能有一个目标文件初始化某个通用符号,并且该定义必须在来自不同目标文件的该通用符号的所有 其他定义中具有最大的大小。

.common 链接指示符只能用于具有 .global 存储的变量。它不能 用于函数符号,也不能用于不透明类型的符号。

.common .global .u32 gbl;

11.7. Cluster 维度指示符#

以下指示符用于指定有关 cluster 的信息:

  • .reqnctapercluster

  • .explicitcluster

  • .maxclusterrank

.reqnctapercluster 指示符指定 cluster 中 CTA 的数量。 .explicitcluster 指示符指定 kernel 应当在带有显式 cluster 细节的情况下启动。.maxclusterrank 指示符指定 cluster 中 CTA 的最大数量。

cluster 维度指示符只能应用于 kernel 函数。

11.7.1. Cluster 维度指示符:.reqnctapercluster#

.reqnctapercluster

声明 cluster 中 CTA 的数量。

.reqnctapercluster nx
.reqnctapercluster nx, ny
.reqnctapercluster nx, ny, nz

通过指定 1D、2D 或 3D cluster 中每个维度的范围,设置 cluster 中线程块(CTA)的数量。 CTA 的总数是各维度中 CTA 数量的乘积。对于指定了 .reqnctapercluster 指示符的 kernel, 如果启动时未指定相同的值,运行时将使用这里指定的值来配置启动。

如果启动时显式指定了 cluster 维度,它应当等于本指示符中指定的值。启动时指定不同的 cluster 维度会导致运行时错误或 kernel 启动失败。

.entry foo .reqnctapercluster 2         { . . . }
.entry bar .reqnctapercluster 2, 2, 1   { . . . }
.entry ker .reqnctapercluster 3, 2      { . . . }

11.7.2. Cluster 维度指示符:.explicitcluster#

.explicitcluster

声明 Kernel 必须在显式指定 cluster 维度的情况下启动。

.explicitcluster

声明本 Kernel 应当在显式指定 cluster 维度的情况下启动。

带有 .explicitcluster 指示符的 Kernel 必须在显式指定 cluster 维度的情况下 启动(在启动时指定,或通过 .reqnctapercluster 指定),否则程序将以运行时错误或 kernel 启动失败告终。

.entry foo .explicitcluster         { . . . }

11.7.3. Cluster 维度指示符:.maxclusterrank#

.maxclusterrank

声明可以成为 cluster 一部分的 CTA 最大数量。

.maxclusterrank n

声明允许成为 cluster 一部分的线程块(CTA)最大数量。

在 kernel 任一次调用中所指定的各 cluster 维度 CTA 数量的乘积, 必须小于或等于本指示符中指定的值。否则该调用会导致运行时错误或 kernel 启动失败。

.maxclusterrank 指示符不能与 .reqnctapercluster 指示符配合使用。

.entry foo ..maxclusterrank 8         { . . . }

11.8. 杂项指示符#

PTX 提供以下杂项指示符:

  • .blocksareclusters

  • .language

11.8.1. 杂项指示符:.blocksareclusters#

.blocksareclusters

指定 CUDA 线程块映射为 cluster。

.blocksareclusters

CUDA API 的默认行为是通过指定线程块数量和每个线程块的线程数量来指定 grid 启动配置。

指定 .blocksareclusters 指示符时,意味着相应 .entry 函数的 grid 启动配置 指定的是 cluster 数量,即启动配置指定的是 cluster 数量而不是线程块数量。这种情况下, 每个 cluster 中的线程块数量由 .reqnctapercluster 指示符指定,线程块大小由 .reqntid 指示符指定。

.blocksareclusters 指示符只允许用于 .entry 函数,并且还需要 指定 .reqntid 和 .reqnctapercluster 指示符。

更多细节参见CUDA 编程指南。

.entry foo .reqntid 32, 32, 1 .reqnctapercluster 32, 32, 1 .blocksareclusters { ... }

11.8.2. 杂项指示符:.language#

.language

指定用于生成 kernel 或函数的源代码语言。

.language lang-list   // comma separated list of strings or integers

.language 指示符声明相应 .entry 或 .func 函数的代码由之生成的源代码 语言。该指示符接受以逗号分隔的语言代码列表,可指定为带引号的字符串名称或无符号整数值。

.language 指示符对 .entry 和 .func 函数均允许使用,并且必须出现 在函数指示符与其函数体之间。

若未指定 .language 指示符,语言默认为 "ptx"。

单个函数可以指定多种语言,表示混合语言来源。 语言名称不区分大小写。

以无符号整数指定的语言代码必须位于 [0..10] 范围内。 字符串名称与整数代码之间的映射如下:

代码

字符串

0

“unknown”

1

保留

2

保留

3

“ptx”

4

“nvvm”

5

“cuda c++”

6

“cuda c++ tile”

7

“tile ir”

8

“python-cutile”

9

“fortran”

10

“optix”

.entry foo () .language "ptx"                    { ... }
.func bar ()  .language "nvvm", "ptx"            { ... }
.entry baz () .language "ptx", "cuda c++", 0x7   { ... }
EN