作为编译器项目的一部分,我必须为 x86 编写 GNU 汇编代码来比较浮点值。我试图找到有关如何在线执行此操作的资源,据我所知,它是这样工作的:
假设我要比较的两个值是浮点堆栈中唯一的值,那么
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 是只弹出一个值还是同时弹出两个值?
TL:DR: 使用上面/下面的条件(比如无符号整数)来测试比较的结果.
/
fstsw
/
sahf
将从FP状态字映射到FLAGS,其中fcomi
(PPro中的新功能)匹配),FP比较集合CF,而不是OF / 顺丰。另见http://www.ray.masmcode.com/tutorial/fpuchap7.htm
现代 SSE/SSE2 标量与 FLAGS 进行比较 也遵循这个,使用 [u]
comiss
/ sd
。 (与 SIMD 比较不同,它有一个谓词作为指令的一部分,作为立即数,因为它们只为每个元素产生一个全零/全一结果,而不是一组标志。)
这一切都来自 Intel 64 和 IA-32 架构软件开发人员手册的第 2 卷。
FCOMI
仅设置 CMP
所做的一些标志。您的代码有 %st(0) == 9
和 %st(1) == 10
。 (因为它是一个堆栈,它们被加载到),参考第2A卷3-348页的表格你可以看到是这样的“ST0< ST(i)", so it will clear ZF and PF and set CF. Meanwhile on pg. 3-544 Vol. 2A you can read that JG
意味着“如果更大则跳空(ZF = 0和SF = OF)”。换句话说,它正在测试符号、溢出和零标志,但是FCOMI
没有设置符号或溢出!
根据你想跳的条件,你应该看看可能的比较结果,然后决定你什么时候跳。
+--------------------+---+---+---+ |比较结果 | Z |磷 |丙 | +--------------------+---+---+---+ | ST0 > ST(i) | 0 | 0 | 0 | | ST0< ST(i) | 0 | 0 | 1 | | ST0 = ST(i) | 1 | 0 | 0 | | unordered | 1 | 1 | 1 | one or both operands were NaN. +--------------------+---+---+---+
我制作了这张小桌子,以便更容易理解:
+------------+---+---+-----+-------------------- ------------------+ |测试 | Z |丙 |联合会 |备注 | +--------------+---+---+-----+------------------ --------------+ | 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) | × | 0 |杰 |只要CF清楚我们就好 | | ST0 > ST(i) | 0 | 0 |贾| CF和ZF都必须清楚 | +--------------+---+---+-----+------------------ --------------+ 图例:X:不关心,0:清除,1:设置
换句话说,条件代码与使用无符号比较的条件代码相匹配。如果您使用
FMOVcc
.,情况也是如此
如果
fcomi
的一个(或两个)操作数是NaN,它设置ZF=1 PF=1 CF=1
。 (FP 比较有 4 种可能的结果:>
、<
、==
或无序)。如果你关心你的代码对 NaN 做了什么,你可能需要额外的 jp
或 jnp
。但并非总是如此:例如,ja
仅在 CF=0 且 ZF=0 时为真,因此在无序情况下不会被采用。如果您希望无序案例采用与 below 或 equal 相同的执行路径,那么ja
就是您所需要的。
在这里你应该使用
JA
如果你想要它打印(即if (!(f2 > f1)) { puts("hello"); }
)和JBE
如果你不这样做(对应于if (!(f2 <= f1)) { puts("hello"); }
)。 (注意这可能有点混乱,因为我们只在不跳转的情况下打印)。
关于你的第二个问题:默认情况下
fcomi
不会弹出任何东西。你想要它的近亲 fcomip
弹出 %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