16

As part of a compiler project I have to write GNU assembler code for x86 to compare floating point values. I have tried to find resources on how to do this online and from what I understand it works like this:

Assuming the two values I want to compare are the only values on the floating point stack, then the fcomi instruction will compare the values and set the CPU-flags so that the je, jne, jl, ... instructions can be used.

I'm asking because this only works sometimes. For example:

.section    .data
msg:    .ascii "Hallo\n\0"
f1:     .float 10.0
f2:     .float 9.0

.globl main
    .type   main, @function
main:
    flds f1
    flds f2
    fcomi
    jg leb
    pushl $msg
    call printf
    addl $4, %esp
leb:
    pushl $0
    call exit

will not print "Hallo" even though I think it should, and if you switch f1 and f2 it still won't which is a logical contradiction. je and jne however seem to work fine.

What am I doing wrong?

PS: does the fcomip pop only one value or does it pop both?

4

1 回答 1

41

TL:DR:使用高于/低于条件(如无符号整数)来测试比较的结果

由于各种历史原因(从 FP 状态字映射到 FLAGS via fcom//fstswsahf which fcomi PPro 中的新功能)匹配),FP 比较集合 CF,而不是 OF / SF。另见http://www.ray.masmcode.com/tutorial/fpuchap7.htm


这一切都来自Intel 64 and IA-32 Architectures Software Developer's Manuals 的第 2 卷。

FCOMI只设置一些标志CMP。您的代码有%st(0) == 9%st(1) == 10。(因为它们被加载到堆栈上),参考第 2A 卷第 3-348 页的表格,您可以看到这是“ST0 < ST(i)”的情况,因此它将清除 ZF 和 PF 并设置CF。同时在 pg。3-544 卷。2A 你可以读到这JG意味着“如果更大(ZF = 0 和 SF = OF)则跳短”。换句话说,它正在测试符号、溢出和零标志,但FCOMI不设置符号或溢出!

根据您希望跳跃的条件,您应该查看可能的比较结果并决定何时要跳跃。

+--------------------+---+---+---+
| 比较结果 | Z | 磷 | C |
+--------------------+---+---+---+
| ST0 > ST(i) | 0 | 0 | 0 |
| ST0 < ST(i) | 0 | 0 | 1 |
| ST0 = ST(i) | 1 | 0 | 0 |
| 无序 | 1 | 1 | 1 | 一个或两个操作数都是 NaN。
+--------------------+---+---+---+

我制作了这张小桌子,以便更容易弄清楚:

+--------------+---+---+------+-------- ----------------+
| 测试 | Z | C | Jcc | 笔记 |
+--------------+---+---+------+-------- ----------------+
| ST0 < ST(i) | X | 1 | JB | 当 CF = 1 | ZF 永远不会被设置
| ST0 <= ST(i) | 1 | 1 | JBE | ZF或CF都可以|
| ST0 == ST(i) | 1 | X | 乙脑 | 在这种情况下永远不会设置 CF |
| ST0 != ST(i) | 0 | X | 杰尼 | |
| ST0 >= ST(i) | X | 0 | JAE | 只要CF清楚我们就很好|
| ST0 > ST(i) | 0 | 0 | JA | CF和ZF都必须清楚 |
+--------------+---+---+------+-------- ----------------+
图例:X:不关心,0:清除,1:设置

换句话说,条件代码与使用无符号比较的条件代码相匹配。如果您使用FMOVcc.

如果其中一个(或两个)操作数fcomi为 NaN,则设置ZF=1 PF=1 CF=1. (FP 比较有 4 种可能的结果:><==或无序)。如果您关心您的代码对 NaN 的作用,您可能需要一个额外的jpjnp. 但并非总是如此:例如,ja仅当 CF=0 且 ZF=0 时才为真,因此在无序情况下将不取。如果您希望无序案例采用与以下相同或相同的执行路径,那么ja您只需要。


JA如果您希望它打印(即if (!(f2 > f1)) { puts("hello"); }),JBE如果您不想打印(对应于),您应该在此处使用if (!(f2 <= f1)) { puts("hello"); }。(请注意,这可能有点令人困惑,因为我们只在不跳转时才打印)。


关于您的第二个问题:默认情况下fcomi不会弹出任何内容。你想要它的近亲fcomippops %st0。您应该始终在使用后清除 fpu 寄存器堆栈,因此假设您希望打印消息,您的程序最终会像这样结束:

.section    .rodata
msg:    .ascii "Hallo\n\0"
f1:     .float 10.0
f2:     .float 9.0 

.globl main
    .type   main, @function
main:
    flds   f1
    flds   f2
    fcomip
    fstp   %st(0) # to clear stack
    ja     leb # won't jump, jbe will
    pushl  $msg
    call   printf
    addl   $4, %esp
leb:
    pushl  $0
    call   exit
于 2011-08-14T15:36:33.620 回答