Go Wiki: AVX512

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

术语

大部分术语来自 Intel 软件开发人员手册
后缀源自 Go 汇编语法,该语法接近 AT&T,后者也使用大小后缀。

列出了一些术语以避免歧义(例如,“opcode”可以有不同的含义)。

术语 描述
操作数 与“指令参数”相同。
Opcode (操作码) 指代指令组的名称。例如,VADDPD 是一个操作码。
它同时指代 VEX 和 EVEX 编码形式以及所有操作数组合。
大多数 AVX-512 的 Go 汇编操作码与 Intel 手册条目匹配,但存在例外情况
使用附加的大小后缀(例如,VCVTTPD2DQYVCVTTPD2DQ)。
Opcode 后缀 覆盖某些操作码属性的后缀。跟在 "." (点) 之后列出。
例如,VADDPD.Z 有 "Z" 操作码后缀。
可以有多个由点分隔的操作码后缀。
大小后缀 如果指令操作数的大小无法仅从操作数推断,则用于指定指令操作数大小的后缀。
例如,VCVTSS2USIL 有 "L" 大小后缀。
Opmask (操作掩码) 同时用于 {k1} 表示法和描述具有 K 寄存器操作数的指令。
与 EVEX 前缀中的掩码支持相关。
Register block (寄存器块) 对多源操作数进行编码以表示寄存器范围。
Intel 手册使用 +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 寄存器。

  • 如果删除 Z 操作码后缀,则为使用 K3 掩码的合并掩码。
  • 如果删除 K3 操作数,将产生汇编器错误。
  • 如果同时删除 Z 操作码后缀和 K3 操作数,则为使用 K0 掩码的合并掩码。

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

EVEX 广播/舍入/SAE 支持

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

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

  • RU_SAE 向 +无穷大舍入
  • RD_SAE 向 -无穷大舍入
  • RZ_SAE 向零舍入
  • RN_SAE 向最近的整数舍入

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

对于启用 {sae} 的寄存器-寄存器 FP 指令,可以通过 SAE 操作码后缀指定异常抑制。

对于具有 m32bcst/m64bcst 操作数的寄存器-内存指令,可以通过 BCST 操作码后缀开启广播。

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

Register block (multi-source) operands (寄存器块(多源)操作数)

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

指定第一个(低位)寄存器就足够了,但出于可读性考虑,Go 汇编器要求显式指定两端的范围。

例如,范围为 +3 的指令可以像这样使用:VP4DPWSSD Z25, [Z0-Z3], (AX)
范围 [Z0-Z3] 表示“寄存器块 Z0, Z1, Z2, Z3”。
无效的范围会导致编译错误。

AVX1 和 AVX2 指令与 EVEX 前缀

之前存在的可以用 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 可以使命名方案更加清晰。

带大小后缀的指令

某些操作码与 Intel 手册条目不匹配。
本节提供搜索便利。

Intel 操作码 Go 汇编器操作码
VCVTPD2DQ VCVTPD2DQX, VCVTPD2DQY
VCVTPD2PS VCVTPD2PSX, VCVTPD2PSY
VCVTTPD2DQ VCVTTPD2DQX, VCVTTPD2DQY
VCVTQQ2PS VCVTQQ2PSX, VCVTQQ2PSY
VCVTUQQ2PS VCVTUQQ2PSX, VCVTUQQ2PSY
VCVTPD2UDQ VCVTPD2UDQX, VCVTPD2UDQY
VCVTTPD2UDQ VCVTTPD2UDQX, VCVTTPD2UDQY
VFPCLASSPD VFPCLASSPDX, VFPCLASSPDY, VFPCLASSPDZ
VFPCLASSPS VFPCLASSPSX, VFPCLASSPSY, VFPCLASSPSZ
VCVTSD2SI VCVTSD2SI, VCVTSD2SIQ
VCVTTSD2SI VCVTSD2SI, VCVTSD2SIQ
VCVTTSS2SI VCVTSD2SI, VCVTSD2SIQ
VCVTSS2SI VCVTSD2SI, VCVTSD2SIQ
VCVTSD2USI VCVTSD2USIL, VCVTSD2USIQ
VCVTSS2USI VCVTSS2USIL, VCVTSS2USIQ
VCVTTSD2USI VCVTTSD2USIL, VCVTTSD2USIQ
VCVTTSS2USI VCVTTSS2USIL, VCVTTSS2USIQ
VCVTUSI2SD VCVTUSI2SDL, VCVTUSI2SDQ
VCVTUSI2SS VCVTUSI2SSL, VCVTUSI2SSQ
VCVTSI2SD VCVTSI2SDL, VCVTSI2SDQ
VCVTSI2SS VCVTSI2SSL, VCVTSI2SSQ
ANDN ANDNL, ANDNQ
BEXTR BEXTRL, BEXTRQ
BLSI BLSIL, BLSIQ
BLSMSK BLSMSKL, BLSMSKQ
BLSR BLSRL, BLSRQ
BZHI BZHIL, BZHIQ
MULX MULXL, MULXQ
PDEP PDEPL, PDEPQ
PEXT PEXTL, PEXTQ
RORX RORXL, RORXQ
SARX SARXL, SARXQ
SHLX SHLXL, SHLXQ
SHRX SHRXL, SHRXQ

编码器细节

由于编码器表顺序略有不同,与旧编码器的位比较可能会因 VEX 编码的指令而失败。

这种差异可能出现在同时具有 {reg, reg/mem}{reg/mem, reg} 形式的寄存器-寄存器指令中。这些指令之一是 VMOVUPS

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

当以下任一条件成立时,将使用 EVEX 编码:

  • 指令使用新寄存器(高 16 个 X/YZK 寄存器)
  • 指令使用与 EVEX 相关的操作码后缀,如 BCST
  • 指令使用仅适用于 AVX-512 的操作数组合

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

对于 EVEX 编码的指令,尽可能应用压缩 disp8。
这也包括 disp8 的广播,有时具有不同的 N 乘数。

有经验的读者可以查看 avx_optabs.go 来了解任何指令的 N 乘数。

例如,VADDPD 有以下 N 乘数:

  • 512 位形式的 N=64;广播时 N=8
  • 256 位形式的 N=32;广播时 N=8
  • 128 位形式的 N=16;广播时 N=8

示例

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

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

这是从 Intel® 优化手册 采用的“使用 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 的一部分。