提问人:JustMaximumPower 提问时间:8/14/2011 最后编辑:ecmJustMaximumPower 更新时间:3/19/2022 访问量:21161
x86 汇编程序:浮点比较
x86 assembler: floating point compare
问:
作为编译器项目的一部分,我必须为 x86 编写 GNU 汇编代码来比较浮点值。我试图找到有关如何在线执行此操作的资源,据我所知,它的工作原理如下:
假设我要比较的两个值是浮点堆栈上的唯一值,那么该指令将比较这些值并设置 CPU 标志,以便 、 、 、 ...可以使用说明。fcomi
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:使用高于/低于条件(如无符号整数)来测试比较结果。
由于各种历史原因(通过 fcom
(PPro 中的新功能)匹配的 fcom
/fstsw
/sahf
从 FP 状态字映射到 FLAGS),FP 比较的是集 CF,而不是 OF / SF。另请参阅 http://www.ray.masmcode.com/tutorial/fpuchap7.htm
现代 SSE/SSE2 标量与 FLAGS 相比也遵循这一点,使用 [u] / .(与 SIMD 比较不同,SIMD 比较将谓词作为指令的一部分,作为即时的,因为它们只为每个元素生成一个全零/全一结果,而不是一组 FLAGS。comiss
sd
这些都来自英特尔 64 和 IA-32 架构软件开发人员手册的第 2 卷。
FCOMI
仅设置一些这样做的标志。您的代码具有 和 .(因为它是它们加载到的堆栈),参考第 2A 卷第 3-348 页上的表格,您可以看到这种情况是“ST0 < ST(i)”,因此它将清除 ZF 和 PF 并设置 CF。 同时,在第 2A 卷第 3-544 页,您可以读到这意味着“如果更大(ZF=0 和 SF=OF)则跳空”。换句话说,它正在测试符号、溢出和零标志,但不设置符号或溢出!CMP
%st(0) == 9
%st(1) == 10
JG
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. +--------------------+---+---+---+
我制作了这张小表格,以便更容易弄清楚:
+--------------+---+---+-----+------------------------------------+ | 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
换言之,条件代码与使用无符号比较的条件代码相匹配。如果您使用 .FMOVcc
如果任一(或两者)操作数为 NaN,则设置 .(FP 比较有 4 种可能的结果:、、或无序)。如果你关心你的代码对 NaN 做了什么,你可能需要一个额外的 or .但并非总是如此:例如,仅当 CF=0 且 ZF=0 时才为真,因此在无序情况下不会取。如果您希望无序情况采用与以下或相等相同的执行路径,那么您只需要这样做。fcomi
ZF=1 PF=1 CF=1
>
<
==
jp
jnp
ja
ja
如果您希望它打印(即),您应该在这里使用(即),如果您不打印(对应于 )。(请注意,这可能有点令人困惑,因为我们只有在不跳时才会打印)。JA
if (!(f2 > f1)) { puts("hello"); }
JBE
if (!(f2 <= f1)) { puts("hello"); }
关于你的第二个问题:默认情况下不会弹出任何东西。你想要它的近亲,它弹出.您应该始终在使用后清除 fpu 寄存器堆栈,因此总而言之,假设您希望打印消息,您的程序最终会像这样结束:fcomi
fcomip
%st0
.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
评论