Pav*_*l P 1 assembly x86-64 instruction-set bmi
可以安全地假设 x64 构建可以使用TZCNT而无需通过 cpu 标志检查其支持吗?
Pet*_*des 10
不,当然不是!x86-64 是 2003 年末的新版本(AMD K8),只有旧版本bsf和bsr位扫描指令,没有 BMI1 的其余部分。
第一个支持 BMI1 的 Intel CPU 是 2013 年的 Haswell。(也引入了 BMI2。)
第一个支持 BMI1 的 AMD CPU 是 2012 年的 Piledriver。K10 中的
AMD ABM(高级位操作)和后来的 AMD CPU 仅添加popcnt和lzcnt,没有tzcnt。
维基百科位操作指令集:支持 CPU。请注意,Celeron/Pentium 品牌的 CPU 不解码 VEX 前缀,因此它们禁用了 AVX 和 BMI1/BMI2,因为 BMI1 和 2 均包含一些 VEX 编码的指令,例如andn和blsr。这很糟糕;当编译器可以在整个可执行文件中的任何地方使用BMI1/2 以实现更高效的可变计数移位和窥视孔时,BMI1/2最有用,因此仍然销售没有 BMI1/2 的新 CPU 并不能让我们更接近于能够像我们一样将它们视为基线为 P6 做cmov在 32 位模式下。
由于您tzcnt特别提到,它的机器代码编码是rep bsf如此旧的 CPU 将其作为 BSF 执行。这会产生与tzcnt输入非零相同的结果。即tzcnt当输入非零时在所有 x86 CPU(自 386 起)上“工作”。
但是当它为零时,tzcnt会产生操作数大小(例如 64),但不会bsf修改目标寄存器。 tzcnt根据结果bsf和输入设置 FLAGS 。AMD 在其 ISA 参考手册中记录了未修改的 dst 行为。英特尔仅将其记录为“未定义值”,但实现了与 AMD 相同的行为,至少在现有 CPU 中是如此。
(这就是为什么bsf/bsr对所有 CPU 都有输出依赖性,例如add。不幸的是tzcnt/lzcnt在 Skylake 之前/也对 Intel Sandybridge 系列有错误依赖性:为什么打破 LZCNT 的“输出依赖性”很重要?。为什么popcnt对 SnB 系列在 Cannon / Ice Lake 之前,因为它共享相同的执行单元。)
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 位输入的成本较低。
| 归档时间: |
|
| 查看次数: |
247 次 |
| 最近记录: |