79、【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:r7 寄存器
【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
接之前 blog
【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:栈指针和帧指针(上)
【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:栈指针和帧指针(下)
分析了栈指针和帧指针的一些概念,并演示了栈帧的操作,在 blog 最后留了个疑问,AAPCS32 文档里写了 r11 作为帧指针寄存器,怎么代码里却用的是 r7?下面来补充解释下这里面栈帧的一些细节
帧指针的选择
之前分析到 AAPCS 这里,可以看到 r11 为帧指针寄存器
而不是在汇编文件里面看到的 r7 作为帧指针寄存器
其实有两个细节:
- 文档里面有描述,r11 可以被用作一个帧指针,首先是英文里 may 的表达,这里用了 may 而不是 must,may 表示可能性或可以,语气委婉,而 must 强调必要性或确定性,语气更加强烈,从单词的使用用法来说,通常情况下,r11 是推荐使用作为帧指针的,而不是强制要求使用
- 其次,我们翻阅的这篇文档是 AAPCS32,意味着它是基于 arm32 位指令集的,而stm32f429i 系列是基于 Arm Cortex-m4 核,是基于 thumb 指令集的
arm32 和 thumb 是 arm 架构中两种不同的指令集状态,在帧指针这块的使用上,有所区别,那么有没有 AAPCS_Thumb 的专门文档呢?没有,AAPCS 在架构上只区分 32 位和 64 位,thumb(这里一般指 thumb2,16 位和 32 位指令混合使用)可以用 AAPCS32 规范来 cover 住
gcc 源码注释
关于帧指针的选择,在 gcc 源码中可以找到蛛丝马迹,gcc 源码链接如下
https://github.com/gcc-mirror/gcc
在 arm.cc 文件中,有这么一段注释
-
这里划红线的说明,在 thumb 模式下,如果需要帧指针,则使用 r7 寄存器
-
而 r11 是 32 位 arm 模式下的传统帧指针寄存器
-
这里有两个概念,hard frame pointer 和 soft frame pointer
-
hard frame pointer 是一种传统意义上的帧指针,是一个特定的寄存器,比如 arm32 中的 r11,在调用约定里面明确规定了
-
soft frame pointer 是一种更加灵活的帧指针实现方式,在需要时启用,不像 hard frame pointer 那样固定为某个寄存器,而是根据编译器的优化策略决定使用哪个寄存器,比如 gcc 上选择的是 r7
为什么是 r7?
了解到 thumb 模式和 arm32 模式之后,再回到这个问题,为什么 thumb 选择了 r7,为什么 thumb 不能和 arm32 一样,选择 r11?
回答这个问题,再看下 Arm Cortex-M4 Processor Technical Reference Manual 这张图
- 首先,R0-R7 寄存器可以接受所有指令,包括 16 位和 32 位的指令集,而 R8-R12 能接受 32 位指令,但只能接受部分 16 位指令集
- 而 thumb 指令集是一个 16位 宽的指令集,相比 32 位宽的 ARM 指令集,在操作范围和灵活性上有所限制,对高寄存器(R8-R15)的操作受到更多限制,不能像处理低寄存器(R0-R7)那样直接访问
- 所以 R8-R15 被排除,选择一个低寄存器(R0-R7)作为帧指针更符合 thumb 指令集的设计初衷
- 而在 R0-R7 中,R0-R3 是 caller-saved,被排除
- 而在 R4-R7 中,R4-R6 在实际编程实践中,更经常被用来存储局部变量或临时值,相比之下,R7 的使用频率较低,便顺理成章的成为了 soft frame pointer 的首选
-marm
最后,在 bash 终端输入如下命令
arm-none-eabi-gcc -S -marm main.c -o main.s
可以看到在 32 位指令集下,帧指针为默认的 fp(r11)寄存器
今天先到这儿,下篇 blog 继续