x86 汇编器:浮点比较
作为编译器项目的一部分,我必须为 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”,也不会打印“Hallo”,如果你切换 f1 和 f2,它仍然不会打印,这是一个逻辑矛盾。然而, je
和 jne
似乎工作正常。
我做错了什么?
PS:fcomip 只弹出一个值还是同时弹出两个值?
As part of a compiler project I have to write GNU assembler code for x86 to compare floating point values. I have tried to find resources on how to do this online and from what I understand it works like this:
Assuming the two values I want to compare are the only values on the floating point stack, then the fcomi
instruction will compare the values and set the CPU-flags so that the je
, jne
, jl
, ... instructions can be used.
I'm asking because this only works sometimes. For example:
.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
will not print "Hallo" even though I think it should, and if you switch f1 and f2 it still won't which is a logical contradiction. je
and jne
however seem to work fine.
What am I doing wrong?
PS: does the fcomip pop only one value or does it pop both?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
TL:DR: 使用上方/下方条件(如无符号整数)来测试比较结果。
对于各种 历史原因(映射通过
fcom
/fstsw
从 FP 状态字到 FLAGS /sahf
其中fcomi
(PPro 中的新功能)匹配),FP 比较集合 CF,而不是 OF / SF。另请参阅http://www.ray.masmcode.com/tutorial/fpuchap7.htm< /a>现代 SSE/SSE2 标量与 FLAGS 也遵循此操作,与 [u]
commiss
/sd
。 (与 SIMD 比较不同,SIMD 比较有一个谓词作为指令的一部分,作为立即数,因为它们只为每个元素生成一个全零/全一结果,而不是一组标志。)这全部来自卷2 Intel 64 和 IA-32 架构软件开发人员指南手册。
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
不会设置符号或溢出!根据您希望跳转的条件,您应该查看可能的比较结果并决定何时跳转。
我制作了这个小表,以便更容易理解:
换句话说,条件代码与使用无符号比较的条件代码相匹配。如果您使用
FMOVcc
,情况也是如此。如果
fcomi
的一个(或两个)操作数为 NaN,则设置ZF=1 PF=1 CF=1
。 (FP 比较有 4 种可能的结果:>
、<
、==
或无序)。如果您关心代码如何处理 NaN,则可能需要额外的jp
或jnp
。但并非总是如此:例如,ja
仅当 CF=0 且 ZF=0 时才为 true,因此在无序情况下不会被采用。如果您希望无序案例采用与以下或等于相同的执行路径,那么ja
就是您所需要的。在这里,如果您希望它打印(即
if (!(f2 > f1)) { put("hello"); }
)和 < code>JBE 如果您不这样做(对应于if (!(f2 <= f1)) { puts("hello"); }
)。 (请注意,这可能有点令人困惑,因为我们只有在不跳转时才打印)。关于你的第二个问题:默认情况下
fcomi
不会弹出任何内容。您需要它的近亲fcomip
,它会弹出%st0
。使用后您应该始终清除 fpu 寄存器堆栈,因此假设您希望打印消息,那么您的程序最终会像这样:TL:DR: Use above / below conditions (like for unsigned integer) to test the result of compares.
For various historical reasons (mapping from FP status word to FLAGS via
fcom
/fstsw
/sahf
whichfcomi
(new in PPro) matches), FP compares set CF, not OF / SF. See also http://www.ray.masmcode.com/tutorial/fpuchap7.htmModern SSE/SSE2 scalar compares into FLAGS follow this as well, with [u]
comiss
/sd
. (Unlike SIMD compares, which have a predicate as part of the instruction, as an immediate, since they only produce a single all-zeros / all-ones result for each element, not a set of FLAGS.)This is all coming from Volume 2 of Intel 64 and IA-32 Architectures Software Developer's Manuals.
FCOMI
sets only some of the flags thatCMP
does. Your code has%st(0) == 9
and%st(1) == 10
. (Since it's a stack they're loaded onto), referring to the table on page 3-348 in Volume 2A you can see that this is the case "ST0 < ST(i)", so it will clear ZF and PF and set CF. Meanwhile on pg. 3-544 Vol. 2A you can read thatJG
means "Jump short if greater (ZF=0 and SF=OF)". In other words it's testing the sign, overflow and zero flags, butFCOMI
doesn't set sign or overflow!Depending on which conditions you wish to jump, you should look at the possible comparison results and decide when you want to jump.
I've made this small table to make it easier to figure out:
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 setsZF=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 extrajp
orjnp
. 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, thenja
is all you need.Here you should use
JA
if you want it to print (ie.if (!(f2 > f1)) { puts("hello"); }
) andJBE
if you don't (corresponds toif (!(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 cousinfcomip
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: