作为编译器项目的一部分,我必须为x86编写GNU汇编代码来比较浮点值。我试图找到有关如何在线进行此操作的资源,据我所知,它的工作原理如下:
假设我要比较的to值是浮点堆栈上的唯一值,那么fcomi
指令将比较这些值并设置CPU标志,以便je, jne, jl,
...可以使用说明。
我问,因为这只会有效。例如:
.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
即使我认为应该打印“Hallo”,如果你切换f1和f2,它仍然不会是一个逻辑矛盾。然而,je
和jne
似乎工作得很好。
我做错了什么?
PS:fcomip只弹出一个值还是弹出两个?
答案 0 :(得分:38)
这一切都来自 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
没有设置符号或溢出!
根据您希望跳跃的条件,您应该查看可能的比较结果,并决定何时跳跃。
+--------------------+---+---+---+ | Comparison results | Z | P | C | +--------------------+---+---+---+ | ST0 > ST(i) | 0 | 0 | 0 | | ST0 < ST(i) | 0 | 0 | 1 | | ST0 = ST(i) | 1 | 0 | 0 | +--------------------+---+---+---+
我已经制作了这张小桌子,以便更容易理解:
+--------------+---+---+-----+------------------------------------+ | Test | Z | C | Jcc | Notes | +--------------+---+---+-----+------------------------------------+ | ST0 < ST(i) | X | 1 | JB | ZF will never be set when CF = 1 | | ST0 <= ST(i) | 1 | 1 | JBE | Either ZF or CF is ok | | ST0 == ST(i) | 1 | X | JE | CF will never be set in this case | | ST0 != ST(i) | 0 | X | JNE | | | ST0 >= ST(i) | X | 0 | JAE | As long as CF is clear we are good | | ST0 > ST(i) | 0 | 0 | JA | Both CF and ZF must be clear | +--------------+---+---+-----+------------------------------------+ Legend: X: don't care, 0: clear, 1: set
换句话说,条件代码与使用无符号比较的条件代码相匹配。如果您使用的是FMOVcc
,也会如此。
如果fcomi
的任一(或两者)操作数是NaN,则设置ZF=1 PF=1 CF=1
。 (FP比较有4种可能的结果:>
,<
,==
或无序结果。如果您关心代码对NaN的作用,则可能需要额外的jp
或jnp
。但并非总是如此:例如,ja
仅在CF = 0且ZF = 0时才为真,因此在无序情况下不会采用它。如果您希望无序案例采用与下面相同或相同的执行路径,则只需ja
即可。
如果您要打印({。{1}}),请使用JA
,如果不打印,则应使用if (!(f2 > f1)) { puts("hello"); }
(对应JBE
)。 (注意这可能有点令人困惑,因为如果我们不跳,我们只打印)。
关于你的第二个问题:默认情况下if (!(f2 <= f1)) { puts("hello"); }
不会弹出任何内容。你想要它的近亲[{1}}弹出fcomi
。您应该在使用后始终清除fpu寄存器堆栈,因此假设您希望打印消息,所有程序中的所有程序都会以此结束:
fcomip