考慮以下:
ammarfaizi2@integral:/tmp$ vi test.c
ammarfaizi2@integral:/tmp$ cat test.c
extern void use_buffer(void *buf);
void a_func(void)
{
char buffer[4096];
use_buffer(buffer);
}
__asm__("emit_mov_rbp_to_rsp:\n\tmovq %rbp, %rsp");
ammarfaizi2@integral:/tmp$ clang -Wall -Wextra -c -O3 -fno-omit-frame-pointer test.c -o test.o
ammarfaizi2@integral:/tmp$ objdump -d test.o
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <emit_mov_rbp_to_rsp>:
0: 48 89 ec mov %rbp,%rsp
3: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
a: 00 00 00
d: 0f 1f 00 nopl (%rax)
0000000000000010 <a_func>:
10: 55 push %rbp
11: 48 89 e5 mov %rsp,%rbp
14: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
1b: 48 8d bd 00 f0 ff ff lea -0x1000(%rbp),%rdi
22: e8 00 00 00 00 call 27 <a_func 0x17>
27: 48 81 c4 00 10 00 00 add $0x1000,%rsp
2e: 5d pop %rbp
2f: c3 ret
ammarfaizi2@integral:/tmp$
在 結束a_func()之前,在回傳之前,是恢復 的功能結語%rsp。它使用add $0x1000, %rspwhich 產生48 81 c4 00 10 00 00.
不能只使用mov %rbp, %rsp它只產生 3 個位元組48 89 ec嗎?
為什么 clang 不使用更短的方式 ( mov %rbp, %rsp)?
通過代碼大小權衡,使用add $0x1000, %rsp而不是的優勢是mov %rbp, %rsp什么?
更新(額外)
即使使用-Os,它仍然會產生相同的代碼。所以我認為必須有一個合理的理由來避免mov %rbp, %rsp。
ammarfaizi2@integral:/tmp$ clang -Wall -Wextra -c -Os -fno-omit-frame-pointer test.c -o test.o
ammarfaizi2@integral:/tmp$ objdump -d test.o
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <emit_mov_rbp_to_rsp>:
0: 48 89 ec mov %rbp,%rsp
0000000000000003 <a_func>:
3: 55 push %rbp
4: 48 89 e5 mov %rsp,%rbp
7: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
e: 48 8d bd 00 f0 ff ff lea -0x1000(%rbp),%rdi
15: e8 00 00 00 00 call 1a <a_func 0x17>
1a: 48 81 c4 00 10 00 00 add $0x1000,%rsp
21: 5d pop %rbp
22: c3 ret
ammarfaizi2@integral:/tmp$
uj5u.com熱心網友回復:
如果它完全使用 RBP 作為幀指標,是的,它mov %rbp, %rsp會更緊湊,AFAIK 至少在所有 x86 微體系結構上都一樣快。(移動消除可能甚至適用于它)。當 add 常量不適合 imm8 時更是如此。
這可能是一個遺漏的優化,非常類似于https://bugs.llvm.org/show_bug.cgi?id=10319(它建議使用leave而不是 mov/pop,這會在 Intel 上額外花費 1 uop 但又節省了 3 個位元組)。它指出在正常情況下整體靜態代碼大小的節省非常小,但沒有考慮效率優勢。在正常構建(-O2沒有-fno-omit-frame-pointer)中,只有少數函式會使用幀指標(僅在使用 VLA / alloca 或過度對齊堆疊時),因此可能的好處更小。
從那個 bug 看來,它只是一個 LLVM 沒有費心尋找的窺視孔,因為許多函式還需要恢復其他暫存器,因此您實際上需要add一些其他值來將 RSP 指向其他推送之下。
(GCC 有時用于mov恢復呼叫保留的 regs,因此它可以使用leave. 使用幀指標,這使得尋址模式編碼相當緊湊,盡管當然 4 位元組 qwordmov -8(%rbp), %r12仍然不如 2 位元組 pop 小。而且如果我們沒有幀指標(例如在-O2代碼中),mov %rbp, %rsp則永遠不是一種選擇。)
在考慮“不值得尋找”的原因之前,我想到了另一個小好處:
呼叫一個保存/恢復RBP的函式后,RBP就是一個加載結果。因此mov %rbp, %rsp,以后使用 RSP 將需要等待該負載。可能一些極端情況最終在存盤轉發延遲上遇到瓶頸,而暫存器修改僅為 1 個周期。
但總的來說,這似乎不太值得額外的代碼大小;我希望這種極端情況很少見。盡管 a 需要新的 RSP 值pop %rbp,因此呼叫者恢復的 RBP 值是我們回傳后兩個加載鏈的結果。(幸運的是ret有分支預測來隱藏延遲。)
因此,在某些基準測驗中兩種方式都值得嘗試;例如,在某些標準基準測驗(如 SPECint)上將其與調整后的 LLVM 版本進行比較。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/315401.html
下一篇:根據路段查找道路起點和終點的順序
