Go Wiki: AVX512

Go 1.11のリリースでは、AVX-512のサポートが導入されました。
このページでは、新機能の使用方法と、重要なエンコーダの詳細について説明します。

用語

ほとんどの用語は、Intel Software Developer’s manualに由来しています。
接尾辞はGoアセンブラ構文に由来し、これはAT&Tに近く、サイズ接尾辞も使用します。

いくつかの用語は、曖昧さを避けるためにリストされています(例:オペコードは異なる意味を持つことがあります)。

用語 説明
オペランド 「命令引数」と同じ。
オペコード 命令グループを参照する名前。例えば、VADDPDはオペコードです。
VEXとEVEXの両方のエンコード形式と、すべてのオペランドの組み合わせを参照します。
AVX-512のほとんどのGoアセンブラのオペコードはIntelマニュアルのエントリと一致しますが、
追加のサイズ接尾辞が使用される場合(例:VCVTTPD2DQYVCVTTPD2DQ)は例外です。
オペコード接尾辞 いくつかのオペコードプロパティを上書きする接尾辞。「.」(ドット)の後にリストされます。
例えば、VADDPD.Zには「Z」オペコード接尾辞があります。
複数のドット区切りのオペコード接尾辞がある場合があります。
サイズ接尾辞 オペランドのみから命令オペランドサイズを推測できない場合に、それを指定する接尾辞。
例えば、VCVTSS2USILには「L」サイズ接尾辞があります。
オペマスク {k1}表記と、Kレジスタオペランドを持つ命令を記述する両方に使用されます。
EVEXプレフィックスのマスキングサポートに関連します。
レジスタブロック レジスタ範囲をエンコードするマルチソースオペランド。
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レジスタが暗示され(「all ones」)、マージングマスキングが実行されます。
これは実質的に「マスキングなし」です。

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は+Infに丸める
  • RD_SAEは-Infに丸める
  • RZ_SAEはゼロに丸める
  • RN_SAEは最も近い値に丸める

丸めモードの詳細については、MXCSR.RC情報を参照してください。

{sae}が有効なreg-reg FP命令の場合、SAEオペコード接尾辞で例外抑制を指定できます。

m32bcst/m64bcstオペランドを持つreg-mem命令の場合、BCSTオペコード接尾辞でブロードキャストをオンにできます。

ゼロ化オペコード接尾辞は、これらのいずれかと組み合わせることができます。
例えば、VMAXPD.SAE.Z Z3, Z2, Z1ZSAEの両方のオペコード接尾辞を使用します。
ゼロ化オペコード接尾辞を最後に置くことが重要です。そうしないとコンパイルエラーになります。

レジスタブロック(マルチソース)オペランド

レジスタブロックは、レジスタ範囲構文を使用して指定されます。

最初の(低い)レジスタを指定するだけで十分ですが、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なしではXおよびY引数を使用できません。

ファイル名は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の場合に{reg, reg/mem}{reg/mem, reg}の両方の形式を持つ命令で発生する可能性があります。そのような命令の1つがVMOVUPSです。

これはコードの動作に影響せず、大きくしたり非効率にしたりすることはありません。
新しいエンコード選択スキームは、Intel XEDから借用されています。

EVEXエンコーディングは、以下のいずれかが真の場合に使用されます。

  • 命令が新しいレジスタ(上位16個のX/YZまたはKレジスタ)を使用する場合
  • 命令がBCSTなどのEVEX関連のオペコード接尾辞を使用する場合
  • 命令がAVX-512でのみ利用可能なオペランドの組み合わせを使用する場合

その他のすべてのケースでは、VEXエンコーディングが使用されます。
これは、可能な限りVEXが使用され、必要な場合にEVEXが使用されることを意味します。

EVEXエンコード命令には、可能な限り圧縮されたdisp8が適用されます。
これには、N乗数が異なる場合があるブロードキャストdisp8も含まれます。

経験豊富な読者は、avx_optabs.goを調べて、任意の命令のN乗数について学ぶことができます。

例えば、VADDPDにはこれらがあります。

  • 512ビット形式の場合はN=64。ブロードキャスト時はN=8
  • 256ビット形式の場合はN=32。ブロードキャスト時はN=8
  • 128ビット形式の場合はN=16。ブロードキャスト時はN=8

網羅的な例は、Goアセンブラのテストスイートにあります。

各ファイルは、特定のAVX-512拡張機能でサポートされているすべての命令形式について、いくつかの例を提供します。
すべての例には、生成されたマシンコードも含まれています。

以下は、Intel® Optimization Manualから採用された「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の一部です。