SysV i386 ABI 的當前 (Linux) 版本在呼叫之前需要 16 位元組堆疊對齊:
輸入引數區域的末尾應在 16(32,如果 __m256 在堆疊上傳遞)位元組邊界對齊。換句話說,當控制轉移到函式入口點時,值 (%esp 4) 總是 16 (32) 的倍數。
在 GCC 8.1 上,此代碼在呼叫callee:( Godbolt )之前將堆疊對齊到 16 位元組邊界
| 來源 | # 位元組 |
|---|---|
| 稱呼 | 4 |
| 推ebp | 4 |
| 子 esp, 24 | 24 |
| 子 esp, 4 | 4 |
| 推eax | 4 |
| 推eax | 4 |
| 推eax | 4 |
| 全部的 | 48 |
在 GCC 8.2 及更高版本的所有版本上,它與 4 位元組邊界對齊:(Godbolt)
| 來源 | # 位元組 |
|---|---|
| 稱呼 | 4 |
| 推ebp | 4 |
| 子 esp, 16 | 16 |
| 推eax | 4 |
| 推eax | 4 |
| 推eax | 4 |
| 全部的 | 36 |
易核實,如果我們縮短或提高的所要求的引數個數callee。
-mprefered-stack-boundary奇怪地更改將運算元更改為子指令,但對實際堆疊對齊沒有任何影響:(Godbolt)
那么,呃,什么給了?
uj5u.com熱心網友回復:
由于您在同一個翻譯單元中提供了函式的定義,顯然 GCC 認為該函式不關心堆疊對齊,也不太關心它。顯然,即使在-O0.
原來當我在手冊中搜索“ipa”選項時,這個選項甚至有一個明顯的名稱:-fipa-stack-alignment默認情況下即使在-O0. 手動將其關閉,-fno-ipa-stack-alignment結果符合您的預期,一秒sub的值取決于推送次數 ( Godbolt ),確保 ESP 在像現代 Linux 版本的 i386 SysV ABI 使用的呼叫之前對齊 16 。
或者,如果您將定義更改為僅宣告,則生成的 asm 符合預期,完全尊重-mpreferred-stack-boundary.
void callee(void* a, void* b) {
}
到
void callee(void* a, void* b);
使用-fPIC還強制GCC無法對Callee采取任何方法,因此它確實尊重功能插入的可能性(例如,通過LD_PRELOAD)的可能性。
不編譯為一個共享庫,GCC允許假設它看到了一個全域函式任何定義是的定義,由于ISO C的一個定義規則。
如果您使用__attribute__((noipa))on 函式定義,則呼叫站點將不會基于該定義假設任何內容。就像您重命名了定義(這樣您仍然可以查看它)并僅提供呼叫者使用的名稱的宣告一樣。
如果您只是想停止行內,您可以__attribute__((noinline,noclone))改為使用,如果優化器只是選擇不行內,但仍然可以看到此定義,則仍然允許呼叫站點。這可能是也可能不是您想要的。
另請參閱如何從 GCC/clang 程式集輸出中去除“噪音”?回復:撰寫其 asm 有趣的函式和編譯器選項。
順便說一句,我發現將宣告/定義更改為可變引數最容易,因此我可以添加或洗掉 args,只需對呼叫者進行更改。我仍然能夠重現你的結果,即使數量隨著額外的引數發生變化,當有一個定義,但不僅僅是一個宣告時,也不改變sub數量push。
void callee(void* a, ...) // {} // comment out a body or not
;
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/346036.html
上一篇:在bash腳本中一次運行多個回圈
