這個問題在這里已經有了答案: 為什么 x86-64 System V 呼叫約定在暫存器中傳遞 args 而不僅僅是堆疊? (1 個回答) 2 小時前關閉。
這個 C 代碼
void test_function(int a, int b, int c, int d) {}
int main() {
test_function(1, 2, 3, 4);
return 0;
}
由 GCC(無標志,版本 12.1.1,目標x86_64-redhat-linux)編譯成
0000000000401106 <test_function>:
401106: 55 push rbp
401107: 48 89 e5 mov rbp,rsp
40110a: 89 7d fc mov DWORD PTR [rbp-0x4],edi
40110d: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
401110: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
401113: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
401116: 90 nop
401117: 5d pop rbp
401118: c3 ret
0000000000401119 <main>:
401119: 55 push rbp
40111a: 48 89 e5 mov rbp,rsp
40111d: b9 04 00 00 00 mov ecx,0x4
401122: ba 03 00 00 00 mov edx,0x3
401127: be 02 00 00 00 mov esi,0x2
40112c: bf 01 00 00 00 mov edi,0x1
401131: e8 d0 ff ff ff call 401106 <test_function>
401136: b8 00 00 00 00 mov eax,0x0
40113b: 5d pop rbp
40113c: c3 ret
為什么將附加暫存器(ecx, edx, esi, edi)用作值 1、2、3、4 的中間存盤,而不是rbp直接將它們放入?
uj5u.com熱心網友回復:
《作為中介存盤》:你的困惑似乎是這部分。
ABI 指定這些函式引數在您看到的暫存器中傳遞(請參閱問題下的注釋)。暫存器不僅用作中介。該值根本不應該放在堆疊上。它們一直保留在暫存器中,除非函式需要將暫存器重用于其他用途或傳遞指向函式引數的指標或類似的東西。
您所看到的test_function只是未啟用優化的編譯產物。將暫存器放入堆疊的mov指令是沒有意義的,因為之后沒有對它們進行任何處理。堆疊指標立即恢復,然后函式回傳。
整個函式應該只是一條ret指令。請參閱https://godbolt.org/z/qG9GjMohY在哪里-O2使用。
如果沒有啟用優化,編譯器不會嘗試洗掉指令,即使它們毫無意義,它總是將變數的值存盤到記憶體中并再次從記憶體中加載它們,即使它們本來可以保存在暫存器中。這就是為什么看-O0匯編幾乎總是毫無意義的原因。
uj5u.com熱心網友回復:
暫存器用于呼叫函式的引數。標準呼叫轉換要求將引數放置在某個暫存器中,因此您在 main 中看到的代碼將引數放入這些暫存器中,而 test_function 中的代碼期望它們在這些暫存器中并從那里讀取它們。
因此,您的后續問題可能是“為什么 test_function 將這些引數復制到堆疊中?”。那是因為您在沒有優化的情況下進行編譯,因此編譯器會生成低效的代碼,在堆疊幀中為每個引數和本地 var 分配空間,并將引數從輸入暫存器復制到堆疊幀作為函式 prolog 的一部分。如果您要在函式中使用這些值,您會看到它從堆疊幀位置讀取它們,即使它們可能仍在暫存器中。如果您使用 -O 進行編譯,您會看到編譯器擺脫了所有這些,因為不需要堆疊幀。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/492917.html
下一篇:如何在32位保護模式下列印新行
