我正在使用一些C代码来检查我的编译器(在本例中为main-git Clang)如何处理ARM ABI中堆栈的函数参数wrt。我发现这个函数:
int test(int a, int b, int c, int d, int e, int f, int g) {
return a + b + c + d + e + f + g;
}
得到翻译,与-O0
,在
08000298 <test>:
8000298: b084 sub sp, #16
800029a: f8dd c018 ldr.w ip, [sp, #24]
800029e: f8dd c014 ldr.w ip, [sp, #20]
80002a2: f8dd c010 ldr.w ip, [sp, #16]
80002a6: 9003 str r0, [sp, #12]
80002a8: 9102 str r1, [sp, #8]
80002aa: 9201 str r2, [sp, #4]
80002ac: 9300 str r3, [sp, #0]
80002ae: 9803 ldr r0, [sp, #12]
80002b0: 9902 ldr r1, [sp, #8]
80002b2: 4408 add r0, r1
80002b4: 9901 ldr r1, [sp, #4]
80002b6: 4408 add r0, r1
80002b8: 9900 ldr r1, [sp, #0]
80002ba: 4408 add r0, r1
80002bc: 9904 ldr r1, [sp, #16]
80002be: 4408 add r0, r1
80002c0: 9905 ldr r1, [sp, #20]
80002c2: 4408 add r0, r1
80002c4: 9906 ldr r1, [sp, #24]
80002c6: 4408 add r0, r1
80002c8: b004 add sp, #16
80002ca: 4770 bx lr
请注意ip
中的值的初始存储。**为什么有?**它们对我来说毫无用处,即使是-O0
。(-O0
解释了why it spills a
to d
from r0-r3 to the stack函数入口,并在需要时重新加载它们。但是这些堆栈参数没有做任何事情。)
我不明白在这种情况下ip
的用法是什么,因为它在后面没有被使用,也没有被用作帧指针(iiuc是它的正常用法吗?)。谢谢!
1条答案
按热度按时间bybem2ql1#
序言中堆栈参数的虚拟加载似乎只是clang的
-O0
代码生成器跨多个体系结构的一个怪癖。它已经存在很长一段时间了(Godbolt最早的叮当版本,3。0,表现出这种行为)。这完全没用,没有理由这是有帮助的,gcc -O0
不这样做。没有ABI或asm需要理解,这只是
-O0
代码生成中许多低效之一,这并不有趣。(GCC
-O0
* 会 * 为叶函数设置帧指针,不像clang)。针对x86-64、i386、RISC-V、MIPS和AArch 64的Clang在所有这些设备上都做了同样的事情。(也是PowerPC,我想,如果我阅读得正确的话。)通常clang会为每个堆栈参数选择不同的寄存器,但Godbolt上的clang 16会加载4次到EAX中,覆盖它。我添加了更多的args,总共10个,因为x86-64 SysV在regs中传递最多6个,而一些像RISC-V和AARch 64,在寄存器中传递最多8个整数args。
另一个例子:https://godbolt.org/z/x4vzcYdEn-开始附近的
mov eax, [rbp + 24]
和mov r10d, [rbp + 16]
是使用不同寄存器的x86-64 GCC示例。早期的ARMv7 clang并不是对每个负载都使用
ip
。它将它们传播到其他寄存器,如R12,LR,R4和R5(在推送这些寄存器之后,它可以覆盖它们。)有了更多的函数args,那些clang 11甚至push
都有更多的寄存器,这样它们就可以加载函数序言中的所有堆栈args,即与打开的{
相关联的asm块(在调试元数据中)。所以clang(trunk)实际上变得不那么糟糕了!32位x86的旧clang也使用额外的寄存器,并将一些堆栈参数复制到它为局部变量保留的空间中。还是把它们都复制下来,有些放在序言里,有些放在寄存器里,直到结尾?clang 8.0.1 on Godbolt非常古怪,对于x86和
-m32
。我不知道编译器内部的什么细节导致了这种行为。或者甚至它是否存在于LLVM-IR中,或者仅在asm的最终生成期间。
我很好奇,如果运行一个优化通道来摆脱这些虚拟加载,而不是为它们发出机器代码(并在优化时将它们放在数据结构中),是否会加快整体编译时间。)
我想知道“触摸”所有的args是否与clang如何安排在序言中溢出寄存器args有关,这是为了实现100%一致的调试而需要的。