BSF/BSR で入力がゼロだったとき、出力は
- Intel: 未定義
- AMD: 変更されない
と、ドキュメントではなっている。
CPU の構造を考えると、条件代入命令 (CMOVcc) と同じで、パイプラインの終わりにある、書き込みステージの動作(出力レジスタへの代入)を行わないこと(ただしフラグ レジスタは更新)になりそうな気がする。
Intel も AMD も同じ動作になると思うので、手元の Core i7-8700B [コンパイラは g++]で試してみる。
sample.cpp
#include <iostream>
#include <iomanip>
using namespace std;
// int int_log2(unsigned x, int z=-1)
// 出力: (int)log2(x)
// x が 0 の場合、z を返す
// Intel 仕様向け
int int_log2_intel(unsigned x, int z=-1)
{
int b;
asm("bsrl %2,%0;" // b = BSR(x);
"cmovz %1,%0" // if (x == 0) b = z;
: "=r" (b), "+r" (z) : "r" (x));
return b;
}
// AMD 仕様向け
int int_log2_amd(unsigned x, int z=-1)
{
asm("bsrl %1,%0" : "+r" (z) : "r" (x));
return z;
}
// メイン関数
int main(int argc, char **argv)
{
cout << showbase << right;
for (int b = -1; b < 32; b++)
{
unsigned x = (b >= 0) ? (1 << b) : 0;
cout << "int_log2(" << hex << setw(10) << x << ")"
<< " = " << dec << setw(2) << int_log2_intel(x)
<< ", " << dec << setw(2) << int_log2_amd(x)
<< endl;
}
return 0;
}
をコンパイルして、実行すると
$ g++ -O3 -o sample sample.cpp
$ ./sample
int_log2( 0) = -1, -1
int_log2( 0x1) = 0, 0
int_log2( 0x2) = 1, 1
int_log2( 0x4) = 2, 2
int_log2( 0x8) = 3, 3
int_log2( 0x10) = 4, 4
int_log2( 0x20) = 5, 5
int_log2( 0x40) = 6, 6
int_log2( 0x80) = 7, 7
int_log2( 0x100) = 8, 8
int_log2( 0x200) = 9, 9
int_log2( 0x400) = 10, 10
int_log2( 0x800) = 11, 11
int_log2( 0x1000) = 12, 12
int_log2( 0x2000) = 13, 13
int_log2( 0x4000) = 14, 14
int_log2( 0x8000) = 15, 15
int_log2( 0x10000) = 16, 16
int_log2( 0x20000) = 17, 17
int_log2( 0x40000) = 18, 18
int_log2( 0x80000) = 19, 19
int_log2( 0x100000) = 20, 20
int_log2( 0x200000) = 21, 21
int_log2( 0x400000) = 22, 22
int_log2( 0x800000) = 23, 23
int_log2( 0x1000000) = 24, 24
int_log2( 0x2000000) = 25, 25
int_log2( 0x4000000) = 26, 26
int_log2( 0x8000000) = 27, 27
int_log2(0x10000000) = 28, 28
int_log2(0x20000000) = 29, 29
int_log2(0x40000000) = 30, 30
int_log2(0x80000000) = 31, 31
となった。手元の Intel CPU では AMD 仕様向けの処理でよさそうです。
AMD 仕様向けの処理がダメな CPU があるのなら、どれなのか知りたい...
追記: 80486 の初期バージョンが該当するらしい