2011-08-14 81 views
15

作爲編譯器項目的一部分,我必須編寫用於x86的GNU彙編代碼來比較浮點值。我試圖找到如何在網上做這方面的資源,並從我的理解它是這樣工作的:x86彙編:浮點比較

假設我要比較的值是浮點堆棧上的唯一值,那麼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 

將不打印「喂」儘管我認爲它應該,如果你切換f1和f2它仍然不會是一個邏輯上的矛盾。然而,jejne似乎工作正常。

我在做什麼錯?

PS:fcomip是否只彈出一個值,還是會同時彈出兩個值?

回答

34

這是來自 Intel 64 and IA-32 Architectures Software Developer's Manuals的第2卷的所有內容。

FCOMI只設置一些CMP所做的標誌。您的代碼有%st(0) == 9%st(1) == 10。 (因爲它是一個堆棧,所以它們被加載到),參考第2A卷第3-348頁的表格,你可以看到這是「ST0 < ST(i)」的情況,所以它將清除ZF和PF並設置CF.同時在第pg。 3-544 Vol。 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 | 
+--------------------+---+---+---+ 

我做了這個小桌子,使其更容易弄清楚:

 
+--------------+---+---+-----+------------------------------------+ 
| 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,也是如此。

如果fcomi的任一個(或兩個)操作數是NaN,則它設置ZF=1 PF=1 CF=1。 (FP比較有4個可能的結果:>,<==或無序)。如果你關心你的代碼如何處理NaN,你可能需要額外的jpjnp。但並非總是如此:例如,只有當CF = 0且ZF = 0時,ja才爲真,因此它在無序情況下不會被採用。如果你想讓無序的情況下采取相同的執行路徑或相同的,那麼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 
+7

非常令人印象深刻的答案。優秀。一個小小的評論:「ja」的反面是「jbe」,而不是「jb」。 –

+2

@Ray Toal:你是對的。儘管在這種情況下它沒有任何區別,但我改變了這個例子,因爲這樣做更有意義。 – user786653

+0

不錯!十分感謝! – JustMaximumPower