閱讀匯編教程,我看到“功能”序言/結語包括:
push bp
mov bp, sp
---
pop bp
但我也看到了其他一些使用pushathenpopa來保存暫存器的教程。那么為什么除了設定 bp 之外,函式 prologue/epilogue 不執行 pusha/popa 來保存暫存器背景關系?
uj5u.com熱心網友回復:
它們不會保存所有暫存器,因為您并不總是需要保存所有暫存器。保存和恢復它們很慢。是的,這是一個很小的單一指令,看起來很節省,但它需要時間和堆疊空間。要了解保存的內容,請查看呼叫約定。
https://en.wikipedia.org/wiki/X86_calling_conventions
PUSHA/PUSHAD—壓入所有通用暫存器
這些是緩慢的指令。在 Skylake 上,PUSHA 需要 19 uop 和 8 個吞吐量周期。POPA 需要 18 uop 和 8c 的吞吐量。
此外,PUSHA/PUSHAD 在 64 位中無效。當 x86 擴展到 64b 時,它們被 AMD 清除,然后被 Intel 清除。
現代編譯器反其道而行之,盡可能避免保存暫存器。LLVM 執行稱為收縮包裝的分析,其中 prolog 被向前推以允許快速提前退出。
https://llvm.org/doxygen/ShrinkWrap_8cpp_source.html
這些是可怕的,可怕的,沒有好的,非常糟糕的指示。
uj5u.com熱心網友回復:
閱讀匯編教程,我看到了“函式”序言/結語......
對于真正的匯編語言(您不必遵守不同語言的呼叫約定),“函式序言/結尾”一詞沒有意義。
對于“旨在符合某些其他語言的呼叫約定的匯編語言”;您只需要保存/恢復一些暫存器(可能沒有)。
舉個例子;對于 CDECL,EAX、ECX 和 EDX 的內容可以被被呼叫者丟棄,永遠不需要被被呼叫者保存/恢復(如果他們關心,呼叫者需要保存它們);如果一個函式不使用任何其他暫存器,被呼叫者也不需要保存或恢復任何其他暫存器。還要注意,“EBP 作為幀指標”是過時的垃圾(它存在是因為除錯器不是很好,并且在除錯資訊改進時變得毫無意義——例如 DWARF 除錯資訊等)。這些東西結合在一起意味著這樣的事情對于 CDECL 來說具有可接受的序言和尾聲:
myFunction:
mov eax,12345 ;eax = returned value
ret
如果確實需要保存和恢復“大量”暫存器;pusha很慢(微編碼),一系列多push條指令也很慢(存盤的地址取決于最近修改的 ESP 中的值)。典型的方法是自己做,例如:
;Don't bother saving EAX, ECX, EDX.
sub esp,16 ;Create space to save 4 registers (but maybe more for local variables)
mov [esp],ebx
mov [esp 4],esi
mov [esp 8],edi
mov [esp 12],ebp
然而; 成本是空間。在代碼大小極其有限(例如“512 位元組的一部分”)的引導加載程式中,聰明的程式員將使用真正的匯編語言(其中“函式序言/結尾”沒有意義),初學者可能會使用它pusha來節省空間(沒有意識到他們沒有理由關心其他編程語言的呼叫約定)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/398683.html
