x86 汇编器:浮点比较

Posted

技术标签:

【中文标题】x86 汇编器:浮点比较【英文标题】:x86 assembler: floating point compare 【发布时间】:2011-10-26 19:34:23 【问题描述】:

作为编译器项目的一部分,我必须为 x86 编写 GNU 汇编代码来比较浮点值。我试图找到有关如何在线执行此操作的资源,并且据我了解它的工作原理如下:

假设我要比较的两个值是浮点堆栈上的唯一值,那么 fcomi 指令将比较这些值并设置 CPU 标志,以便 jejne、@987654324 @, ... 指令可以使用。

我问是因为这有时只有效。例如:

.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 是只弹出一个值还是同时弹出两个值?

【问题讨论】:

【参考方案1】:

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


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

FCOMI 只设置CMP 所做的一些标志。您的代码有%st(0) == 9%st(1) == 10。 (因为它们被加载到堆栈上),参考第 2A 卷中第 3-348 页的表格,您可以看到这是“ST0 JG 的意思是“如果更大(ZF=0 和 SF=OF)则跳短”。换句话说,它正在测试符号、溢出和零标志,但FCOMI 没有设置符号或溢出!

根据你想跳的条件,你应该看看可能的比较结果,然后决定你想跳的时间。

+--------------------+---+---+---+ |比较结果 | Z |磷 | C | +--------------------+---+---+---+ | ST0 > ST(i) | 0 | 0 | 0 | | ST0

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

+--------------+---+---+------+-------- ----------------+ |测试 | Z | C | Jcc |笔记 | +--------------+---+---+------+-------- ----------------+ | 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 时为真,因此在无序情况下将不取。如果你想让无序的case采用与下面相同或相等的执行路径,那么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

【讨论】:

非常令人印象深刻的答案。杰出的。一个小评论:ja 的反义词是jbe,而不是jb @Ray Toal:你是绝对正确的。尽管在这种情况下没有任何区别,但我更改了示例,因为这样更有意义。

以上是关于x86 汇编器:浮点比较的主要内容,如果未能解决你的问题,请参考以下文章

x86汇编实现一个注入器

x86汇编实现一个注入器

GNU 汇编器 x86 指令后缀(如“mov.s”中的“.s”)如何工作?

汇编器的NASM

现代32位或64位x86汇编

Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段