Ori*_*ent 1 x86 assembly fpu x87
英特尔®64和IA-32体系结构软件开发人员手册(第2卷)表示,对于FST / FSTP FPU指令F 受影响的标志:
简单的测试(几乎没有任何价值)告诉我,C0,C2,C3不会受到影响:
#include <iostream>
#include <bitset>
#include <cstdlib>
#include <cstdint>
int main()
{
double x = -1.0;
std::uint16_t a = 0, b = 0;
asm volatile ("fld %[x] ; ftst ; fnstsw %%ax ; mov %%ax, %[a] ; fstp %%st ; fnstsw %%ax ; mov %%ax, %[b] ;"
: [a]"=m"(a), [b]"=m"(b)
: [x]"t"(x)
: "cc", "memory");
std::cout << std::bitset< 16 >(a) << std::endl;
std::cout << std::bitset< 16 >(b) << std::endl;
std::cout << " ^^^" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
“未定义”是什么意思?FSTP可以更改值,还是仅更改它们的值?
如果是这样,它将说“未修改”或“不受影响”。
“未定义”表示该值可以是任何值,并且在CPU微体系结构之间可能有所不同。某些CPU可能会保留旧值,某些CPU可能会清除或设置这些位,或者某些微体系结构状态会泄漏到每次运行指令时可能不同的位中。或者可以根据是否为NaN或Inf来设置它们。
但是,英特尔没有记录任何有关将发生哪种行为的任何记录。最重要的是,保持其选择的开放性使其在将来的CPU中可能会有所不同,因此,如果要编写安全的,面向未来的代码,则测试当前CPU的功能将毫无用处。
(尽管英特尔很可能会继续做目前所做的事情。但是一些重新设计可能会有所不同。)当然,其他厂商可能也会有所不同。值得检查AMD的x86手册,看看他们是否说出CPU的功能。
(产生一个未定义的值是不是像C未定义的行为。它不会破坏你的程序的其余部分。C2将读为0或1,而不是把它放到它甚至可能没有运行任何指令再次改变一些奇怪的状态记录为影响C2。)
当输入= 0的目标寄存器值为“ undefined”时,asm文档中“ undefined”的另一种用法用于bsf和bsr指令(并且ZF设置为1)。
实际上,在这种情况下,英特尔硬件不会更改目标。(所以有点像cmov您可以在运行之前将input = 0的结果放入输出中的情况bsf)。实际上, AMD确实在其AMD手册中记录了这种行为,并且大概是Intel关心的某些软件取决于这种行为。因此,英特尔极不可能更改它,而IDK为什么他们不只是记录它,以便我们可以利用它。 lzcnt并且tzcnt已经存在于BMI1中,并且输入= 0行为明确。
这种未经修改的dst行为会带来实际的性能损失:这意味着指令需要输入依赖项,否则它将成为只写目标。这会创建错误的依赖关系,从而防止exec混乱。(更糟糕的是,在Skylake之前的CPU上,lzcnt并且tzcnt具有相同的错误输出依赖项popcnt。Skylake之后的至少两个uarch仍然具有这种错误输出依赖项 。)