はじめに
AVXのビットテストは、整数版(VPTEST)と実数版(VTESTPD)があって、動作が異なる。ちょっと混乱するので覚書。ビットテストはZF、CFの双方に影響するが、以下ZFの振る舞いのみについて記述する。
整数版(VPTEST)
整数版のVPTESTは、全ビットに対してANDをかける。全ビットのANDがゼロならZFが立ち、そうでなければZFはクリアされる。対応する組み込み関数は_mm256_testz_si256
。これはZFが立っていたら1を、そうでなければ0を返す。1
要するに、純粋に256ビットのANDだと思えば良い。以下、いくつか例示。
- 要素ごとのANDが全て0なら1
__m256i v1 = _mm256_set_epi32(0,1,2,4,8,16,32,64);
__m256i v2 = _mm256_set_epi32(1,2,4,8,16,32,64,128);
std::cout << _mm256_testz_si256(v1,v2) << std::endl;
// => 1
- 要素ごとのANDが一つでも非ゼロなら0
__m256i v1 = _mm256_set_epi32(0,1,2,4,8,16,32,64);
__m256i v2 = _mm256_set_epi32(1,1,4,8,16,32,64,128);
std::cout << _mm256_testz_si256(v1,v2) << std::endl;
// => 0
- 同じ値でも0
__m256i v1 = _mm256_set_epi32(0,1,2,3,4,5,6,7);
__m256i v2 = _mm256_set_epi32(0,1,2,3,4,5,6,8);
std::cout << _mm256_testz_si256(v1,v2) << std::endl;
// => 0
なので、例えばmm256_testz_si256(v1,v1)
で、YMMレジスタが全てゼロであるかどうかを調べたり、(本質的に同じことだが)mm256_testz_si256(v1-v2,v1-v2)
で二つのYMMレジスタが全く同じ値をもっているかどうかを調べることができる。
実数版(VTESTPD)
整数版が「全てのbitのAND」をとっていたのに対して、実数版は「符号ビット」にのみ作用する。図解するまでもないが、こんな感じ。
YMMレジスタを食わせた場合は、4つの要素の符号ベクトルのみを集めた4bitの値を作って、それにANDをかけて、ゼロになったらZFを立て、そうでなければZFをクリアする。対応する組み込み関数は_mm256_testz_pd
で、ZFが立っていれば1を、そうでなければ0を返す。1
要するに「対応する位置の要素が同時に負であれば1、そうでなければ0」となる。以下、いくつか例示。
- 負の要素がなければ値に関わらず1
__m256d v1 = _mm256_set_pd(3,2,1,0);
__m256d v2 = _mm256_set_pd(7,6,5,4);
std::cout << _mm256_testz_pd(v1,v2) << std::endl;
// => 1
- 同じ位置に負の要素があれば、値が異なっていても0
__m256d v1 = _mm256_set_pd(3,2,-1,0);
__m256d v2 = _mm256_set_pd(7,6,-5,4);
std::cout << _mm256_testz_pd(v1,v2) << std::endl;
// => 0
- 負の要素の位置が異なっていれば1
__m256d v1 = _mm256_set_pd(3,2,-1,0);
__m256d v2 = _mm256_set_pd(7,-6,5,4);
std::cout << _mm256_testz_pd(v1,v2) << std::endl;
// => 1
- 対応する負の要素が複数あっても0
__m256d v1 = _mm256_set_pd(3,-2,-1,0);
__m256d v2 = _mm256_set_pd(7,-6,-5,4);
std::cout << _mm256_testz_pd(v1,v2) << std::endl;
// => 0
なので、第一引数と第二引数に同じベクトルを食わせると「4つの要素のうちどれか一つでも負なら0、そうでなければ1」という判定に使える。っていうかそれ以外の有効な使い方を思いつかない・・・。
まとめ
パックド・ビット・テストは、整数版と実数版で命令が異なり、動作も異なる。特にANDPDとかORPDがビットごとにかかるのに、VTESTPDは符号ビットにしかかからないのにしばらく気づかなかったのでまとめておいた。ちゃんと読むとマニュアルに書いてあるんだけど、その書き方が
IF (TEMP[63] = TEMP[127] = TEMP[191] = TEMP[255] = 0)
THEN ZF←1;
ELSE ZF←0;
みたいにさらっと書いてあったので・・・。
-
本当はZFが立っていれば非ゼロ、そうでなければ0だが、手元の環境では1を返す実装になっていた。 ↩