x86 汇编器:浮点比较

问题描述 投票:0回答:1

作为编译器项目的一部分,我必须为 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 是只弹出一个值还是同时弹出两个值?

x86 floating-point compare assembly gnu-assembler
1个回答
42
投票

TL:DR: 使用上面/下面的条件(比如无符号整数)来测试比较的结果.

由于各种历史原因(通过fcom

 / 
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
© www.soinside.com 2019 - 2024. All rights reserved.