为什么ARM gcc在函数开始时将寄存器r3和lr压入堆栈? [复制]

2023-11-27

我尝试编写一个像这样的简单测试代码(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 来 sub sp,堆栈是 8 字节对齐的,它可以用 4 对它进行 sub 吗?


根据标准 ARM 嵌入式 ABI, r0通过r3用于将参数传递给函数及其返回值,同时lr(又名:r14) 是链接寄存器,其用途是保存函数的返回地址。

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么ARM gcc在函数开始时将寄存器r3和lr压入堆栈? [复制] 的相关文章

随机推荐

  • 如何自动停止VBA宏?

    I know you can manually stop a running VBA macro with Ctrl Break but is there any way to have the code stop automaticall
  • 安装 HAXM 时不支持 VT

    我正在尝试安装使用 Intel x86 模拟器加速器的快速 Android 模拟器 我已经通过 SDK 管理器下载了加速器 但是当我尝试安装它时 我在安装开始时收到以下错误消息 我知道我的 CPU i7 3520M 支持 VT X 虚拟化
  • macOS 上 SwiftUI 中列表内的 TextField:编辑效果不佳

    这个问题是关于 SwiftUI 的macOS应用程序 不是 iOS 或 Catalyst 使用 Xcode 12 4 SwiftUI 2 问题是 编辑列表内的文本字段效果不佳 事实上 它的效果非常差 以至于一开始我以为我根本无法编辑它 这是
  • sqlite3“操作错误:接近”(“:语法错误”python

    简而言之 我正在尝试创建一个 sql 数据库表并向其中输入数据 我让它以更简单的方式工作 但是当我将它放入我的脚本中时 它会导致此错误 我希望这是我错过的一些简单的事情 任何帮助 建议将不胜感激 conn sqlite3 connect D
  • Angularjs - 装饰控制器

    我正在尝试为我的控制器设置一个装饰器 我的目的是在我的应用程序中的所有控制器中引入一些常见的行为 我已将其配置为在 Angular 1 2 x 中工作 但从 1 3 x 开始有一些重大更改破坏了代码 现在得到的错误是 控制器不是一个函数 下
  • 类型参数“T”不受 impl 特征、自身类型或谓词的约束

    当特征具有相关类型时 我很难理解特征的使用 这是一个非常简单的例子 pub trait Message pub trait SendsMessages type Message Message fn send msg Self Messag
  • 如何在 Moose 中使用单个构建器构建多个属性?

    使用 Moose 是否可以创建一个同时构建多个属性的构建器 我有一个项目 其中对象有多个字段 集 如果请求该集中的任何成员 我想继续填充它们 我的假设是 如果我需要姓名 我还需要生日 并且由于它们位于同一个表中 因此在一个查询中获取两者会更
  • 基类指针可以指向派生类对象。为什么反之则不然呢?

    基类指针可以指向派生类对象 为什么不进行强制转换则反之亦然 从逻辑上讲 基类不会有足够的派生类信息 但派生类也应该有基类的信息 我在这里缺少一些基础知识 如果我告诉你我有一只狗 你就可以放心地假设我有一只宠物 如果我告诉你我有一只宠物 你不
  • Sql批量插入--文件不存在

    我有以下查询要插入表中 BULK INSERT tblMain FROM c Type txt WITH FIELDTERMINATOR ROWTERMINATOR n GO 它收到消息 消息 4860 16 级 状态 1 第 1 行无法批
  • 在运行时动态选择要使用的 .dll 版本

    我正在开发一个 SharePoint 实用程序 该应用程序适用于 SharePoint 2007 和 2010 当我引用 12 0 0 0 版本的 SharePoint dll 时 该应用程序适用于 SharePoint 2007 但不适用
  • TFS 2012 + Visual studio 2012:某些设置选项返回“用户名或密码不正确”

    我已将 TFS 2010 升级到 TFS 2012 没有出现任何问题 我可以连接到源代码管理 签入以及工作所需的一切 但是 如果我转到我所连接的团队项目的设置页面 则会出现以下选项work正如预期 团队项目 源代码控制 团队项目 门 户设置
  • 如何从列表中删除元素?

    我有一个列表 我想从中删除一个元素 我怎样才能做到这一点 我尝试在参考手册中查找我认为该函数的明显名称 但没有找到合适的名称 Answer recommended by R Language Collective 如果您不想就地修改列表 例
  • 使用 Chromedriver 制作程序,出现错误:“无法使用此命令获取 Chrome 版本”

    这是我的代码 我这样做是因为当我尝试输入 chromedriver exe 的路径时 我要么收到 WebDriverException 消息 chromedriver exe 可执行文件可能有错误的权限 或 WebDriverExcepti
  • MVVM Light Toolkit - Messenger 使用事件聚合器还是中介器模式?

    有人可以帮我看看是否I Messenger 类 和实现 from MVVM轻工具包演示了使用事件聚合器模式 or 中介者模式 如果有人建议它部分遵循这两种模式 那么我会请求详细信息 说明实现的哪一部分类似于哪种模式以保持答案有效 Ref O
  • RoR - 选择禁用 include_blank 的标签

    我想要这样的结果
  • 从 fstream 读取单个字符?

    我正在尝试从 stdio 迁移到 iostream 事实证明这非常困难 我已经掌握了加载文件和关闭文件的基础知识 但我真的不知道流是什么 或者它们如何工作 与此相比 在工作室中一切都相对简单和直接 我需要做的是 从文本文件中读取单个字符 根
  • 如何创建一个可以切换tab内容的表格?

    I want to make a table looks like this 内部表格不是问题 但不知道如何创建外部框架 其中包括 商品描述 运输 和 退货 选项卡 一个最小的例子将不胜感激 谢谢 你当然可以使用 jQuery 来解决这个问
  • 在 sqlalchemy 中对相同的声明性基础使用不同的架构

    我对 Pyramid 和 SQLAlchemy 都很陌生 我正在使用 SQLAlchemy 开发 Python Pyramid 项目 我在下面设置了一个简单的模型 我将如何在运行时将其与不同的模式一起使用 这将是一个 PostgreSQL
  • 溢出:自动在触摸设备中不起作用(iOS)

    我已经使用 twitter bootstrap 实现了一个网站 在网站的顶部 我使用了一个导航栏 在其中使用了一个下拉菜单 下拉菜单由 和 标签组成 我正在显示该 dropdwon 菜单中的成员列表 当成员列表增长时 下拉菜单水平增长 为此
  • 为什么ARM gcc在函数开始时将寄存器r3和lr压入堆栈? [复制]

    这个问题在这里已经有答案了 我尝试编写一个像这样的简单测试代码 main c main c void test void main test 然后我使用arm non eabi gcc进行编译并使用objdump来获取汇编代码 arm no