Go Wiki:AVX512
Go 1.11 发行版引入了 AVX-512 支持。
本页面介绍如何使用新功能以及一些重要的编码器详细信息。
术语
大多数术语来自 英特尔软件开发人员手册。
后缀源自 Go 汇编语法,它接近 AT&T,后者也使用大小后缀。
列出一些术语以避免歧义(例如,操作码可能具有不同的含义)。
术语 | 说明 |
---|---|
操作数 | 与“指令参数”相同。 |
操作码 |
指指令组的名称。例如,VADDPD 是一个操作码。它指 VEX 和 EVEX 编码形式以及所有操作数组合。 大多数适用于 AVX-512 的 Go 汇编操作码与英特尔手册条目匹配,但以下情况除外 使用附加大小后缀(例如, VCVTTPD2DQY 为 VCVTTPD2DQ )。
|
操作码后缀 |
覆盖某些操作码属性的后缀。列在“.”(点)之后。 例如, 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
寄存器。
- 如果删除
Z
操作码后缀,则使用K3
掩码进行合并掩码。 - 如果删除
K3
操作数,则会生成汇编器错误。 - 如果删除
Z
操作码后缀和K3
操作数,则使用K0
掩码进行合并掩码。
将 K0
寄存器用于 {k1}
操作数是编译时错误(有关详细信息,请参阅 手册)。
EVEX 广播/舍入/SAE 支持
通过操作码后缀激活嵌入式广播、舍入和 SAE。
对于启用了{er}
的 reg-reg FP 指令,可以指定舍入操作码后缀
RU_SAE
朝正无穷舍入RD_SAE
朝负无穷舍入RZ_SAE
朝零舍入RN_SAE
朝最近舍入
要了解有关舍入模式的更多信息,请参阅MXCSR.RC 信息。
对于启用了{sae}
的 reg-reg FP 指令,可以使用SAE
操作码后缀指定异常抑制。
对于带有m32bcst/m64bcst
操作数的 reg-mem 指令,可以使用BCST
操作码后缀打开广播。
清零操作码后缀可以与其中任何一个结合使用。
例如,VMAXPD.SAE.Z Z3, Z2, Z1
同时使用了Z
和SAE
操作码后缀。
重要的是将清零操作码后缀放在最后,否则会产生编译错误。
寄存器块(多源)操作数
使用寄存器范围语法指定寄存器块。
仅指定第一个(低)寄存器就足够了,但 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
。
也就是说,如果VADDPD
在avx512f
中可用,则不能在没有avx512vl
的情况下使用X
和Y
参数。
文件名遵循GNU as
(gas)约定。
avx512extmap.csv可以使命名方案更加明显。
带有大小后缀的指令
某些操作码与英特尔手册条目不匹配。
提供此部分是为了搜索方便。
英特尔操作码 | 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}
形式的 reg-reg 情况的指令,可能会出现此差异。其中一个这样的指令是 VMOVUPS
。
这不会影响代码行为,也不会使其更大/效率更低。
新的编码选择方案借鉴自 Intel XED。
在以下任何一种情况下都使用 EVEX 编码
- 指令使用新寄存器(高 16 个
X
/Y
、Z
或K
寄存器) - 指令使用 EVEX 相关的操作码后缀,如
BCST
- 指令使用仅适用于 AVX-512 的操作数组合
在所有其他情况下,使用 VEX 编码。
这意味着,在可能的情况下使用 VEX,在需要时使用 EVEX。
对于 EVEX 编码指令,在可能的情况下应用压缩 disp8。
这还涵盖广播 disp8,它有时具有不同的 N 乘数。
经验丰富的读者可以检查 avx_optabs.go 以了解任何指令的 N 乘数。
例如,VADDPD
具有以下内容
- 512 位形式的
N=64
;广播时的N=8
- 256 位形式的
N=32
;广播时的N=8
- 128 位形式的
N=16
;广播时的N=8
示例
可以在 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 的一部分。