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 的一部分。