首页 > 解决方案 > x64 支持是否意味着 BMI1 支持?

问题描述

假设 x64 构建可以使用TZCNT而不通过 cpu 标志检查它的支持是否安全?

标签: assemblyx86-64instruction-setbmi

解决方案


不,当然不是!x86-64 是 2003 年底的新产品(AMD K8),只有遗留bsfbsr位扫描指令,其余的 BMI1 没有。

第一个支持 BMI1 的 Intel CPU 是 2013 年的 Haswell。(也引入了 BMI2。)
第一个支持 BMI1 的 AMD CPU 是 2012 年的 Piledriver。K10和后来的 AMD CPU中的
AMD ABM(高级位操作)popcnt只添加了和lzcnt,而不是tzcnt

维基百科位操作指令集:支持 CPU。请注意,Celeron/Pentium 品牌的 CPU 不解码 VEX 前缀,因此它们禁用了 AVX 和 BMI1/BMI2,因为 BMI1 和 2 都包含一些 VEX 编码指令,例如andnblsr。这很糟糕;当编译器可以在整个可执行文件中的任何地方使用BMI1/2 以实现更有效的变量计数转换和窥视孔时, BMI1/2最有用,因此仍然销售没有 BMI1/2 的新 CPU 并不能让我们更接近能够像我们一样将它们视为基线cmov在 32 位模式下为 P6 做。


旧 CPU 上的 TZCNT 解码

由于您特别提到tzcnt,它的机器代码编码是rep bsf如此旧的 CPU 将其作为 BSF 执行。tzcnt这会产生与输入非零相同的结果。即tzcnt当输入为非零时,在所有 x86 CPU(自 386 起)上“工作”。

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

(这就是为什么bsf/对bsr所有 CPU 都有add输出依赖tzcnt例如在 Cannon / Ice Lake 之前,因为它共享相同的执行单元。)lzcntpopcnt


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

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

为什么 TZCNT 适用于我的 Sandy Bridge 处理器?

不存在这样的向后/向前兼容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 上实现x|(1ULL<<63)64 位输入的成本较低。


推荐阅读