Bjö*_*ist 6 c assembly gcc x86-64 llvm
我用gcc和clang编译的一个简单的函数:
void test() {
printf("hm");
printf("hum");
}
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)
和
$ 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
Run Code Online (Sandbox Code Playgroud)
我感兴趣的区别是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个字节.
在Intel上,sub
/ add
将触发堆栈引擎插入额外的uop以同步%rsp
管道的无序执行部分.(参见Agner Fog的microarch doc,特别是第91页,关于堆栈引擎.AFAIK,它在Haswell和Pentium M上的工作方式相同,只要它需要插入额外的uops.
将push
/ pop
会少走融合域微指令,因此可能会效率更高,即使他们使用的存储/装载港口.它们介于呼叫/转换对之间.
因此,push
/ pop
至少不会变慢,但占用较少的指令字节.更好的I-cache密度是好的.
顺便说一句,我认为这对insn的意思是在call
推送8B返回地址后保持堆栈16B对齐.这是ABI最终需要半无用指令的一种情况.更复杂的函数需要一些堆栈空间来溢出本地,然后在函数调用后重新加载它们,通常sub $something, %rsp
会保留空间.
SystemV(Linux)amd64 ABI保证在函数入口处, (%rsp + 8)
堆栈中的args(如果有的话)将是16B对齐的.(http://x86-64.org/documentation/abi.pdf).你必须安排你所使用的任何功能的情况,或者如果他们使用SSE对齐的负载进行分段,则是你的错.或者因为假设他们如何使用AND
掩盖地址或其他东西而崩溃.
归档时间: |
|
查看次数: |
394 次 |
最近记录: |