x86 汇编器 : floating point compare

标签 x86 floating-point compare assembly gnu-assembler

作为编译器项目的一部分,我必须为 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 它仍然不会,这是一个逻辑矛盾。然而, jejne 似乎工作正常。

我做错了什么?

PS:fcomip 只弹出一个值还是同时弹出两个值?

最佳答案

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

对于各种historical reasons (映射 from FP status word to FLAGS via fcom / fstsw / sahffcomi (PPro 中的新功能)匹配),FP 比较集合 CF,而不是 OF/SF。另请参阅http://www.ray.masmcode.com/tutorial/fpuchap7.htm

现代 SSE/SSE2 标量与 FLAGS 进行比较 follow this as well ,与 [u]comis/sd。 (与 SIMD 比较不同,SIMD 比较有一个谓词作为指令的一部分,作为立即数,因为它们只为每个元素生成一个全零/全一结果,而不是一组标志。)

<小时/>

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

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 |
| unordered          | 1 | 1 | 1 |  one or both operands were NaN.
+--------------------+---+---+---+

I've made this small table to make it easier to figure out:

+--------------+---+---+-----+------------------------------------+
| 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

In other words the condition codes match those for using unsigned comparisons. The same goes if you're using FMOVcc.

If either (or both) operand to fcomi is NaN, it sets ZF=1 PF=1 CF=1. (FP compares have 4 possible results: >, <, ==, or unordered). If you care what your code does with NaNs, you may need an extra jp or jnp. But not always: for example, ja is only true if CF=0 and ZF=0, so it will be not-taken in the unordered case. If you want the unordered case to take the same execution path as below or equal, then ja is all you need.


Here you should use JA if you want it to print (ie. if (!(f2 > f1)) { puts("hello"); }) and JBE if you don't (corresponds to if (!(f2 <= f1)) { puts("hello"); }). (Note this might be a little confusing due to the fact that we only print if we don't jump).


Regarding your second question: by default fcomi doesn't pop anything. You want its close cousin fcomip which pops %st0. You should always clear the fpu register stack after usage, so all in all your program ends up like this assuming you want the message printed:

.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

关于x86 汇编器 : floating point compare,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7057501/

相关文章:

linux - 获取错误 : bad register name `%bpl'

assembly - 不延迟汇编的循环会使我的电脑崩溃吗?

x86 - 在 x64 中完全不使用分段吗?

java - java中截断结果集值的函数

floating-point - 如何找到 3D 矢量的哈希值?

python - 具有浮点值的 WxSlider?

ruby - 如何将字符串与数组进行比较以确定最高索引值?

assembly - x86 程序集的 BIOS 版本

c# - 如何比较两个十六进制数

mysql表优化,仅两列与日期时间比较