有一些留下一个寄存器或一些带有未定义值的标志。英特尔和AMD 在这方面可能有所不同。
在某些情况下,真实硬件在这些未定义情况下的实际行为保留了对依赖它的某些旧软件的向后兼容性。例如,BSF输入 = 0 设置 ZF 并保持目标寄存器不变。 (在当前的 Intel 和 AMD 硬件上。我不知道旧的英特尔硬件是否有所不同,如果没有,bsf
/bsr
这并不是一个真正的指令示例executes不同的是,只是缺乏面向未来的书面保证.)
但不同之处在于,英特尔将其记录为让目标寄存器留下“未定义”的内容。AMD 的手册明确记录并保证 AMD CPU 将离开目的地未修改的在这种情况下。
AMD 的 AMD64 手册 (2017年3月) for bsr
/bsf
:
如果第二个操作数包含 0,则指令将 ZF 设置为 1,并且不更改目标寄存器的内容
所以这不是保证在纸面上,模拟 tzcnt / 实现是安全的std::countr_zero
as mov eax, 32
/ bsf eax, edx
,尽管这在实践中有效,并且可能会继续在未来的 CPU 上发挥作用。 (这就是为什么bsf
/ bsr
具有输出依赖性。)英特尔最终可能会记录此行为,在这种情况下,编译器将能够使用它来更有效地countr_zero
/ countl_zero
没有 BMI1。 Intel 最近确实记录了 AVX 暗示 16 字节对齐加载/存储在 Intel CPU 上是原子的,因此供应商记录其 CPU 多年来一直在做的事情并不是史无前例的。
如果性能差异很重要,那么有很多(请参阅x86标签维基)!
您不仅仅是在谈论不受支持的指令,是吗?就像某些非常早期的 x86-64 CPU 上的长模式不支持 LAHF/SAHF 一样?或者早期 K8 也不支持 CMPXCHG16B。
不支持的指令最有趣的情况是LZCNT解码为BSR在不支持它的 CPU 上,REP 前缀将被忽略。即使对于非零输入,它们也会返回相反的结果。 (_lzcnt_u32(x) == 31-bsr(x))。 TZCNT 在不支持它的 CPU 上类似地解码为 (REP) BSF,但它们执行相同的操作,除非输入 = 0。我之前没有提到这一点,因为以不同方式运行相同的机器代码与运行相同的机器代码不同操作说明不同地,但听起来这正是您所要求的。
我们只讨论非特权指令吗?特权指令的行为可能还有更多差异。例如,Intel 和 AMD 在 SYSRET 中都有不同的错误Linux 必须解决这个问题以避免恶意用户空间导致内核挂起。
另一种情况我不确定是否有效:预取W作为 NOP 在至少从 Core2 到 Haswell 的 Intel CPU 上运行,但在 AMD CPU 上运行(以及自 Broadwell 以来的 Intel CPU)作为实际的预取.
因此,有些 CPU 将其作为 NOP 运行,有些将其作为预取运行(因此无论哪种方式都没有架构上可见的影响),除了在古老的 CPU 上,它会作为非法 insn 出错。 64位Windows8.1显然要求 PREFETCHW 可以无故障地运行(这会阻止它在(某些?)64 位 Pentium4 CPU 上运行)。