2012-10-29 27 views
5

在VC++的反彙編中,正在進行函數調用。編譯器在推送寄存器之前將本地指針指向寄存器:爲什麼VC + +編譯器MOV + PUSH參數而不是隻推動它們? x86

memcpy(nodeNewLocation, pNode, sizeCurrentNode); 
0041A5DA 8B 45 F8    mov   eax,dword ptr [ebp-8] 
0041A5DD 50     push  eax 
0041A5DE 8B 4D 0C    mov   ecx,dword ptr [ebp+0Ch] 
0041A5E1 51     push  ecx 
0041A5E2 8B 55 D4    mov   edx,dword ptr [ebp-2Ch] 
0041A5E5 52     push  edx 
0041A5E6 E8 67 92 FF FF  call  00413852 
0041A5EB 83 C4 0C    add   esp,0Ch 

爲什麼不直接推它們?即

push dword ptr [ebp-8] 

此外,如果你要做一個單獨的推,爲什麼不做手動。換句話說,而不是做「推EAX」上面,做

mov [esp], eax 

等等這樣做的好處是,做3個MOVS後,你可以做一個減法來設置新的堆棧指​​針,而不是含蓄用推動減去三次。

UPDATE --- Release版本

這是編譯版本相同的代碼:

; 741 : memcpy(nodeNewLocation, pNode, sizeCurrentNode); 

    00087 8b 45 f8  mov  eax, DWORD PTR _sizeCurrentNode$[ebp] 
    0008a 8b 7b 04  mov  edi, DWORD PTR [ebx+4] 
    0008d 50  push eax 
    0008e 56  push esi 
    0008f 57  push edi 
    00090 e8 00 00 00 00 call _memcpy 
    00095 83 c4 0c  add  esp, 12   ; 0000000cH 

絕對比調試版本更有效,但它仍然是做了MOV/PUSH組合。

+2

這是實際編譯在發佈模式?它看起來模糊調試 – harold

+0

它被編譯爲調試。爲什麼在這種情況下會有所作爲? –

+1

因爲編譯器不會在調試模式下關心這些事情。 – harold

回答

1

其實我找出了原因。它與指令在奔騰MMX上的流水線方式有關。有兩條流水線U和V,它們允許MMX處理器一次處理2條指令。如果它們是可配對的,則它們是。 PUSH不相互配對,但它們與MOV配對。所以,如果你寫:

mov eax, [indirect] 
mov esi, [indirect] 
push eax 
push esi 

那麼,什麼情況是,指示#1和#3獲得配對,#2,#4獲得配對左右,有效地,這四個指令相同的週期數與運行一個單獨的mov/push和一個單獨的mov/push比兩個[間接]快。這個確切的情況將在第4.3節,p。 41,Agner Fog的Microarchitecture優化指南的實例4.11a和4.11b,可以在因特網上廣泛獲得。

+3

他們實際上不會在這裏配對,因爲他們必須在PMMX中相鄰。如果MSVC仍在針對PMMX進行優化,我也會感到非常驚訝。 – harold

1

我懷疑它只是在調試版本中,或者在某些情況下通過流水線或其他考慮(例如,它可以將一個參數放入esi並在調用之後使用它)。我看着一些二進制文件和MSVC絕對不會用這樣推:

push ebx   ; mthd 
push dword ptr [ebp+place+4] 
push dword ptr [ebp+place] ; pos 
push [ebp+filedes] ; fh 
call __lseeki64_nolock 

(從CRT代碼)

至於第二個問題,解決esp指令長於推:"push eax"是一個而"mov [esp-8], eax"四個字節。實際上,這種方法(mov而不是push)默認情況下由GCC使用,因爲幾個版本之前(選項-maccumulate-outgoing-args)並且導致了notable increases in code size。據說它使代碼更快,但我不相信。

+0

我顯示發佈版本ASM,它具有類似的MOV。在查看項目的代碼時,編譯器從不進行間接推送,只有MOV。我的懷疑是,這可能是爲了支持x86的一個非常舊的版本,如286或386或其他東西。 –

+0

@TylerDurden:每個具有32位寄存器的x86系列成員都有「PUSH內存」指令。但是它們通常比MOV-PUSH組合執行速度慢,因爲現代微架構並不真正喜歡具有多個內存訪問的指令。 –

5

這是一個優化。它是在英特爾處理器手冊明確提及的,第4卷,部分12.3.3.6:

在的Intel Atom微架構,使用PUSH/POP指令來管理堆棧空間 和函數調用之間的地址調整/回報會更使用ENTER/LEAVE替代方案優於 。這是因爲PUSH/POP不需要MSROM 流程和堆棧指針地址更新在AGU完成。 當被調用函數需要返回給調用者時,被調用者可以發出POP指令 來恢復數據並從EBP中恢復堆棧指針。

裝配/編譯器編碼規則19.(MH衝擊中,M一般性)對於Intel Atom處理器,PUSH/POP的青睞寄存器形式,並避免使用LEAVE;使用LEA 來調整ESP而不是ADD/SUB。

本手冊的其餘部分並不清楚原因,但它確實提到了隱式ESP調整可能的3個週期AGU失速。

+0

這不是一個真正的Atom問題。我使用VS 10,它是在Atom存在之前編寫的編譯器。此外,Atom主要是筆記本電腦等低功耗架構。 –

+0

這是一個微架構問題,Atom只是實現它的許多芯片中的一個。 –

相關問題