汇编:使用两个fld,fcomp,fnstsw和test 41h转换为if语句

时间:2015-08-01 07:45:59

标签: assembly x86 disassembly fpu x87

有人可以帮助我理解以下代码吗?

fld qword ptr [L1000F168]
fcomp   qword ptr [L1000A2F0]
fld qword ptr [L1000F168]
fnstsw  ax
test    ah,41h
jnz L100012F0

它是从执行代码转换为程序集的编译器的输出。

到目前为止我所知道的是

if(value[L1000F168] != value[L1000A2F0])
   continue following
else
   goto L100012F0

我说错了吗?

我不明白为什么[L1000F168]被加载两次以及为什么ah41h进行比较?因为41h表示(无效操作)或(堆栈错误)?

1 个答案:

答案 0 :(得分:1)

有关x87状态字的信息,请参阅this page

请注意,16位状态字存储在ax,但test指令仅查看高8位(ah)。因此41h匹配状态字的C0C3位。在ah上使用8位测试可以避免因使用带有test的16位imm16而导致英特尔CPU速度减慢。 (操作数大小前缀改变指令其余部分的长度,即所谓的长度改变前缀解码器停止)。

fld / fcomp / fld / fnstsw:这对我来说也很奇怪。我想知道目标是基于内存位置来处理状态字集中的非正常位,而是基于C0设置fcomp等等。 (这不是你得到的,因为fld使C0-3未定义或设置。)

英特尔的insn参考手册说fld未定义C0C3,因此生成此代码的编译器取决于某些特定行为。也许没有fwait,状态字也不会从fcomp更新?我还没有弄清楚整个fwait的事情。我还没有找到(或者看起来很难)解释你何时需要fwait旧CPU,以及什么时候没有。据我了解,你从未在P5或更高版本上做过。

无论如何,我认为这段代码的作用是什么:

if (! ([L1000F168] > [L1000A2F0]))
    goto L100012F0;

测试不大于测试<=或无序测试。这假设fld实际上并未修改C0C3。也许手册只说它没有定义,因为它可以在加载非正规或其他条件时设置它们?或者它在目前的芯片中根本不存在。或者它可能是英特尔手册中的另一个错误,实际上这已经很好了。或许这段代码不再适用了!最好用调试器测试它。

我认为一种更合理的方式来获得程序员可能希望的结果

fld    qword ptr [L1000F168]
fcom   qword ptr [L1000A2F0]
fnstsw ax
test   ah,41h     ; C0|C3: both zero iff st(0) > [L1000A2F0]
jnz    L100012F0

由于OP说代码是编译器输出,我想这意味着它没有很好的x87优化来避免弹出/重新加载相同的值。

For P6 and later CPUs

fld    qword ptr [L1000F168]
fcomi  qword ptr [L1000A2F0]
jna    L100012F0   ;  jump unless st(0) > [L1000A2F0]

我可能有一种向后比较的感觉,所以如果某些事情没有意义,请仔细检查。

这段代码是什么来的?是编译输出吗?或者它可能是手写的asm?