【发布时间】:2015-07-21 11:12:03
【问题描述】:
我用 gcc 和 clang 编译的一个小函数:
void test() {
printf("hm");
printf("hum");
}
$ gcc test.c -fomit-frame-pointer -masm=intel -O3 -S
sub rsp, 8
.cfi_def_cfa_offset 16
mov esi, OFFSET FLAT:.LC0
mov edi, 1
xor eax, eax
call __printf_chk
mov esi, OFFSET FLAT:.LC1
mov edi, 1
xor eax, eax
add rsp, 8
.cfi_def_cfa_offset 8
jmp __printf_chk
和
$ clang test.c -mllvm --x86-asm-syntax=intel -fomit-frame-pointer -O3 -S
# BB#0:
push rax
.Ltmp1:
.cfi_def_cfa_offset 16
mov edi, .L.str
xor eax, eax
call printf
mov edi, .L.str1
xor eax, eax
pop rdx
jmp printf # TAILCALL
我感兴趣的区别是gcc使用sub rsp, 8/add rsp, 8作为函数prolog,而clang使用push rax/pop rdx。
为什么编译器使用不同的函数序言?哪个变种更好? push 和 pop 当然会编码成更短的指令,但它们比 add 和 sub 快还是慢?
首先摆弄堆栈的原因似乎是 abi 要求 rsp 对非叶程序进行 16 字节对齐。我找不到任何可以删除它们的编译器标志。
从您的回答来看,push & pop 似乎更好。 push rax + pop rdx = 1 + 1 = 2 与 sub rsp, 8 + add rsp, 8 = 4 + 4 = 8。所以前一对可以免费节省 6 个字节。
【问题讨论】:
-
这是一个选择问题。很难说哪个变种更好。可能这两种变体在性能方面都非常相似。
-
回复:您的编辑。是的,ABI 保证在函数入口处,
(%rsp + 8)是 16B 对齐的。 (我将大部分评论编辑到我的答案中)。
标签: c gcc assembly llvm x86-64