c - 为什么 ARM gcc 在函数开始时将寄存器 r3 和 lr 压入堆栈?

标签 c gcc assembly arm

<分区>

我试着写了一个像这样的简单测试代码(main.c):

main.c
void test(){
}
void main(){
    test();
}

然后我用arm-non-eabi-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 是否只是随机选择 1 个寄存器来推送? 如果是为了堆栈对齐(ARM 为 8 字节)要求,为什么不直接减去 sp?谢谢。

==================更新==========================

@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 替换它吗?

最佳答案

根据Standard ARM Embedded ABI , r0r3 用于将参数传递给函数及其返回值,同时 lr (又名:r14 ) 是链接寄存器,其目的是保存函数的返回地址。

很明显 lr 必须被保存,否则 main() 将无法返回到它的调用者。

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

毕竟,得出这只是 GCC 优化的结论是合乎逻辑的。

关于c - 为什么 ARM gcc 在函数开始时将寄存器 r3 和 lr 压入堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32622762/

相关文章:

c - C中的插入二叉搜索树

c - 为什么不同的标题具有相同的名称?

iphone - gcc-4.2 失败,退出代码为 1 错误

assembly - CPU 从用户模式切换到内核模式 : What exactly does it do? 它是如何进行这种转换的?

c - 在程序集中搜索 2 个不同值的最快方法

c - 整数从字符串[]接收到错误的值

c - 为什么会导致执行错误呢?

c++ - 如何处理浮点计算中的超精度?

c++ 应用程序在非 AVX CPU 上崩溃

linux - 使用NASM以尽可能少的代码打印换行符