AVX2は256bit幅(double 4要素、float 8要素)、AVX512は512bit幅(double 8要素、float 16要素)のレジスタ操作が可能。レジスタ内での水平方向の並べ替えや選択ができるunpack命令,blend命令,shuffle命令,permute命令をまとめた際のメモ。
Intel® Intrinsics Guideに命令一覧があり、動作も一応書いているが実際に動かさないとよくわからないので動かしてみた。いくらかはIntel compilerでしか使えないもの(特に数学関数関連)、Xeon PhiのようなMIC環境でしか使えないもの(特にshuffle関数関連)、特定のAVX512環境でしか使えない命令(精度依存の関数)もあるのでコンパイルできなかったらサイトで対応アーキテクチャの確認をする。
動作確認
関数の数が多くわかりやすい図は作っていないのでインプットとアウトプットの数値を示す。使ったcodeは下のほうに記載。整数immediate (imm) は2bitが直感的にわかりやすいので使っているが10進数でも16進数でもいい。
unpack 命令
- 2つのレジスタから特定の要素を選ぶ
unpacklo
AVX2
- 256bitのレジスタを下位128bit, 上位128bitにわけた際にそれぞれの下位64bitから値を選択
- 以降、
src0
は第一引数a
,src1
は第二引数b
に対応。返り値はdst
- カンマを区切りがわかりやすいように適当に入れている
__m256d _mm256_unpacklo_pd (__m256d a, __m256d b)
__m256 _mm256_unpacklo_ps (__m256 a, __m256 b)
// double
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_unpacklo_pd
dst : 12 2 , 10 0
// float
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_unpacklo_ps
dst : 15 5 14 4 , 11 1 10 0
AVX512
- 512bitのレジスタを4つの128bitにわけた際にそれぞれの下位64bitから値を選択
__m512d _mm512_unpacklo_pd (__m512d a, __m512d b)
__m256 _mm512_unpacklo_ps (__m512 a, __m512 b)
// double
src0 : 7 6 , 5 4 , 3 2 , 1 0
src1 : 17 16 , 15 14 , 13 12 , 11 10
_mm512_unpacklo_pd
dst : 16 6 , 14 4 , 12 2 , 10 0
// float
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
src1 : 35 34 33 32 , 31 30 29 28 , 27 26 25 24 , 23 22 21 20
_mm512_unpacklo_ps
dst : 33 13 32 12 , 29 9 28 8 , 25 5 24 4 , 21 1 20 0
unpackhi
AVX2
- 256bitのレジスタを下位128bit, 上位128bitにわけた際にそれぞれの上位64bitから値を選択
__m256d _mm256_unpackhi_pd (__m256d a, __m256d b)
__m256 _mm256_unpackhi_ps (__m256 a, __m256 b)
// double
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_unpackhi_pd
dst : 13 3 , 11 1
// float
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_unpackhi_ps
dst : 17 7 16 6 , 13 3 12 2
AVX512
- 512bitのレジスタを4つの128bitにわけた際にそれぞれの上位64bitから値を選択
__m512d _mm512_unpackhi_pd (__m512d a, __m512d b)
__m256 _mm512_unpackhi_ps (__m512 a, __m512 b)
// double
src0 : 7 6 , 5 4 , 3 2 , 1 0
src1 : 17 16 , 15 14 , 13 12 , 11 10
_mm512_unpackhi_pd
dst : 17 7 , 15 5 , 13 3 , 11 1
// float
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
src1 : 35 34 33 32 , 31 30 29 28 , 27 26 25 24 , 23 22 21 20
_mm512_unpackhi_ps
dst : 35 15 34 14 , 31 11 30 10 , 27 7 26 6 , 23 3 22 2
blend 命令
- 2つのレジスタから要素を選ぶ
- レジスタに入る順番は固定
blend
- imm は整数値
- bitが 0 なら src0 , 1 なら src1 を選択
- 整数値指定の AVX512 blend 命令はなし. mask 指定のみ
AVX2
__m256d _mm256_blend_pd (__m256d a, __m256d b, const int imm8)
__m256 _mm256_blend_ps (__m256 a, __m256 b, const int imm8)
// double
src0 : 3 2 1 0
src1 : 13 12 11 10
_mm256_blend_pd(imm=0b1010)
dst : 13 2 11 0
// 8bit imm のうち下位4bitのみ有効
// float
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm256_blend_ps(imm=0b00010111)
dst : 7 6 5 14 3 12 11 10
blendv
- imm はマスク
- bitが 0 なら src0 , 1 なら src1 を選択
AVX2
__m256d _mm256_blendv_pd (__m256d a, __m256d b, __m256d mask)
__m256 _mm256_blendv_ps (__m256 a, __m256 b, __m256 mask)
// double
src0 : 3 2 1 0
src1 : 13 12 11 10
_mm256_blendv_pd(mask=_mm256_set_pd(0,-0.0,0,-0.0))
dst : 3 12 1 10
// float
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm256_blendv_ps(mask=_mm256_set_ps(-0.0f,0,-0.0f,0,0,-0.0f,-0.0f,0))
dst : 17 6 15 4 3 12 11 0
- マスクでは最上位bitが0か1かをみるので -0.0 (double), -0.0f (float) で1埋め
- 浮動小数点数では負にすれば最上位ビット(符号ビット)は1
blend_mask
AVX512
- AVX2 とマスクの位置が違うのに注意
__m512d _mm512_mask_blend_pd (__mmask8 k, __m512d a, __m512d b)
__m512 _mm512_mask_blend_ps (__mmask16 k, __m512 a, __m512 b)
// double
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm512_mask_blend_pd(mask=0b10100101)
dst : 17 6 15 4 3 12 1 10
// float
src0 : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
src1 : 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20
_mm512_mask_blend_ps(mask=0b1010010100110101)
dst : 35 14 33 12 11 30 9 28 7 6 25 24 3 22 1 20
mask move (AVX512)
- AVX512では blend命令ではないがマスク付きmove命令で同様のことが実現可能
-
move
でなくmov
- mask_blend とマスクの引数の位置が違うのに注意
-
__m512d _mm512_mask_mov_pd (__m512d a, __mmask8 k, __m512d b)
__m512 _mm512_mask_mov_ps (__m512 a, __mmask16 k, __m512 b)
// double
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm512_mask_mov_pd(mask=0b10100101)
dst : 17 6 15 4 3 12 1 10
// float
src0 : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
src1 : 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20
_mm512_mask_mov_ps(mask=0b1010010100110101)
dst : 35 14 33 12 11 30 9 28 7 6 25 24 3 22 1 20
- zeromask blend命令は存在しないが zeromask moveならある
__m512d _mm512_maskz_mov_pd (__mmask8 k, __m512d a)
__m512 _mm512_maskz_mov_ps (__mmask16 k, __m512 a)
// double
src1 : 17 16 15 14 13 12 11 10
_mm512_maskz_mov_pd(mask=0b10100101)
dst : 17 0 15 0 0 12 0 10
// float
src1 : 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20
_mm512_maskz_mov_ps(mask=0b1010010100110101)
dst : 35 0 33 0 0 30 0 28 0 0 25 24 0 22 0 20
shuffle 命令
- permuteと似ているがdstのどの要素にsrc0,src1のどの要素を入るべきかがある程度決まっている(自由度が少ない)
- 同じ命令名でも float と double によって振る舞いが違うので注意
- 更にAVX2とAVX512で振る舞いが違うので注意
- 基本的にAVX2では下位半分要素、上位半分要素は混じらない
- 基本的にAVX512では128bit区切の要素同士は混じらない
_MM_SHUFFLE()
マクロ
- 10進数16進数指定がわかりにくいのと、環境によっては2進数指定ができないので(わかりやすい)マクロが用意されている
- 10進数指定で0から3までの値を
_MM_SHUFFLE()
に4つ入れる - 返り値が4要素単位の指定には便利だがそれ以外だと逆にわかりにくい
-
_mm256_shuffle_ps
,_mm512_shuffle_ps
,_mm512_shuffle_f64x2
,_mm512_shuffle_f32x4
には有用 - それ以外は使えない
-
_MM_SHUFFLE(D,C,B,A) = 0bddccbbaa
shuffle
AVX2
__m256d _mm256_shuffle_pd (__m256d a, __m256d b, const int imm8)
__m256 _mm256_shuffle_ps (__m256 a, __m256 b, const int imm8)
double (AVX2)
- どの要素が入るかはだいたい決まっている
- imm[0]でsrc0の[1:0]要素のどちらか
- imm[1]でsrc1の[1:0]要素のどちらか
- imm[2]でsrc0の[3:2]要素のどちらか
- imm[3]でsrc1の[3:2]要素のどちらか
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_shuffle_pd(imm=0b1101)
dst : 13 3 , 10 1
// imm[0]により src0 下位128bitからのどちらかを選択。この場合はimm[0]=1なので1
// imm[1]により src1 下位128bitからのどちらかを選択。この場合はimm[1]=0なので10
// imm[2]により src0 上位128bitからのどちらかを選択。この場合はimm[2]=1なので3
// imm[3]により src1 上位128bitからのどちらかを選択。この場合はimm[3]=1なので13
_mm256_shuffle_pd(imm=0xf=_MM_SHUFFLE(3,3,3,3)=0b1111)
dst : 13 3 , 11 1
// この場合は全て1なので上位側を選択
// この場合はあまりマクロが適さない
float (AVX2)
- どの要素が入るかはだいたい決まっているがdoubleの場合と違うので注意
- imm[1:0]でsrc0の[3:0]要素のどれかを選択
- imm[3:2]でsrc0の[3:0]要素のどれかを選択
- imm[5:4]でsrc1の[3:0]要素のどれかを選択
- imm[7:6]でsrc1の[3:0]要素のどれかを選択
- imm[1:0]でsrc0の[7:4]要素のどれかを選択 ...以下繰り返し
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_shuffle_ps(imm=0b10001101)
dst : 16 14 7 5 , 12 10 3 1
// imm指定は dst に入る下位128bitと上位128bitに入るパターンを使いまわし
// imm[1:0]が01なので src0 の下位128bitのfloat[1]の1
// imm[3:2]が11なので src0 の下位128bitのfloat[3]の3
// imm[5:4]が00なので src1 の下位128bitのfloat[0]の10
// imm[7:6]が10なので src1 の下位128bitのfloat[2]の12
// 上位128bitも下位bitと同じ規則で値が入る
_mm256_shuffle_ps(imm=_MM_SHUFFLE(2,1,1,3)=0b10010111)
dst : 16 15 5 7 , 12 11 1 3
// この場合はマクロが見やすい
// _MM_SHUFFLE(src1[2],src1[1],src0[1],src0[3])
AVX512
__m512d _mm512_shuffle_pd (__m512d a, __m512d b, const int imm8)
__m512 _mm512_shuffle_ps (__m512 a, __m512 b, const int imm8)
double (AVX512)
_mm512_shuffle_pd(imm=0b10010110)
src0 : 7 6 , 5 4 , 3 2 , 1 0
src1 : 17 16 , 15 14 , 13 12 , 11 10
dst : 17 6 , 14 5 , 12 3 , 11 0
// 立っているbitに応じて各々の上位か下位の値が選ばれる
// _mm256_shuffle_psとは上位と下位を使いまわさないところが大きく違う
// _MM_SHUFFLEマクロは適さない
_mm512_shuffle_pd(imm=0b01110011)
src0 : 7 6 , 5 4 , 3 2 , 1 0
src1 : 17 16 , 15 14 , 13 12 , 11 10
dst : 16 7 , 15 5 , 12 2 , 11 1
float (AVX512)
_mm512_shuffle_ps(imm=0b10001101)
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
src1 : 35 34 33 32 , 31 30 29 28 , 27 26 25 24 , 23 22 21 20
dst : 34 32 15 13 , 30 28 11 9 , 26 24 7 5 , 22 20 3 1
// imm[1:0]が 01bitなので src0からfloat[1]の1を選択
// imm[3:2]が 11bitなので src0からfloat[3]の3を選択
// imm[5:4]が 00bitなので src1からfloat[0]の20を選択
// imm[7:6]が 10bitなので src1からfloat[2]の22を選択
// 以降、このパターンで128bitずつ選択
_mm512_shuffle_ps(imm=_MM_SHUFFLE(2,1,3,3)=0b10011111)
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
src1 : 35 34 33 32 , 31 30 29 28 , 27 26 25 24 , 23 22 21 20
dst : 34 33 15 15 , 30 29 11 11 , 26 25 7 7 , 22 21 3 3
// _MM_SHUFFLEマクロは使おうと思えば使える
// _MM_SHUFFLE(src1[2],src1[1],src0[3],src0[3])
shuffle_{f64x2,f32x4}
- 似たような命令に
shuffle_f64x2, shuffle_f32x4
が存在 - 通常のshuffleよりまとまった要素が動く
- 常に下位半分bitにはsrc0のどれか, 上位半分bitにはsrc1のどれかが入る
AVX2
- AVX512VLでサポート
__m256d _mm256_shuffle_f64x2 (__m256d a, __m256d b, const int imm8)
__m256 _mm256_shuffle_f32x4 (__m256 a, __m256 b, const int imm8)
double (AVX2)
- immは2bitで指定
- doubleが2個セット(128bit)単位で選択される. カンマで一区切り
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_shuffle_f64x2(imm=0b00)
dst : 11 10 , 1 0
_mm256_shuffle_f64x2(imm=0b01)
dst : 11 10 , 3 2
_mm256_shuffle_f64x2(imm=0b10)
dst : 13 12 , 1 0
_mm256_shuffle_f64x2(imm=0b11)
dst : 13 12 , 3 2
float (AVX2)
- immは2bitで指定
- floatが4個セット(128bit)単位で選択される. カンマで一区切り
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_shuffle_f32x4(imm=0b00)
dst : 13 12 11 10 , 3 2 1 0
_mm256_shuffle_f32x4(imm=0b01)
dst : 13 12 11 10 , 7 6 5 4
_mm256_shuffle_f32x4(imm=0b10)
dst : 17 16 15 14 , 3 2 1 0
_mm256_shuffle_f32x4(imm=0b11)
dst : 17 16 15 14 , 7 6 5 4
AVX512
- AVX512Fでサポート
- 自由度が256bitより大きいためimmは8bit(2bitずつの4要素)
- 下位半分要素、上位半分要素は混じらない
- 下位、上位半分要素内では128bit区切の要素同士は混じってもよい
__m512d _mm512_shuffle_f64x2 (__m512d a, __m512d b, const int imm8)
__m512 _mm512_shuffle_f32x4 (__m512 a, __m512 b, const int imm8)
double (AVX512)
- doubleが2個セット(128bit)単位で選択される. カンマで一区切り
src0 : 7 6 , 5 4 , 3 2 , 1 0
src1 : 17 16 , 15 14 , 13 12 , 11 10
_mm512_shuffle_f64x2(imm=0b00000000)
dst : 11 10 , 11 10 , 1 0 , 1 0
_mm512_shuffle_f64x2(imm=0b01011101)
dst : 13 12 , 13 12 , 7 6 , 3 2
_mm512_shuffle_f64x2(imm=_MM_SHUFFLE(3,0,3,0)=0b11001100)
dst : 17 16 , 11 10 , 7 6 , 1 0
_mm512_shuffle_f64x2(imm=_MM_SHUFFLE(3,3,3,3)=0b11111111)
dst : 17 16 , 17 16 , 7 6 , 7 6
float (AVX512)
- floatが4個セット(128bit)単位で選択される. カンマで一区切り
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
src1 : 35 34 33 32 , 31 30 29 28 , 27 26 25 24 , 23 22 21 20
_mm512_shuffle_f32x4(imm=0b00000000)
dst : 23 22 21 20 , 23 22 21 20 , 3 2 1 0 , 3 2 1 0
_mm512_shuffle_f32x4(imm=0b01011101)
dst : 27 26 25 24 , 27 26 25 24 , 15 14 13 12 , 7 6 5 4
_mm512_shuffle_f32x4(imm=_MM_SHUFFLE(3,0,3,0)=0b11001100)
dst : 35 34 33 32 , 23 22 21 20 , 15 14 13 12 , 3 2 1 0
_mm512_shuffle_f32x4(imm=_MM_SHUFFLE(3,3,3,3)=0b11111111)
dst : 35 34 33 32 , 35 34 33 32 , 15 14 13 12 , 15 14 13 12
permute 命令
- 1つのレジスタ内の要素の並び替え
- 2つのレジスタの要素を任意に選んで並べ替え
- shuffle命令より柔軟に並べ替えが可能
permute
- 1つのレジスタ内の要素の並び替え
- 上位bitと下位bitが別れている
AVX2
__m256d _mm256_permute_pd (__m256d a, int imm8)
__m256 _mm256_permute_ps (__m256 a, int imm8)
double (AVX2)
src0 : 3 2 , 1 0
_mm256_permute_pd(imm=0b1010)
dst : 3 2 , 1 0
// imm[0]により下位半分の0か1を選択. ここでは0なので0
// imm[1]により下位半分の0か1を選択. ここでは1なので1
// imm[2]により上位半分の2か3を選択. ここでは0なので2
// imm[3]により上位半分の2か3を選択. ここでは1なので3
_mm256_permute_pd(imm=0b1111)
dst : 3 3 , 1 1
float (AVX2)
src0 : 7 6 5 4 , 3 2 1 0
_mm256_permute_ps(imm=0b01011010)
dst : 5 5 6 6 , 1 1 2 2
// imm[1:0]により下位半分の4つのどれかを選択. ここでは10なので2
// imm[3:2]により下位半分の4つのどれかを選択. ここでは10なので2
// imm[5:4]により下位半分の4つのどれかを選択. ここでは01なので1
// imm[7:6]により下位半分の4つのどれかを選択. ここでは01なので1
// 同じパターンを使い上位半分bitも選ぶ
_mm256_permute_ps(imm=0b11110011)
dst : 7 7 4 7 , 3 3 0 3
AVX512
__m512d _mm512_permute_pd (__m512d a, const int imm8)
__m512 _mm512_permute_ps (__m512 a, const int imm8)
double (AVX512)
src0 : 7 6 , 5 4 , 3 2 , 1 0
_mm512_permute_pd(imm=0b00001111)
dst : 6 6 , 4 4 , 3 3 , 1 1
// imm[0]により下位2要素[1:0]のどちらかを選択. ここでは1なので1
// imm[1]により下位2要素[1:0]のどちらかを選択. ここでは1なので1
// imm[2]により次の2要素[3:2]のどちらかを選択. ここでは1なので3
// imm[3]により次の2要素[3:2]のどちらかを選択. ここでは1なので3
// imm[4]により次の2要素[5:4]のどちらかを選択. ここでは0なので4
// imm[5]により次の2要素[5:4]のどちらかを選択. ここでは0なので4
// imm[6]により最後の2要素[7:6]のどちらかを選択. ここでは0なので6
// imm[7]により最後の2要素[7:6]のどちらかを選択. ここでは0なので6
_mm512_permute_pd(imm=0b10011001)
dst : 7 6 , 4 5 , 3 2 , 0 1
float (AVX512)
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
_mm512_permute_pd(imm=0b01101001)
dst : 13 14 14 13 , 9 10 10 9 , 5 6 6 5 , 1 2 2 1
// imm[1:0]により下位4要素[3:0]のどれかを選択. ここでは01なので1
// imm[3:2]により下位4要素[3:0]のどれかを選択. ここでは10なので2
// imm[5:4]により下位4要素[3:0]のどれかを選択. ここでは10なので2
// imm[7:6]により下位4要素[3:0]のどれかを選択. ここでは01なので1
// 同じパターンを使い128bitごとに繰り返し
_mm512_permute_pd(imm=0b01111000)
dst : 13 15 14 12 , 9 11 10 8 , 5 7 6 4 , 1 3 2 0
permute?x?
- 1つのレジスタ内の要素の並び替え
- 上位bitと下位bitが別れていない
AVX2
__m256d _mm256_permute4x64_pd (__m256d a, const int imm8)
double (AVX2)
src0 : 3 2 1 0
_mm256_permute4x64_pd(imm=0b10001110)
dst : 2 0 3 2
// imm[1:0]により4つのどれかを選択. ここでは10なので2
// imm[3:2]により4つのどれかを選択. ここでは11なので3
// imm[5:4]により4つのどれかを選択. ここでは00なので0
// imm[7:6]により4つのどれかを選択. ここでは10なので2
_mm256_permute4x64_pd(imm=0b11110000)
dst : 3 3 0 0
permute{2,4}f128
- 今までは一つのレジスタの並べ替えだったがこれは二つのレジスタを並べ替えて返す
AVX2
- 8bit の引数を取るがimm[2:0], imm[3] と imm[6:4], imm[7] で情報が区切られる
- imm[3]とimm[7]が1ならゼロセット。その場合のimm[2:0]とimm[6:4]は意味を持たなくなる
- imm[2:0],imm[6:4]で0から3を表現するため2bit目と6bit目はそもそも使われない
__m256d _mm256_permute2f128_pd (__m256d a, __m256d b, int imm8)
__m256 _mm256_permute2f128_ps (__m256 a, __m256 b, int imm8)
double (AVX2)
-
imm[2:0],imm[6:4]が
- 0ならsrc0の下位128bit
- 1ならsrc0の上位128bit
- 2ならsrc1の下位128bit
- 3ならsrc2の上位128bit
-
128bit単位なので2要素単位で選択
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_permute4x64_pd(imm=0b00100000)
dst : 11 10 , 1 0
//imm[3],imm[7]が0なので0セットはしない
//2bit目を無視したimm[1:0]が00なのでsrc0の下位128bitを選択
//6bit目を無視したimm[5:4]が10なのでsrc1の下位128bitを選択
_mm256_permute2f128_pd(imm=0b00110011)
dst : 13 12 , 13 12
_mm256_permute2f128_pd(imm=0b00111001)
dst : 13 12 , 0 0
//imm[3]が1なので下位128bitはゼロセット
_mm256_permute2f128_pd(imm=0b10110000)
dst : 0 0 , 1 0
//imm[7]が1なので上位128bitはゼロセット
float (AVX2)
- 128bit単位なので4要素単位で選択
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_permute2f128_ps(imm=0b00100000)
dst : 13 12 11 10 , 3 2 1 0
//imm[3],imm[7]が0なので0セットはしない
//2bit目を無視したimm[1:0]が00なのでsrc0の下位128bitを選択
//6bit目を無視したimm[5:4]が10なのでsrc1の下位128bitを選択
_mm256_permute2f128_ps(imm=0b00110011)
dst : 17 16 15 14 , 17 16 15 14
_mm256_permute2f128_ps(imm=0b00111001)
dst : 17 16 15 14 , 0 0 0 0
//imm[3]が1なので下位128bitはゼロセット
_mm256_permute2f128_ps(imm=0b10110000)
dst : 0 0 0 0 , 3 2 1 0
//imm[7]が1なので上位128bitはゼロセット
共用体設定 (AVX2)
- src0, src1をそれぞれ src0(B,A), src1(D,C)としたときによく使うものでマクロを作っておけばある程度はわかりやすい
-
_mm512_permute4f128_ps
の共用体の真似 -
_MM_PERM128_0A
の 0はzeroset
-
typedef enum
{
_MM_PERM128_AA = 0b00000000, _MM_PERM128_BA = 0b00010000,
_MM_PERM128_AB = 0b00000001, _MM_PERM128_DC = 0b00110010,
_MM_PERM128_AD = 0b00000011, _MM_PERM128_D0 = 0b00111000,
_MM_PERM128_0A = 0b10100000
} _MM_PERM128_ENUM;
src0 : 3 2 , 1 0
src1 : 13 12 , 11 10
_mm256_permute2f128_pd(imm=_MM_PERM128_AD)
dst : 1 0 , 13 12
_mm256_permute2f128_pd(imm=_MM_PERM128_D0)
dst : 13 12 , 0 0
src0 : 7 6 5 4 , 3 2 1 0
src1 : 17 16 15 14 , 13 12 11 10
_mm256_permute2f128_ps(imm=_MM_PERM128_AA)
dst : 3 2 1 0 , 3 2 1 0
AVX512
- 現状では Intel compiler のみ
- doubleバージョンはないようだ
#ifdef __INTEL_COMPILER
__m512 _mm512_permute4f128_ps (__m512 a, _MM_PERM_ENUM imm8)
#endif
float (AVX512)
- 128bitのfloatの4要素づつを下位からA,B,C,D と割当て( src(D,C,B,A) )、その塊で選択
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
### D , C , B , A
_mm512_permute_pd(imm=0b01101001=BCCB)
dst : 7 6 5 4 , 11 10 9 8 , 11 10 9 8 , 7 6 5 4
// 2進数割当も可能 0b01,10,10,01=BCCB
_mm512_permute_pd(imm=0b01111000=BDCA)
dst : 7 6 5 4 , 15 14 13 12 , 11 10 9 8 , 3 2 1 0
_mm512_permute_pd(imm=_MM_PERM_DBDA)
dst : 15 14 13 12 , 7 6 5 4 , 15 14 13 12 , 3 2 1 0
// 共用体で定義された変数を使ったほうがわかりやすい
_mm512_permute_pd(imm=_MM_PERM_ABCD)
dst : 3 2 1 0 , 7 6 5 4 , 11 10 9 8 , 15 14 13 12
permutex
AVX2
- AVX512VL + AVX512F
- float バージョンはないようだ
__m256d _mm256_permutex_pd (__m256d a, int imm8)
-
_mm256_permute4x64_pd
と振る舞いは全く同じ
double (AVX2)
src0 : 3 2 1 0
_mm256_permutex_pd(imm=0b00111001)
dst : 0 3 2 1
_mm256_permutex_pd(imm=0b10110000)
dst : 2 3 0 0
AVX512
- AVX512F
- float バージョンはないようだ
__m512d _mm512_permutex_pd (__m512d a, const int imm8)
double (AVX512)
src0 : 7 6 5 4 , 3 2 1 0
_mm512_permutex_pd(imm=0b00001111)
dst : 4 4 7 7 , 0 0 3 3
// imm[1:0]により下位4要素のどれかを選択. ここでは11なので3
// imm[3:2]により下位4要素のどれかを選択. ここでは11なので3
// imm[5:4]により下位4要素のどれかを選択. ここでは00なので0
// imm[7:6]により下位4要素のどれかを選択. ここでは00なので0
// 同じパターンで上位4要素も選択
_mm512_permutex_pd(imm=0b10011001)
dst : 6 5 6 5 , 2 1 2 1
permutvar
- 今までは即値を引数に与えていたがこれはレジスタを引数に与える
- 1つのレジスタ内の要素の並び替え
AVX2
- AVX512VL + AVX512F
__m256d _mm256_permutevar_pd (__m256d a, __m256i b)
__m256 _mm256_permutevar_ps (__m256 a, __m256i b)
double (AVX2)
- 意味があるのは imm[1], imm[65], imm[129], imm[193]
- 64bit区切りのそれぞれの最下位よりひとつ上のbitが0か1を見る非常に中途半端な感じ
- 下位128bitと上位128bit区切り
src0 : 3 2 , 1 0
mm256_permutevar_pd(imm=_mm256_set_epi64x(0,0,0,0))
dst : 2 2 , 0 0
// imm[1] が0なので下位128bitの下位
// imm[65] が0なので下位128bitの下位
// imm[129] が0なので上位位128bitの下位
// imm[193] が0なので上位128bitの下位
_mm256_permutevar_pd(imm=_mm256_set_epi64x(2,0,2,0)=(0b10,0b00,0b10,0b00)
dst : 3 2 , 1 0
float (AVX2)
- 意味があるのは imm[1:0], imm[33:32], imm[65:64], ... , imm[225:224] と32bit区切りのそれぞれの最下位2bit
- 8要素に0-3を入れる。それ以上の数値入れても下位2bitで判定
- 下位128bitと上位128bit区切り
src0 : 7 6 5 4 , 3 2 1 0
_mm256_permuvar_ps(imm=_mm256_set_epi32(3,2,1,0,3,2,1,0))
dst : 7 6 5 4 , 3 2 1 0
// imm[1:0] が0なので下位128bitのfloat[0]
// imm[33:32] が1なので下位128bitのfloat[1] ,...
// imm[193:192] が2なので上位位128bitのfloat[2]
// imm[225:224] が3なので上位128bitのfloat[3]
_mm256_permuvar_ps(imm=_mm256_set_epi32(3,3,3,3,3,3,3,3))
dst : 7 7 7 7 , 3 3 3 3
permutevar8x32
- floatバージョンのみ
- 上位bitと下位bitの区切りなし
- 8要素に0-7を入れる.それ以上の数値入れても下位3bitで判定
__m256 _mm256_permutevar8x32_ps (__m256 a, __m256i idx)
float (AVX2)
src0 : 7 6 5 4 3 2 1 0
_mm256_permutevar8x32_ps(imm=_mm256_set_epi32(3,2,1,0,7,7,5,0))
dst : 3 2 1 0 7 7 5 0
_mm256_permutevar8x32_ps(imm=_mm256_set_epi32(0,1,2,3,4,5,6,7))
dst : 0 1 2 3 4 5 6 7
AVX512
- AVX512F
- 整数レジスタを引数に与える
__m512d _mm512_permutevar_pd (__m512d a, __m512i b)
__m512 _mm512_permutevar_ps (__m512 a, __m512i b)
double (AVX512)
- 各整数要素の最下位bitで判定するのではなく各整数要素の2bit目で判定
- 0や1を与えても0 , 2を与えると1 という非常にわかりにくい
- b[1],b[65],...,b[385],b[499] 番目のbitで判定
src0 : 7 6 , 5 4 , 3 2 , 1 0
_mm512_permutevar_pd(imm=_mm512_set_epi64(0,0,0,0,0,0,0,0))
dst : 6 6 , 4 4 , 2 2 , 0 0
_mm512_permutevar_pd(imm=_mm512_set_epi64(2,2,0,2,2,0,1,0)
dst : 7 7 , 4 5 , 3 2 , 0 0
float (AVX512)
- b[1],b[65],...,b[385],b[499] 番目のbitで判定
src0 : 15 14 13 12 , 11 10 9 8 , 7 6 5 4 , 3 2 1 0
_mm512_permutevar_ps(imm=_mm512_set_epi32(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
dst : 12 12 12 12 , 8 8 8 8 , 4 4 4 4 , 0 0 0 0
_mm512_permutevar_ps(imm=_mm512_set_epi32(2,3,3,0,2,2,2,2,0,3,2,1,0,1,2,3))
dst : 14 15 15 12 , 10 10 10 10 , 4 7 6 5 , 0 1 2 3
permutexvar
AVX2
- AVX512VL + AVX512F
__m256d _mm256_permutexvar_pd (__m256i idx, __m256d a)
__m256 _mm256_permutexvar_ps (__m256i idx, __m256 a)
double (AVX2)
-
_mm256_permutevar8x32_ps
のdouble版 - 上位と下位のbitの区切りなし
- srcとフラグの位置が逆なので注意
src0 : 3 2 1 0
_mm256_permutexvar_pd(imm=_mm256_set_epi32(3,2,1,0))
dst : 3 2 1 0
_mm256_permutexvar_pd(imm=_mm256_set_epi32(0,3,1,3))
dst : 0 3 1 3
float (AVX2)
-
_mm256_permutevar8x32_ps
と同じ - srcとフラグの位置が逆なので注意
src0 : 7 6 5 4 3 2 1 0
_mm256_permutexvar_ps(imm=_mm256_set_epi32(3,2,1,0,7,7,5,0))
dst : 3 2 1 0 7 7 5 0
_mm256_permutexvar_ps(imm=_mm256_set_epi32(0,1,2,3,4,5,6,7))
dst : 0 1 2 3 4 5 6 7
AVX512
- AVX512F
__m512d _mm512_permutexvar_pd (__m512i idx, __m512d a)
__m512 _mm512_permutexvar_ps (__m512i idx, __m512 a)
double (AXV512)
src0 : 7 6 5 4 3 2 1 0
_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,0,3,0,2,0,1,0))
dst : 4 0 3 0 2 0 1 0
_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,4,5,5,6,6,7,7))
dst : 4 4 5 5 6 6 7 7
float (AXV512)
src0 : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
_mm512_permutexvar_ps(imm=_mm512_set_epi32(1,2,3,0,0,0,0,0,0,0,10,11,12,0,0,15))
dst : 1 2 3 0 0 0 0 0 0 0 10 11 12 0 0 15
_mm512_permutexvar_ps(imm=_mm512_set_epi32(4,10,14,0,2,2,2,2,0,3,2,15,0,1,2,3))
dst : 4 10 14 0 2 2 2 2 0 3 2 15 0 1 2 3
permutex2var
AVX2
- AVX512VL + AVX512F
__m256d _mm256_permutex2var_pd (__m256d a, __m256i idx, __m256d b)
__m256 _mm256_permutex2var_ps (__m256 a, __m256i idx, __m256 b)
double (AVX2)
- 上位と下位のbitの区切りなし
- src0(3,2,1,0), src1(7,6,5,4) としたときに対応するindex
src0 : 3 2 1 0
src1 : 13 12 11 10
_mm256_permutex2var_pd(imm=_mm256_set_epi32(7,5,2,0))
dst : 13 11 2 0
_mm256_permutex2var_pd(imm=_mm256_set_epi32(2,7,4,1))
dst : 2 13 10 1
float (AVX2)
- 上位と下位のbitの区切りなし
- src0(7,6,5,4,3,2,1,0), src1(15,14,13,12,11,10,9,8) としたときに対応するindex
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm256_permutex2var_ps(imm=_mm256_set_epi32(10,2,1,15,7,0,9,0))
dst : 12 2 1 17 7 0 11 0
_mm256_permutex2var_ps(imm=_mm256_set_epi32(0,1,2,3,14,15,4,6))
dst : 0 1 2 3 16 17 4 6
AVX512
- AVX512F
__m512d _mm512_permutex2var_pd (__m512d a, __m512i idx, __m512d b)
__m512 _mm512_permutex2var_ps (__m512 a, __m512i idx, __m512 b)
double (AXV512)
src0 : 7 6 5 4 3 2 1 0
src1 : 17 16 15 14 13 12 11 10
_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,14,3,14,2,15,1,10))
dst : 4 16 3 16 2 17 1 12
_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,4,15,15,6,6,9,9))
dst : 4 4 17 17 6 6 11 11
float (AXV512)
src0 : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
src1 : 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20
_mm512_permutexvar_ps(imm=_mm512_set_epi32(1,2,3,31,30,29,28,27,20,1,2,11,12,0,0,15))
dst : 1 2 3 35 34 33 32 31 24 1 2 11 12 0 0 15
_mm512_permutexvar_ps(imm=_mm512_set_epi32(2,4,6,4,10,12,14,16,18,20,22,24,26,28,30,0))
dst : 2 4 6 4 10 12 14 20 22 24 26 28 30 32 34 0
code
code.c
#include <stdio.h>
#include <stdlib.h>
#include <x86intrin.h>
void print3_m256(__m256 s0, __m256 s1, __m256 dst,
const char *label)
{
int vsize=8;
vsize-=1;
printf("%s\n",label);
printf("src0 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&s0)[ix]);
}
printf("\n");
printf("src1 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&s1)[ix]);
}
printf("\n");
printf("dst : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&dst)[ix]);
}
printf("\n");
printf("\n");
}
void print3_m256d(__m256d s0, __m256d s1, __m256d dst,
const char *label)
{
int vsize=4;
vsize-=1;
printf("%s\n",label);
printf("src0 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&s0)[ix]);
}
printf("\n");
printf("src1 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&s1)[ix]);
}
printf("\n");
printf("dst : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&dst)[ix]);
}
printf("\n");
printf("\n");
}
#ifdef __AVX512F__
void print3_m512(__m512 s0, __m512 s1, __m512 dst,
const char *label)
{
int vsize=16;
vsize-=1;
printf("%s\n",label);
printf("src0 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&s0)[ix]);
}
printf("\n");
printf("src1 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&s1)[ix]);
}
printf("\n");
printf("dst : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((float*)&dst)[ix]);
}
printf("\n");
printf("\n");
}
void print3_m512d(__m512d s0, __m512d s1, __m512d dst,
const char *label)
{
int vsize=8;
vsize-=1;
printf("%s\n",label);
printf("src0 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&s0)[ix]);
}
printf("\n");
printf("src1 : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&s1)[ix]);
}
printf("\n");
printf("dst : ");
for(int ix = vsize; ix>= 0; --ix) {
printf("%2g ", ((double*)&dst)[ix]);
}
printf("\n");
printf("\n");
}
#endif
void printf_red(const char *label)
{
printf("\x1b[31m%s\x1b[39m",label);
}
void printf_blue(const char *label)
{
printf("\x1b[34m%s\x1b[39m",label);
}
void check_avx2()
{
printf_red("##### AVX #####\n");
printf("\n");
__m256 src0, src1, dst;
__m256d src0d, src1d, dstd;
src0 = _mm256_set_ps(7,6,5,4,3,2,1,0);
src1 = _mm256_set_ps(17,16,15,14,13,12,11,10);
src0d = _mm256_set_pd(3,2,1,0);
src1d = _mm256_set_pd(13,12,11,10);
printf_blue("### unpacklo ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_unpacklo_pd(src0d, src1d);
print3_m256d(src0d, src1d, dstd, "_mm256_unpacklo_pd");
dst = _mm256_setzero_ps();
dst = _mm256_unpacklo_ps(src0, src1);
print3_m256(src0, src1, dst, "_mm256_unpacklo_ps");
printf_blue("### unpackhi ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_unpackhi_pd(src0d, src1d);
print3_m256d(src0d, src1d, dstd, "_mm256_unpackhi_pd");
dst = _mm256_setzero_ps();
dst = _mm256_unpackhi_ps(src0, src1);
print3_m256(src0, src1, dst, "_mm256_unpackhi_ps");
printf_blue("### blend ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_blend_pd(src0d, src1d, 0b1010);
print3_m256d(src0d, src1d, dstd, "_mm256_blend_pd(imm=0b1010)");
dst = _mm256_setzero_ps();
dst = _mm256_blend_ps(src0, src1, 0b00010111);
print3_m256(src0, src1, dst, "_mm256_blend_ps(imm=0b00010111)");
printf_blue("### blendv ###\n");
__m256d maskd = _mm256_set_pd(0,-0.0,0,-0.0);
dstd = _mm256_setzero_pd();
dstd = _mm256_blendv_pd(src0d, src1d, maskd);
print3_m256d(src0d, src1d, dstd, "_mm256_blendv_pd(mask=_mm256_set_pd(0,-0.0,0,-0.0))");
__m256 mask = _mm256_set_ps(-0.0f,0,-0.0f,0,0,-0.0f,-0.0f,0);
dst = _mm256_setzero_ps();
dst = _mm256_blendv_ps(src0, src1, mask);
print3_m256(src0, src1, dst, "_mm256_blendv_ps(mask=_mm256_set_ps(-0.0f,0,-0.0f,0,0,-0.0f,-0.0f,0))");
printf_blue("### shuffle ###\n");
/* _MM_SHUFFLE(dd,cc,bb,aa) = 0bddccbbaa */
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_pd(src0d, src1d, 0b1101);
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_pd(imm=0b1101)");
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_pd(src0d, src1d, 0xf);
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_pd(imm=0xf=_MM_SHUFFLE(3,3,3,3)=0b1111)");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_ps(src0, src1, 0b10001101);
print3_m256(src0, src1, dst, "_mm256_shuffle_ps(imm=0b10001101)");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_ps(src0, src1, _MM_SHUFFLE(2,1,1,3));
print3_m256(src0, src1, dst, "_mm256_shuffle_ps(imm=_MM_SHUFFLE(2,1,1,3)=0b10010111)");
printf_blue("### permute ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute_pd(src0d, 0b1010);
print3_m256d(src0d, src0d, dstd, "_mm256_permute_pd(imm=0b1010)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute_pd(src0d, 0b1111);
print3_m256d(src0d, src0d, dstd, "_mm256_permute_pd(imm=0b1111)");
dst = _mm256_setzero_ps();
dst = _mm256_permute_ps(src0, 0b01011010);
print3_m256(src0, src0, dst, "_mm256_permute_ps(imm=0b01011010)");
dst = _mm256_setzero_ps();
dst = _mm256_permute_ps(src0, 0b11110011);
print3_m256(src0, src0, dst, "_mm256_permute_ps(imm=0b11110011)");
printf_blue("### permute4x64 ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute4x64_pd(src0d, 0b10001110);
print3_m256d(src0d, src0d, dstd, "_mm256_permute4x64_pd(imm=0b10001110)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute4x64_pd(src0d, 0b11110000);
print3_m256d(src0d, src0d, dstd, "_mm256_permute4x64_pd(imm=0b11110000)");
printf_blue("### permute2f128 ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, 0b00100000);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=0b00100000)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, 0b00110011);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=0b00110011)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, 0b00111001);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=0b00111001)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, 0b10110000);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=0b10110000)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, 0b00100000);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=0b00100000)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, 0b00110011);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=0b00110011)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, 0b00111001);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=0b00111001)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, 0b10110000);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=0b10110000)");
#if 1
typedef enum
{
_MM_PERM128_AA = 0b00000000, _MM_PERM128_BA = 0b00010000,
_MM_PERM128_AB = 0b00000001, _MM_PERM128_DC = 0b00110010,
_MM_PERM128_AD = 0b00000011, _MM_PERM128_D0 = 0b00111000,
_MM_PERM128_0A = 0b10100000
} _MM_PERM128_ENUM;
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, _MM_PERM128_AA);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=_MM_PERM128_AA)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, _MM_PERM128_AB);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=_MM_PERM128_AB)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, _MM_PERM128_BA);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=_MM_PERM128_BA)");
dst = _mm256_setzero_ps();
dst = _mm256_permute2f128_ps(src0, src1, _MM_PERM128_DC);
print3_m256(src0, src1, dst, "_mm256_permute2f128_ps(imm=_MM_PERM128_DC)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, _MM_PERM128_AD);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=_MM_PERM128_AD)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, _MM_PERM128_D0);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=_MM_PERM128_D0)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permute2f128_pd(src0d, src1d, _MM_PERM128_0A);
print3_m256d(src0d, src1d, dstd, "_mm256_permute2f128_pd(imm=_MM_PERM128_0A)");
#endif
}
#ifdef __AVX512F__
void check_avx512()
{
printf("\n\n");
printf("\x1b[31m##### AVX512 #####\x1b[39m\n");
printf("\n");
__m512 src0, src1, dst;
__m512d src0d, src1d, dstd;
src0 = _mm512_set_ps(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
src1 = _mm512_set_ps(35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20);
src0d = _mm512_set_pd(7,6,5,4,3,2,1,0);
src1d = _mm512_set_pd(17,16,15,14,13,12,11,10);
printf_blue("### unpacklo ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_unpacklo_pd(src0d, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_unpacklo_pd");
dst = _mm512_setzero_ps();
dst = _mm512_unpacklo_ps(src0, src1);
print3_m512(src0, src1, dst, "_mm512_unpacklo_ps");
printf_blue("### unpackhi ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_unpackhi_pd(src0d, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_unpackhi_pd");
dst = _mm512_setzero_ps();
dst = _mm512_unpackhi_ps(src0, src1);
print3_m512(src0, src1, dst, "_mm512_unpackhi_ps");
printf_blue("### mask_blend ###\n");
__mmask8 mask8 = 0b10100101;
dstd = _mm512_setzero_pd();
dstd = _mm512_mask_blend_pd(mask8, src0d, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_mask_blend_pd(mask=0b10100101)");
__mmask16 mask16 = 0b1010010100110101;
dst = _mm512_setzero_ps();
dst = _mm512_mask_blend_ps(mask16, src0, src1);
print3_m512(src0, src1, dst, "_mm512_mask_blend_ps(mask=0b1010010100110101)");
printf_blue("### mask_mov ###\n");
mask8 = 0b10100101;
dstd = _mm512_setzero_pd();
dstd = _mm512_mask_mov_pd(src0d, mask8, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_mask_mov_pd(mask=0b10100101)");
mask16 = 0b1010010100110101;
dst = _mm512_setzero_ps();
dst = _mm512_mask_mov_ps(src0, mask16, src1);
print3_m512(src0, src1, dst, "_mm512_mask_mov_ps(mask=0b1010010100110101)");
printf_blue("### maskz_mov ###\n");
mask8 = 0b10100101;
dstd = _mm512_setzero_pd();
dstd = _mm512_maskz_mov_pd(mask8, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_maskz_mov_pd(mask=0b10100101)");
mask16 = 0b1010010100110101;
dst = _mm512_setzero_ps();
dst = _mm512_maskz_mov_ps(mask16, src1);
print3_m512(src0, src1, dst, "_mm512_maskz_mov_ps(mask=0b1010010100110101)");
printf_blue("### shuffle ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_pd(src0d, src1d, 0b10010110);
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_pd(imm=0b10010110)");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_pd(src0d, src1d, 0b01110011);
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_pd(imm=0b01110011)");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_ps(src0, src1, 0b10001101);
print3_m512(src0, src1, dst, "_mm512_shuffle_ps(imm=0b10001101)");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_ps(src0, src1, _MM_SHUFFLE(2,1,3,3));
print3_m512(src0, src1, dst, "_mm512_shuffle_ps(imm=_MM_SHUFFLE(2,1,3,3)=0b10011111)");
printf_blue("### permute ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_permute_pd (src0d, 0b00001111);
print3_m512d(src0d, src0d, dstd, "_mm512_permute_pd(imm=0b00001111)");
dstd = _mm512_setzero_pd();
dstd = _mm512_permute_pd (src0d, 0b10011001);
print3_m512d(src0d, src0d, dstd, "_mm512_permute_pd(imm=0b10011001)");
dst = _mm512_setzero_ps();
dst = _mm512_permute_ps (src0, 0b01101001);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=0b01101001)");
dst = _mm512_setzero_ps();
dst = _mm512_permute_ps (src0, 0b01111000);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=0b01111000)");
#ifdef __INTEL_COMPILER
printf("INTEL COMPILER\n");
printf_blue("### permute4f128 ###\n");
dst = _mm512_setzero_ps();
dst = _mm512_permute4f128_ps(src0, 0b01101001);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=0b01101001)");
dst = _mm512_setzero_ps();
dst = _mm512_permute4f128_ps(src0, 0b01111000);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=0b01111000)");
dst = _mm512_setzero_ps();
dst = _mm512_permute4f128_ps(src0, _MM_PERM_DBDA);
printf("_MM_PERM_DBDA %x\n",_MM_PERM_DBDA);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=_MM_PERM_DBDA)");
dst = _mm512_setzero_ps();
dst = _mm512_permute4f128_ps(src0, _MM_PERM_ABCD);
printf("_MM_PERM_ABCD %x\n",_MM_PERM_ABCD);
print3_m512(src0, src0, dst, "_mm512_permute_pd(imm=_MM_PERM_ABCD)");
#endif
printf_blue("### permutex ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_permutex_pd (src0d, 0b00001111);
print3_m512d(src0d, src0d, dstd, "_mm512_permutex_pd(imm=0b00001111)");
dstd = _mm512_setzero_pd();
dstd = _mm512_permutex_pd (src0d, 0b10011001);
print3_m512d(src0d, src0d, dstd, "_mm512_permutex_pd(imm=0b10011001)");
printf_blue("### permutevar ###\n");
__m512i flag512i = _mm512_set_epi64(0,0,0,0,0,0,0,0);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutevar_pd (src0d, flag512i);
print3_m512d(src0d, src0d, dstd, "_mm512_permutevar_pd(imm=_mm512_set_epi64(0,0,0,0,0,0,0,0))");
flag512i = _mm512_set_epi64(2,2,0,2,2,0,1,0);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutevar_pd (src0d, flag512i);
print3_m512d(src0d, src0d, dstd, "_mm512_permutevar_pd(imm=_mm512_set_epi64(2,2,0,2,2,0,1,0))");
flag512i = _mm512_set_epi32(0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0);
dst = _mm512_setzero_ps();
dst = _mm512_permutevar_ps (src0, flag512i);
print3_m512(src0, src0, dst, "_mm512_permutevar_ps(imm=_mm512_set_epi32(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))");
flag512i = _mm512_set_epi32(2,3,3,0,2,2,2,2,
0,3,2,1,0,1,2,3);
dst = _mm512_setzero_ps();
dst = _mm512_permutevar_ps (src0, flag512i);
print3_m512(src0, src0, dst, "_mm512_permutevar_ps(imm=_mm512_set_epi32(2,3,3,0,2,2,2,2,0,3,2,1,0,1,2,3))");
printf_blue("### permutexvar ###\n");
/*
src:(7, 6, 5, 4, 3, 2, 1, 0)
*/
flag512i = _mm512_set_epi64(4,0,3,0,2,0,1,0);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutexvar_pd (flag512i, src0d);
print3_m512d(src0d, src0d, dstd, "_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,0,3,0,2,0,1,0))");
flag512i = _mm512_set_epi64(4,4,5,5,6,6,7,7);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutexvar_pd (flag512i, src0d);
print3_m512d(src0d, src0d, dstd, "_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,4,5,5,6,6,7,7))");
/*
src:(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
*/
flag512i = _mm512_set_epi32(1,2,3,0,0,0,0,0,
0,0,10,11,12,0,0,15);
dst = _mm512_setzero_ps();
dst = _mm512_permutexvar_ps (flag512i, src0);
print3_m512(src0, src0, dst, "_mm512_permutexvar_ps(imm=_mm512_set_epi32(1,2,3,0,0,0,0,0,0,0,10,11,12,0,0,15))");
flag512i = _mm512_set_epi32(4,10,14,0,2,2,2,2,
0,3,2,15,0,1,2,3);
dst = _mm512_setzero_ps();
dst = _mm512_permutexvar_ps (flag512i, src0);
print3_m512(src0, src0, dst, "_mm512_permutexvar_ps(imm=_mm512_set_epi32(4,10,14,0,2,2,2,2,0,3,2,15,0,1,2,3))");
printf_blue("### permutex2var ###\n");
/*
src0:(7, 6, 5, 4, 3, 2, 1, 0)
src1:(15, 14, 13, 12, 11, 10, 9, 8)
*/
flag512i = _mm512_set_epi64(4,14,3,14,2,15,1,10);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutex2var_pd (src0d, flag512i, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,14,3,14,2,15,1,10))");
flag512i = _mm512_set_epi64(4,4,15,15,6,6,9,9);
dstd = _mm512_setzero_pd();
dstd = _mm512_permutex2var_pd (src0d, flag512i, src1d);
print3_m512d(src0d, src1d, dstd, "_mm512_permutexvar_pd(imm=_mm512_set_epi64(4,4,15,15,6,6,9,9))");
/*
src0:(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
src1:(31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16)
*/
flag512i = _mm512_set_epi32(1,2,3,31,30,29,28,27,
20,1,2,11,12,0,0,15);
dst = _mm512_setzero_ps();
dst = _mm512_permutex2var_ps (src0, flag512i, src1);
print3_m512(src0, src1, dst, "_mm512_permutexvar_ps(imm=_mm512_set_epi32(1,2,3,31,30,29,28,27,20,1,2,11,12,0,0,15))");
flag512i = _mm512_set_epi32(2,4,6,4,10,12,14,16,
18,20,22,24,26,28,30,0);
dst = _mm512_setzero_ps();
dst = _mm512_permutex2var_ps (src0, flag512i, src1);
print3_m512(src0, src1, dst, "_mm512_permutexvar_ps(imm=_mm512_set_epi32(2,4,6,4,10,12,14,16,18,20,22,24,26,28,30,0))");
}
#endif
#ifdef __AVX512VL__
void check_avx2_avx512vl()
{
printf("\n\n");
printf("\x1b[31m##### AVX2-AVX512VL #####\x1b[39m\n");
printf("\n");
__m256 src0, src1, dst;
__m256d src0d, src1d, dstd;
src0 = _mm256_set_ps(7,6,5,4,3,2,1,0);
src1 = _mm256_set_ps(17,16,15,14,13,12,11,10);
src0d = _mm256_set_pd(3,2,1,0);
src1d = _mm256_set_pd(13,12,11,10);
printf_blue("### shuffle64x2 ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_f64x2(src0d, src1d, 0b00); //2-bit immediate
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_f64x2(imm=0b00)");
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_f64x2(src0d, src1d, 0b01);
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_f64x2(imm=0b01)");
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_f64x2(src0d, src1d, 0b10);
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_f64x2(imm=0b10)");
dstd = _mm256_setzero_pd();
dstd = _mm256_shuffle_f64x2(src0d, src1d, 0b11);
print3_m256d(src0d, src1d, dstd, "_mm256_shuffle_f64x2(imm=0b11)");
printf_blue("### shuffle32x4 ###\n");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_f32x4(src0, src1, 0b00); //2-bit immediate
print3_m256(src0, src1, dst, "_mm256_shuffle_f32x4(imm=0b00)");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_f32x4(src0, src1, 0b01);
print3_m256(src0, src1, dst, "_mm256_shuffle_f32x4(imm=0b01)");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_f32x4(src0, src1, 0b10);
print3_m256(src0, src1, dst, "_mm256_shuffle_f32x4(imm=0b10)");
dst = _mm256_setzero_ps();
dst = _mm256_shuffle_f32x4(src0, src1, 0b11);
print3_m256(src0, src1, dst, "_mm256_shuffle_f32x4(imm=0b11)");
printf_blue("### permutex ###\n");
dstd = _mm256_setzero_pd();
dstd = _mm256_permutex_pd(src0d, 0b00111001);
print3_m256d(src0d, src0d, dstd, "_mm256_permutex_pd(imm=0b00111001)");
dstd = _mm256_setzero_pd();
dstd = _mm256_permutex_pd(src0d, 0b10110000);
print3_m256d(src0d, src0d, dstd, "_mm256_permutex_pd(imm=0b10110000)");
printf_blue("### permutevar ###\n");
__m256i flag256i = _mm256_set_epi64x(0,0,0,0);
dstd = _mm256_setzero_pd();
dstd = _mm256_permutevar_pd(src0d, flag256i);
print3_m256d(src0d, src0d, dstd, "_mm256_permutevar_pd(imm=_mm256_set_epi64x(0,0,0,0))");
flag256i = _mm256_set_epi64x(0b10,0b00,0b10,0b00); // 2,0,2,0
dstd = _mm256_setzero_pd();
dstd = _mm256_permutevar_pd(src0d, flag256i);
print3_m256d(src0d, src0d, dstd, "_mm256_permutevar_pd(imm=_mm256_set_epi64x(2,0,2,0)=(0b10,0b00,0b10,0b00)");
flag256i = _mm256_set_epi32(3,2,1,0,3,2,1,0);
dst = _mm256_setzero_ps();
dst = _mm256_permutevar_ps(src0, flag256i);
print3_m256(src0, src0, dst, "_mm256_permutevar_ps(imm=_mm256_set_epi32(3,2,1,0,3,2,1,0))");
flag256i = _mm256_set_epi32(3,3,3,3,3,3,3,3);
dst = _mm256_setzero_ps();
dst = _mm256_permutevar_ps(src0, flag256i);
print3_m256(src0, src0, dst, "_mm256_permutevar_ps(imm=_mm256_set_epi32(3,3,3,3,3,3,3,3))");
printf_blue("### permutevar8x32 ###\n");
flag256i = _mm256_set_epi32(3,2,1,0,7,7,5,0);
dst = _mm256_setzero_ps();
dst = _mm256_permutevar8x32_ps(src0, flag256i);
print3_m256(src0, src0, dst, "_mm256_permutevar8x32_ps(imm=_mm256_set_epi32(3,2,1,0,7,7,5,0))");
flag256i = _mm256_set_epi32(0,1,2,3,4,5,6,7);
dst = _mm256_setzero_ps();
dst = _mm256_permutevar8x32_ps(src0, flag256i);
print3_m256(src0, src0, dst, "_mm256_permutevar8x32_ps(imm=_mm256_set_epi32(0,1,2,3,4,5,6,7))");
printf_blue("### permutexvar ###\n");
flag256i = _mm256_set_epi64x(3,2,1,0);
dstd = _mm256_setzero_pd();
dstd = _mm256_permutexvar_pd(flag256i, src0d);
print3_m256d(src0d, src0d, dstd, "_mm256_permutexvar_pd(imm=_mm256_set_epi32(3,2,1,0))");
flag256i = _mm256_set_epi64x(0,3,1,3);
dstd = _mm256_setzero_pd();
dstd = _mm256_permutexvar_pd(flag256i, src0d);
print3_m256d(src0d, src0d, dstd, "_mm256_permutexvar_pd(imm=_mm256_set_epi32(0,3,1,3))");
flag256i = _mm256_set_epi32(3,2,1,0,7,7,5,0);
dst = _mm256_setzero_ps();
dst = _mm256_permutexvar_ps(flag256i, src0);
print3_m256(src0, src0, dst, "_mm256_permutexvar_ps(imm=_mm256_set_epi32(3,2,1,0,7,7,5,0))");
flag256i = _mm256_set_epi32(0,1,2,3,4,5,6,7);
dst = _mm256_setzero_ps();
dst = _mm256_permutexvar_ps(flag256i, src0);
print3_m256(src0, src0, dst, "_mm256_permutexvar_ps(imm=_mm256_set_epi32(0,1,2,3,4,5,6,7))");
printf_blue("### permutex2var ###\n");
flag256i = _mm256_set_epi64x(7,5,2,0);
dstd = _mm256_setzero_pd();
dstd = _mm256_permutex2var_pd(src0d, flag256i, src1d);
print3_m256d(src0d, src1d, dstd, "_mm256_permutex2var_pd(imm=_mm256_set_epi32(7,5,2,0))");
flag256i = _mm256_set_epi64x(2,7,4,1);
dstd = _mm256_setzero_pd();
dstd = _mm256_permutex2var_pd(src0d, flag256i, src1d);
print3_m256d(src0d, src1d, dstd, "_mm256_permutex2var_pd(imm=_mm256_set_epi32(2,7,4,1))");
flag256i = _mm256_set_epi32(10,2,1,15,7,0,9,0);
dst = _mm256_setzero_ps();
dst = _mm256_permutex2var_ps(src0, flag256i, src1);
print3_m256(src0, src1, dst, "_mm256_permutex2var_ps(imm=_mm256_set_epi32(10,2,1,15,7,0,9,0))");
flag256i = _mm256_set_epi32(0,1,2,3,14,15,4,6);
dst = _mm256_setzero_ps();
dst = _mm256_permutex2var_ps(src0, flag256i, src1);
print3_m256(src0, src1, dst, "_mm256_permutex2var_ps(imm=_mm256_set_epi32(0,1,2,3,14,15,4,6))");
}
void check_avx512_avx512vl()
{
printf("\n\n");
printf("\x1b[31m##### AVX512-AVX512VL #####\x1b[39m\n");
printf("\n");
__m512 src0, src1, dst;
__m512d src0d, src1d, dstd;
src0 = _mm512_set_ps(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
src1 = _mm512_set_ps(35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20);
src0d = _mm512_set_pd(7,6,5,4,3,2,1,0);
src1d = _mm512_set_pd(17,16,15,14,13,12,11,10);
printf_blue("### shuffle64x2 ###\n");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_f64x2(src0d, src1d, 0b00000000); //4-bit immediate
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_f64x2(imm=0b00000000)");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_f64x2(src0d, src1d, 0b01011101);
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_f64x2(imm=0b01011101)");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_f64x2(src0d, src1d, _MM_SHUFFLE(3,0,3,0));
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_f64x2(imm=_MM_SHUFFLE(3,0,3,0)=0b11001100)");
dstd = _mm512_setzero_pd();
dstd = _mm512_shuffle_f64x2(src0d, src1d, _MM_SHUFFLE(3,3,3,3));
print3_m512d(src0d, src1d, dstd, "_mm512_shuffle_f64x2(imm=_MM_SHUFFLE(3,3,3,3)=0b11111111)");
printf_blue("### shuffle32x4 ###\n");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_f32x4(src0, src1, 0b00000000); //4-bit immediate
print3_m512(src0, src1, dst, "_mm512_shuffle_f32x4(imm=0b00000000)");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_f32x4(src0, src1, 0b01011101);
print3_m512(src0, src1, dst, "_mm512_shuffle_f32x4(imm=0b01011101)");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_f32x4(src0, src1, _MM_SHUFFLE(3,0,3,0));
print3_m512(src0, src1, dst, "_mm512_shuffle_f32x4(imm=_MM_SHUFFLE(3,0,3,0)=0b11001100)");
dst = _mm512_setzero_ps();
dst = _mm512_shuffle_f32x4(src0, src1, _MM_SHUFFLE(3,3,3,3));
print3_m512(src0, src1, dst, "_mm512_shuffle_f32x4(imm=_MM_SHUFFLE(3,3,3,3)=0b11111111)");
}
#endif
int main(int argc, char **argv)
{
check_avx2();
#ifdef __AVX512F__
check_avx512();
#endif
#ifdef __AVX512VL__
check_avx2_avx512vl();
check_avx512_avx512vl();
#endif
return EXIT_SUCCESS;
}
- コンパイル
$ gcc -mavx512f code.c
$ gcc -mavx512f -mavx512vl code.c
$ icc -std=c11 -xCORE-AVX512 shuffle.c