x64 支持是否意味着 BMI1 支持?

Does x64 support imply BMI1 support?

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

不,当然不是! x86-64 是 2003 年末的新版本 (AMD K8),只有遗留的 bsfbsr 位扫描指令,以及 none 其余的 BMI1。

第一个支持 BMI1 的英特尔 CPU 是 2013 年的 Haswell。(同时引入了 BMI2。)
第一个支持 BMI1 的 AMD CPU 是 2012 年的 Piledriver。
AMD ABM (Advanced Bit Manipulation)在K10及之后的AMD CPUs中只增加了popcntlzcnt,没有tzcnt.

Wikipedia Bit Manipulation Instruction Sets: Supporting CPUs. Note that Celeron/Pentium branded CPUs don't decode VEX prefixes, so they have AVX and BMI1/BMI2 disabled because BMI1 and 2 each include some VEX-coded instructions like andn and blsr. This sucks; BMI1/2 are most useful when compilers can use it everywhere 贯穿整个可执行文件以实现更有效的变量计数转换和窥视孔,因此仍在销售没有 BMI1/2 的新 CPUs 并不能使我们更接近于能够像我们在 32 位模式下对 P6 cmov 所做的那样将它们视为基线​​。


旧 CPUs

上的 TZCNT 解码

由于您具体提到了 tzcnt,它的机器代码编码是 rep bsf,所以较旧的 CPU 将作为 BSF 执行它。如果输入非零,这会产生与 tzcnt 相同的结果。即,当输入非零时,tzcnt 在所有 x86 CPUs(从 386 开始)“有效”。

但是当它为零时,tzcnt would produce the operand-size (e.g. 64), but bsf 不修改目标寄存器。 tzcnt 根据结果设置 FLAGS,bsf 根据输入设置 FLAGS。 AMD 在其 ISA 参考手册中记录了 dst-unmodified 行为。 Intel 仅将其记录为“未定义值”,但实现了与 AMD 相同的行为,至少在现有 CPUs 中是这样。

(这就是为什么 bsf / bsr 对所有 CPU 具有输出依赖性,例如 add。不幸的是 tzcnt / lzcnt 在 Skylake 之前也对 Intel Sandybridge 系列有错误的依赖:Why does breaking the "output dependency" of LZCNT matter?. And why popcnt does on SnB-family before Cannon / Ice Lake, because .)


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

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

lzcnt 不存在此类向后/向前兼容。将它解码为 rep bsr 并忽略无意义的 rep 前缀会给你 31 - lzcnt(x),位索引。 https://fgiesen.wordpress.com/2013/10/18/bit-scanning-equivalencies/

一个方便的技巧是 ctz( x | 0x80000000 ) 因为 OR 很便宜 1,并且保证总是有一个非零位bsf寻找。它不会更改任何非零 x 的结果,因为它是 bsf 将查看的最后一位。这个技巧也适用于 __builtin_clz(x|1) / bsr,它甚至更好,因为 or reg, imm8 甚至比 imm32.

更短

脚注 1:or reg, imm32 适用于 32 位常量; bts reg,63 在某些 CPU 上实现 64 位输入 x|(1ULL<<63) 的成本较低。