Go Wiki:AVX512

Go 1.11 发行版引入了 AVX-512 支持。
本页面介绍如何使用新功能以及一些重要的编码器详细信息。

术语

大多数术语来自 英特尔软件开发人员手册
后缀源自 Go 汇编语法,它接近 AT&T,后者也使用大小后缀。

列出一些术语以避免歧义(例如,操作码可能具有不同的含义)。

术语 说明
操作数 与“指令参数”相同。
操作码 指指令组的名称。例如,VADDPD 是一个操作码。
它指 VEX 和 EVEX 编码形式以及所有操作数组合。
大多数适用于 AVX-512 的 Go 汇编操作码与英特尔手册条目匹配,但以下情况除外
使用附加大小后缀(例如,VCVTTPD2DQYVCVTTPD2DQ)。
操作码后缀 覆盖某些操作码属性的后缀。列在“.”(点)之后。
例如,VADDPD.Z 具有“Z”操作码后缀。
可以有多个以点分隔的操作码后缀。
大小后缀 如果无法仅从操作数推断出指令操作数大小,则指定该大小的后缀。
例如,VCVTSS2USIL 具有“L”大小后缀。
操作掩码 用于 {k1} 表示法和描述具有 K 寄存器操作数的指令。
与 EVEX 前缀中的掩码支持相关。
寄存器块 对寄存器范围进行编码的多源操作数。
英特尔手册对寄存器块使用 +n 表示法。
例如,+3 是 4 个寄存器的寄存器块。
FP 浮点

新寄存器

启用 EVEX 的指令可以访问 64 位模式下的另外 16 个 X(128 位 xmm)和 Y(256 位 ymm)寄存器,以及 32 个新的 Z(512 位 zmm)寄存器。32 位模式仅获取 Z0-Z7

新操作掩码寄存器命名为 K0-K7
它们可用于掩码和特殊操作掩码指令(如 KADDB)。

掩码支持

支持掩码的指令可以省略 K 寄存器操作数。
在这种情况下,隐含 K0 寄存器(“全部为 1”)并执行合并掩码。
这实际上是“无掩码”。

K1-K7 寄存器可用于覆盖默认操作掩码。
K 寄存器应放在目标操作数之前。

可以使用 Z 操作码后缀激活归零掩码。归零掩码要求指定 K0 以外的掩码寄存器。

例如,VADDPD.Z (AX), Z30, K3, Z10 使用归零掩码和显式 K 寄存器。

K0 寄存器用于 {k1} 操作数是编译时错误(有关详细信息,请参阅 手册)。

EVEX 广播/舍入/SAE 支持

通过操作码后缀激活嵌入式广播、舍入和 SAE。

对于启用了{er}的 reg-reg FP 指令,可以指定舍入操作码后缀

要了解有关舍入模式的更多信息,请参阅MXCSR.RC 信息

对于启用了{sae}的 reg-reg FP 指令,可以使用SAE操作码后缀指定异常抑制。

对于带有m32bcst/m64bcst操作数的 reg-mem 指令,可以使用BCST操作码后缀打开广播。

清零操作码后缀可以与其中任何一个结合使用。
例如,VMAXPD.SAE.Z Z3, Z2, Z1同时使用了ZSAE操作码后缀。
重要的是将清零操作码后缀放在最后,否则会产生编译错误。

寄存器块(多源)操作数

使用寄存器范围语法指定寄存器块。

仅指定第一个(低)寄存器就足够了,但 Go 汇编器出于可读性原因需要使用两端明确指定范围。

例如,带有+3范围的指令可以使用类似VP4DPWSSD Z25, [Z0-Z3], (AX)的指令。
范围[Z0-Z3]读作“Z0、Z1、Z2、Z3 的寄存器块”。
无效范围会导致编译错误。

带有 EVEX 前缀的 AVX1 和 AVX2 指令

以前存在的可以使用 EVEX 前缀编码的操作码现在可以访问 AVX-512 特性,例如更宽的寄存器文件、清零/合并掩码等。例如,VADDPD现在可以使用 512 位向量寄存器。

有关更多信息,请参阅编码器详细信息

受支持的扩展

获取受支持扩展的最新列表的最佳方法是在测试套件目录中执行ls -1

最新列表包括

aes_avx512f
avx512_4fmaps
avx512_4vnniw
avx512_bitalg
avx512_ifma
avx512_vbmi
avx512_vbmi2
avx512_vnni
avx512_vpopcntdq
avx512bw
avx512cd
avx512dq
avx512er
avx512f
avx512pf
gfni_avx512f
vpclmulqdq_avx512f

128 位和 256 位指令还额外需要avx512vl
也就是说,如果VADDPDavx512f中可用,则不能在没有avx512vl的情况下使用XY参数。

文件名遵循GNU as(gas)约定。
avx512extmap.csv可以使命名方案更加明显。

带有大小后缀的指令

某些操作码与英特尔手册条目不匹配。
提供此部分是为了搜索方便。

