我试图编写如下(main.c)这样的简单测试代码:
main.c
void test(){
}
void main(){
test();
}然后,我使用arm-non gcc编译并使用objdump获得组装代码:
arm-none-eabi-gcc -g -fno-defer-pop -fomit-frame-pointer -c main.c
arm-none-eabi-objdump -S main.o > output程序集代码将推动r3和lr寄存器,甚至该函数什么也不做。
main.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <test>:
void test(){
}
0: e12fff1e bx lr
00000004 <main>:
void main(){
4: e92d4008 push {r3, lr}
test();
8: ebfffffe bl 0 <test>
}
c: e8bd4008 pop {r3, lr}
10: e12fff1e bx lr我的问题是为什么arm gcc选择将r3推到堆栈中,甚至test()函数也从不使用它?gcc只是随机选择一个寄存器推吗?如果它符合堆栈对齐(ARM为8字节)的要求,为什么不直接减去sp呢?谢谢。
==================Update==========================
@KemyLand作为您的回答,我有另一个例子:源代码是:
void test1(){
}
void test(int i){
test1();
}
void main(){
test(1);
}我在上面使用相同的编译命令,然后获得以下程序集:
main.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <test1>:
void test1(){
}
0: e12fff1e bx lr
00000004 <test>:
void test(int i){
4: e52de004 push {lr} ; (str lr, [sp, #-4]!)
8: e24dd00c sub sp, sp, #12
c: e58d0004 str r0, [sp, #4]
test1();
10: ebfffffe bl 0 <test1>
}
14: e28dd00c add sp, sp, #12
18: e49de004 pop {lr} ; (ldr lr, [sp], #4)
1c: e12fff1e bx lr
00000020 <main>:
void main(){
20: e92d4008 push {r3, lr}
test(1);
24: e3a00001 mov r0, #1
28: ebfffffe bl 4 <test>
}
2c: e8bd4008 pop {r3, lr}
30: e12fff1e bx lr如果push {r3,lr}在第一个示例中是为了使用较少的指令,为什么在这个函数test()中,编译器不只是使用一条指令?
push {r0, lr}它使用3条指令而不是1条。
push {lr}
sub sp, sp #12
str r0, [sp, #4]顺便问一下,为什么它用12作为子sp,堆栈是对齐的8字节,它只可以用4子对吗?
发布于 2015-09-17 05:08:28
根据标准ARM嵌入式ABI,r0通过r3将参数传递给一个函数,其返回值,而lr (a.k.a:r14)是链接寄存器,其目的是保存函数的返回地址。
显然,必须保存lr,因为否则main()将无法返回到其调用方。
现在臭名昭著的是,每一个ARM指令都需要32位,正如您所提到的,ARM的调用堆栈对齐要求为8字节。而且,作为奖励,我们正在使用嵌入式ARM ABI,因此代码大小将被优化。因此,通过推送未使用的寄存器来保存lr和调整堆栈(不需要r3,因为test()不接受参数,也不返回任何内容),然后弹出单个32位指令,而不是添加更多的指令(从而浪费宝贵的内存!)操作堆栈指针。
毕竟,得出这样的结论是非常合乎逻辑的,这只是GCC的一个优化。
https://stackoverflow.com/questions/32622762
复制相似问题