为什么llvm和gcc在x86 64上使用不同的函数序言?

为什么llvm和gcc在x86 64上使用不同的函数序言?,c,gcc,assembly,llvm,x86-64,C,Gcc,Assembly,Llvm,X86 64,我正在用gcc和clang编译一个简单的函数: void测试(){ printf(“hm”); printf(“hum”); } 及 我感兴趣的区别是gcc使用子rsp,8/添加rsp,8用于函数prolog,而clang使用push rax/pop rdx 为什么编译器使用不同的函数序言?哪种变体更好push和pop当然编码为较短的指令,但它们比add和sub快还是慢 堆栈篡改的首要原因似乎是abi要求rsp对非叶过程进行16字节对齐。我找不到任何删除它们的编译器标志 从你的答案来看,推送

我正在用gcc和clang编译一个简单的函数:

void测试(){
printf(“hm”);
printf(“hum”);
}

我感兴趣的区别是gcc使用
子rsp,8
/
添加rsp,8
用于函数prolog,而clang使用
push rax
/
pop rdx

为什么编译器使用不同的函数序言?哪种变体更好
push
pop
当然编码为较短的指令,但它们比
add
sub
快还是慢

堆栈篡改的首要原因似乎是abi要求rsp对非叶过程进行16字节对齐。我找不到任何删除它们的编译器标志


从你的答案来看,推送和流行音乐似乎更好<代码>推送rax+pop rdx=1+1=2与
子rsp、8+添加rsp、8=4+4=8
相比。因此,前一对可以节省6个字节,而无需任何费用。

根据我在我的机器上做的实验,
push/pop
add/sub
的速度相同。我想所有现代计算机都应该如此


无论如何,差异(如果有的话)确实是微观的,因此我建议您安全地假设它们是等效的…

在Intel上,
sub
/
add
将触发堆栈引擎插入额外的uop,以同步管道无序执行部分的
%rsp
。(请参见第91页,特别是关于堆栈引擎的内容。另外,它在Haswell和Pentium M上的工作原理仍然相同,只要它需要插入额外的UOP即可

push
/
pop
将占用更少的融合域UOP,因此即使它们使用存储/加载端口,也可能更高效。它们位于调用/重试对之间

因此,
push
/
pop
至少不会变慢,但需要更少的指令字节。更好的I-cache密度很好

顺便说一句,我认为这对INSN的要点是在
调用
推送8B返回地址后保持堆栈16B对齐。这是ABI最终需要半无用指令的一种情况。更复杂的函数需要一些堆栈空间来溢出局部,然后在函数调用后重新加载它们,通常将
小于$s要保留空间,请输入%rsp


SystemV(Linux)amd64 ABI保证在函数入口,
(%rsp+8)
,堆栈上的参数(如果有)将与16B对齐。()。您必须安排您调用的任何函数都是这样,否则,如果他们使用SSE对齐的加载将fault从中分离出来,那就是您的错。或者,如果他们假设如何使用
来屏蔽地址或其他东西,则会崩溃。

这是一个选择问题。很难说哪种变体更好。可能两种变体都是nts在性能方面非常相似。re:您的编辑。是的,ABI保证在函数输入时,
(%rsp+8)
是16B对齐的。(我在回答中编辑了大部分注释).什么样的实验?你是在测试uop吞吐量上的瓶颈吗?我同意大多数时候可能没有什么区别。我做了最幼稚的事情:将指令复制数千次(实际上使用宏),将整个过程放入一个循环中,然后运行。我不确定这在uop上是否是瓶颈。您能确认吗?
add
每次都使用相同的寄存器,需要前一个寄存器的输出作为输入,使延迟成为限制。
add
在SnB/IvB上的吞吐量为3个周期,在Haswell上的吞吐量为4个周期,如果它们是独立的。
push
可以维持1/周期,
pop
2/周期。与现代CPU一样,重要的是上下文(其他哪些INSN正在竞争执行资源,以及它如何融入依赖链)。是的,这只是为了保持堆栈对齐。还要注意的是,大多数时间函数都会为局部变量分配一些空间,
sub
变量在这种情况下效率更高。可能编译器编写者只是没有针对不需要局部变量的情况进行优化。是的,很少使用局部变量的非叶函数我认为clang使用它不关心的
push
/
pop
数据是一种巧妙的优化。
$ 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