首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么ARM gcc在函数开始时将寄存器r3和lr推到堆栈中?

为什么ARM gcc在函数开始时将寄存器r3和lr推到堆栈中?
EN

Stack Overflow用户
提问于 2015-09-17 04:46:48
回答 1查看 3.2K关注 0票数 7

我试图编写如下(main.c)这样的简单测试代码:

代码语言:javascript
复制
main.c
void test(){
}
void main(){
    test();
}

然后,我使用arm-non gcc编译并使用objdump获得组装代码:

代码语言:javascript
复制
arm-none-eabi-gcc -g -fno-defer-pop -fomit-frame-pointer -c main.c
arm-none-eabi-objdump -S main.o > output

程序集代码将推动r3和lr寄存器,甚至该函数什么也不做。

代码语言:javascript
复制
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作为您的回答,我有另一个例子:源代码是:

代码语言:javascript
复制
void test1(){
}
void test(int i){
        test1();
}
void main(){
        test(1);
}

我在上面使用相同的编译命令,然后获得以下程序集:

代码语言:javascript
复制
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()中,编译器不只是使用一条指令?

代码语言:javascript
复制
push {r0, lr}

它使用3条指令而不是1条。

代码语言:javascript
复制
push {lr}
sub sp, sp #12
str r0, [sp, #4]

顺便问一下,为什么它用12作为子sp,堆栈是对齐的8字节,它只可以用4子对吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-17 05:08:28

根据标准ARM嵌入式ABIr0通过r3将参数传递给一个函数,其返回值,而lr (a.k.a:r14)是链接寄存器,其目的是保存函数的返回地址。

显然,必须保存lr,因为否则main()将无法返回到其调用方。

现在臭名昭著的是,每一个ARM指令都需要32位,正如您所提到的,ARM的调用堆栈对齐要求为8字节。而且,作为奖励,我们正在使用嵌入式ARM ABI,因此代码大小将被优化。因此,通过推送未使用的寄存器来保存lr和调整堆栈(不需要r3,因为test()不接受参数,也不返回任何内容),然后弹出单个32位指令,而不是添加更多的指令(从而浪费宝贵的内存!)操作堆栈指针。

毕竟,得出这样的结论是非常合乎逻辑的,这只是GCC的一个优化。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32622762

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档