英特尔操作码 Go 汇编器操作码
VCVTPD2DQ VCVTPD2DQXVCVTPD2DQY
VCVTPD2PS VCVTPD2PSXVCVTPD2PSY
VCVTTPD2DQ VCVTTPD2DQXVCVTTPD2DQY
VCVTQQ2PS VCVTQQ2PSXVCVTQQ2PSY
VCVTUQQ2PS VCVTUQQ2PSXVCVTUQQ2PSY
VCVTPD2UDQ VCVTPD2UDQXVCVTPD2UDQY
VCVTTPD2UDQ VCVTTPD2UDQXVCVTTPD2UDQY
VFPCLASSPD VFPCLASSPDXVFPCLASSPDYVFPCLASSPDZ
VFPCLASSPS VFPCLASSPSXVFPCLASSPSYVFPCLASSPSZ
VCVTSD2SI VCVTSD2SIVCVTSD2SIQ
VCVTTSD2SI VCVTSD2SIVCVTSD2SIQ
VCVTTSS2SI VCVTSD2SIVCVTSD2SIQ
VCVTSS2SI VCVTSD2SIVCVTSD2SIQ
VCVTSD2USI VCVTSD2USILVCVTSD2USIQ
VCVTSS2USI VCVTSS2USILVCVTSS2USIQ
VCVTTSD2USI VCVTTSD2USILVCVTTSD2USIQ
VCVTTSS2USI VCVTTSS2USILVCVTTSS2USIQ
VCVTUSI2SD VCVTUSI2SDLVCVTUSI2SDQ
VCVTUSI2SS VCVTUSI2SSLVCVTUSI2SSQ
VCVTSI2SD VCVTSI2SDLVCVTSI2SDQ
VCVTSI2SS VCVTSI2SSLVCVTSI2SSQ
ANDN ANDNLANDNQ
BEXTR BEXTRLBEXTRQ
BLSI BLSILBLSIQ
BLSMSK BLSMSKLBLSMSKQ
BLSR BLSRLBLSRQ
BZHI BZHILBZHIQ
MULX MULXLMULXQ
PDEP PDEPLPDEPQ
PEXT PEXTLPEXTQ
RORX RORXLRORXQ
SARX SARXLSARXQ
SHLX SHLXLSHLXQ
SHRX SHRXLSHRXQ

编码器详细信息

由于编码器表顺序略有不同,因此使用较旧编码器进行按位比较可能会导致 VEX 编码指令失败。

对于具有 {reg, reg/mem}{reg/mem, reg} 形式的 reg-reg 情况的指令,可能会出现此差异。其中一个这样的指令是 VMOVUPS

这不会影响代码行为,也不会使其更大/效率更低。
新的编码选择方案借鉴自 Intel XED

在以下任何一种情况下都使用 EVEX 编码

在所有其他情况下,使用 VEX 编码。
这意味着,在可能的情况下使用 VEX,在需要时使用 EVEX。

对于 EVEX 编码指令,在可能的情况下应用压缩 disp8。
这还涵盖广播 disp8,它有时具有不同的 N 乘数。

经验丰富的读者可以检查 avx_optabs.go 以了解任何指令的 N 乘数。

例如,VADDPD 具有以下内容

示例

可以在 Go 汇编器 测试套件 中找到大量的示例。

每个文件都为特定 AVX-512 扩展中的每个受支持指令形式提供了多个示例。
每个示例还包括生成的机器代码。

以下是 英特尔® 优化手册 中采用的“使用 AVX-512CD 的矢量化直方图更新”。

for i := 0; i < 512; i++ {
  histo[key[i]] += 1
}
top:
        VMOVUPS     0x40(SP)(DX*4), Z4  //; vmovups     zmm4, [rsp+rdx*4+0x40]
        VPXORD      Z1, Z1, Z1          //; vpxord      zmm1, zmm1, zmm1
        KMOVW       K1, K2              //; kmovw       k2, k1
        VPCONFLICTD Z4, Z2              //; vpconflictd zmm2, zmm4
        VPGATHERDD  (AX)(Z4*4), K2, Z1  //; vpgatherdd  zmm1{k2}, [rax+zmm4*4]
        VPTESTMD    histo<>(SB), Z2, K0 //; vptestmd    k0, zmm2, [rip+0x185c]
        KMOVW       K0, CX              //; kmovw       ecx, k0
        VPADDD      Z0, Z1, Z3          //; vpaddd      zmm3, zmm1, zmm0
        TESTL       CX, CX              //; test        ecx, ecx
        JZ          noConflicts         //; jz          noConflicts
        VMOVUPS     histo<>(SB), Z1     //; vmovups     zmm1, [rip+0x1884]
        VPTESTMD    histo<>(SB), Z2, K0 //; vptestmd    k0, zmm2, [rip+0x18ba]
        VPLZCNTD    Z2, Z5              //; vplzcntd    zmm5, zmm2
        XORB        BX, BX              //; xor         bl, bl
        KMOVW       K0, CX              //; kmovw       ecx, k0
        VPSUBD      Z5, Z1, Z1          //; vpsubd      zmm1, zmm1, zmm5
        VPSUBD      Z5, Z1, Z1          //; vpsubd      zmm1, zmm1, zmm5

resolveConflicts:
        VPBROADCASTD CX, Z5     //; vpbroadcastd zmm5, ecx
        KMOVW CX, K2            //; kmovw        k2, ecx
        VPERMD Z3, Z1, K2, Z3   //; vpermd       zmm3{k2}, zmm1, zmm3
        VPADDD Z0, Z3, K2, Z3   //; vpaddd       zmm3{k2}, zmm3, zmm0
        VPTESTMD Z2, Z5, K2, K0 //; vptestmd     k0{k2}, zmm5, zmm2
        KMOVW K0, SI            //; kmovw        esi, k0
        ANDL SI, CX             //; and          ecx, esi
        JZ noConflicts          //; jz           noConflicts
        ADDB $1, BX             //; add          bl, 0x1
        CMPB BX, $16            //; cmp          bl, 0x10
        JB resolveConflicts     //; jb           resolveConflicts

noConflicts:
        KMOVW       K1, K2             //; kmovw       k2, k1
        VPSCATTERDD Z3, K2, (AX)(Z4*4) //; vpscatterdd [rax+zmm4*4]{k2}, zmm3
        ADDL        $16, DX            //; add         edx, 0x10
        CMPL        DX, $1024          //; cmp         edx, 0x400
        JB          top                //; jb          top

此内容是 Go Wiki 的一部分。