共计 1188 个字符,预计需要花费 3 分钟才能阅读完成。
ARM 函数调用基础
ARM 架构采用 AAPCS(ARM Architecture Procedure Call Standard)规范定义函数调用约定。关键要点包括:

- 寄存器分工:
- r0-r3 用于参数传递和返回值
- r4-r11 为被调用者保存寄存器
- r13(SP) 栈指针,r14(LR) 链接寄存器
-
r15(PC) 程序计数器
-
标准栈帧结构:
- 典型栈帧包含局部变量、保存的寄存器和参数空间
- 栈指针 (SP) 向低地址增长
- 帧指针 (FP) 可选使用
性能瓶颈分析
通过实际性能采样发现主要问题集中在:
- 内存访问开销:每次栈访问需要 3 - 5 个时钟周期
- 寄存器溢出:当变量超过可用寄存器时强制使用栈存储
- 栈空间浪费:对齐填充和过度预分配的栈空间
典型性能损失案例:
; 未优化函数序言
push {r4-r6, lr} ; 4 周期
sub sp, #32 ; 1 周期(分配局部变量空间)
核心优化策略
1. 寄存器分配优化
优先使用 r0-r3 传递参数,利用 r4-r11 保存中间结果。示例:
; 优化前(使用栈传递第 4 个参数)
push {r0-r3} ; 参数入栈
bl func
; 优化后(使用 r3 传递第 4 个参数)
mov r3, r0 ; 重用寄存器
bl func
2. 局部变量精简
通过合并变量和复用空间减少栈分配:
// 优化前
int a, b, c; // 3 个独立变量
// 优化后
struct { // 合并为结构体
int a;
union {b, c};
} vars;
3. 关键内联优化
对小型函数使用 __attribute__((always_inline)) 强制内联:
__attribute__((always_inline))
int clamp(int x) {return (x > 255) ? 255 : x;
}
4. 栈帧手动布局
精确控制栈空间分配和对齐:
; 优化前
sub sp, #16 ; 自动对齐可能产生浪费
; 优化后
sub sp, #12 ; 精确计算所需空间
and sp, #0xFFFFFFF8 ; 手动 8 字节对齐
性能对比测试
在 Cortex-A72 平台上测试典型数学函数:
| 优化方案 | 周期数(万次调用) | L1 缓存命中率 |
|---|---|---|
| 基线版本 | 1580 | 82% |
| 寄存器优化 | 1240(-21.5%) | 89% |
| 综合优化方案 | 876(-44.6%) | 93% |
安全与调试保障
所有优化需保证:
- 异常处理栈帧符合 ARM EHABI 规范
- 保持 DWARF 调试信息完整性
- 不破坏栈溢出保护机制
最佳实践指南
根据场景选择优化策略:
- 实时系统:优先保证最坏执行时间(WCET)
- 高性能计算:最大化寄存器利用率
- 内存受限设备:最小化栈空间占用
架构扩展思考
RISC- V 的调用约定与 ARM 存在差异:
- 更多通用寄存器(a0-a7, t0-t6)
- 硬件支持压缩指令(C 扩展)
- 可定制的调用约定
但核心优化思想 (减少内存访问、提高寄存器利用率) 仍适用,具体实现需调整。
总结
通过本文的优化方法,在实测中获得了最高 44.6% 的性能提升。关键点在于:精准控制栈内存访问、最大化寄存器利用率、保持代码可维护性。这些技术对嵌入式系统和性能敏感应用具有重要价值。
正文完
