x64支持是否暗示BMI1支持?

时间:2020-04-25 08:18:36

标签: assembly x86-64 instruction-set bmi

可以安全地假设x64构建可以使用TZCNT而无需通过cpu标志检查其支持吗?

1 个答案:

答案 0 :(得分:8)

不,当然不是! x86-64是2003年底推出的新产品(AMD K8),仅具有传统的bsfbsr位扫描指令,而其余的BMI1都没有。

2013年,第一个支持BMI1的Intel CPU是Haswell 1 。(还引入了BMI2。)
2012年,第一个支持BMI1的AMD CPU是打桩机。
在K10和更高版本的CPU中,AMD ABM (Advanced Bit Manipulation)仅添加了popcntlzcnt,而没有添加tzcnt

维基百科Bit Manipulation Instruction Sets: Supporting CPUs。请注意,Celeron / Pentium品牌的CPU不会解码VEX前缀,因此它们禁用了AVX和BMI1 / BMI2,因为它们包含诸如andnblsr之类的指令。真烂BMI1 / 2在整个可执行文件中都是most useful when compilers can use it everywhere,可实现更有效的变量计数转换和窥视孔,因此仍在销售不带BMI1 / 2的新CPU并不能使我们更像P6那样将其视为基准cmov(在32位模式下)。


自从您特别提到tzcnt以来,它的机器代码编码为rep bsf,因此较旧的CPU将把它作为BSF执行。如果输入非零,则产生与tzcnt相同的结果。即 tzcnt在输入为非零时,可以在所有x86 CPU(自386开始)上工作。

但是当它为零时,tzcnt会产生操作数大小(例如64),但是bsf会保留目标寄存器不变。 tzcnt根据结果设置标志,bsf根据输入设置标志。 AMD在其ISA参考手册中记录了未经修改的行为。英特尔仅将其记录为“未定义的值”,但至少在现有CPU中实现了与AMD相同的行为。

(这就是bsf / bsr对所有CPU都有输出依赖性的原因。不幸的是,tzcnt / lzcnt在Skylake之前也对Intel Sandybridge系列有错误的依赖性:Why does breaking the "output dependency" of LZCNT matter?。为什么popcnt在SnB系列before Cannon / Ice Lake上也如此,因为it shares the same execution unit。)


tzcnt在AMD上明显更快,因此针对“通用”或AMD CPU进行调整的编译器通常会使用tzcnt而不是bsf而不检查CPU功能。

例如用于GNU C __builtin_ctz。该内在函数对input = 0的行为未定义,因此允许其仅使用bsf而无需检查0。因此,由于在任何情况下都无法保证结果,因此也允许使用tzcnt

Why does TZCNT work for my Sandy Bridge processor?

lzcnt不存在这种向后/向前兼容。将其解码为rep bsr,而忽略无意义的rep前缀,则会得到31 - lzcnt(x),即位索引。 https://fgiesen.wordpress.com/2013/10/18/bit-scanning-equivalencies/

一个方便的技巧是ctz( x | 0x80000000 ),因为OR很便宜(至少对于32位常量而言),并且保证bsf总是有一个非零的位。但不会更改任何非零x的结果,因为它是bsf所要查看的最后一位。对于__builtin_clz(x|1) / bsr,这是一个更好的技巧。