x86 汇编器:浮点比较
Posted
技术标签:
【中文标题】x86 汇编器:浮点比较【英文标题】:x86 assembler: floating point compare 【发布时间】:2011-10-26 19:34:23 【问题描述】:作为编译器项目的一部分,我必须为 x86 编写 GNU 汇编代码来比较浮点值。我试图找到有关如何在线执行此操作的资源,并且据我了解它的工作原理如下:
假设我要比较的两个值是浮点堆栈上的唯一值,那么 fcomi
指令将比较这些值并设置 CPU 标志,以便 je
、jne
、@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 它仍然不会,这是一个逻辑矛盾。 je
和 jne
但似乎工作正常。
我做错了什么?
PS:fcomip 是只弹出一个值还是同时弹出两个值?
【问题讨论】:
【参考方案1】:TL:DR:使用高于/低于条件(如无符号整数)来测试比较结果。
对于各种historical reasons(映射from FP status word to FLAGS via fcom
/ fstsw
/ sahf
,fcomi
(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 的作用,您可能需要额外的 jp
或 jnp
。但并非总是如此:例如,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 汇编器:浮点比较的主要内容,如果未能解决你的问题,请参考以下文章
GNU 汇编器 x86 指令后缀(如“mov.s”中的“.s”)如何工作?
Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段