寄存器指南

RISC-V sp / x2 寄存器:栈指针、栈帧与 ABI 对齐规则

sp 是 RISC-V 标准调用约定使用的栈指针。函数会围绕 sp 建立栈帧、保存需要恢复的值或接收栈上传参;标准 ABI 要求 sp 在过程入口按 128-bit 边界对齐,并在执行期间保持对齐。

psABI:x2 的 ABI 名称是 sp,调用后保持。
psABI:标准 ABI 中栈向低地址增长,sp 需要 128-bit 对齐。
新手规则:只按栈帧规则移动 sp,返回前恢复;不要当普通临时寄存器。
物理编号 x2ABI 名 sp保存责任 被调用者

psABI 将 sp 标记为调用后保持,但它是特殊的:函数可以在 prologue 中移动 sp 来建立栈帧,只要在 epilogue 中精确恢复到入口值即可。这与普通 s* 寄存器(借用后必须逐位还原)在「可移动」这一点上有本质区别。

角色
栈指针
调用约定
被调用者保存
先记一句
标准 ABI 要求栈指针在过程入口按 128-bit 边界对齐,并在执行期间保持对齐。
psABI 规范依据

栈指针

psABI 将 x2 命名为 sp,并标记为调用后保持。函数可以在栈帧建立期间移动 sp,但返回前必须恢复到入口值。

调用后保持:
RISC-V psABI integer register convention
快速理解与检索要点

sp / x2 是标准调用约定的栈指针。psABI 标记它调用后保持;函数可以移动 sp 建立栈帧,但返回前必须恢复,并保持标准 ABI 的 128-bit 对齐要求。

psABI 将 x2 命名为 sp,并标记为调用后保持。
标准 ABI 中栈向低地址增长,sp 在过程入口按 128-bit 边界对齐。

适合什么时候用

  • - 建立当前函数的栈帧,保存 ra、s* 寄存器和局部变量。
  • - 处理参数寄存器不够时放到栈上的额外参数。
  • - 在函数入口下移,在函数返回前恢复到入口值。

什么时候不要这样用

  • - 不要把 sp 当普通临时寄存器参与计算。
  • - 不要只调整 sp 不恢复,调用链和局部变量访问都会被破坏。

调用前后会发生什么

1

标准 ABI 要求栈指针在过程入口按 128-bit 边界对齐,并在执行期间保持对齐。

2

callee 可以移动 sp 建立栈帧,但返回前必须恢复。

3

调用其他函数前,当前函数应确保栈状态满足 ABI 要求。

典型 RV64 栈帧进入与退出

示例用于理解规则,不替代完整程序
addi sp, sp, -16
sd   ra, 8(sp)
sd   s0, 0(sp)
addi s0, sp, 16
...
ld   s0, 0(sp)
ld   ra, 8(sp)
addi sp, sp, 16
ret
1.RV64 示例:分配 16 字节栈空间(sp 向低地址移动)
2.保存返回地址 ra 到 sp+8
3.保存旧 frame pointer s0 到 sp+0
4.建立当前 frame pointer,s0 指向调用者的栈帧底部
5.函数体,此时可通过 s0 相对寻址访问局部变量和参数
6.从 sp+0 恢复旧 s0
7.从 sp+8 恢复 ra
8.释放栈空间(sp 回退到调用者位置)
9.通过 ra 返回调用者

与其他寄存器的关系

sp vs s0/fp (帧指针)

sp 是 x2 栈指针,标准 ABI 中栈向低地址增长,过程入口的 sp 需要保持 128-bit 对齐。s0/fp 是 x8;psABI 规定帧指针是可选的,但如果存在,必须位于 x8/s0。两者都属于调用后保持的 ABI 状态,但 sp 负责栈位置,s0/fp 只在采用帧指针约定时承担帧指针角色。

为什么不能当普通寄存器用

把 sp 当作普通临时寄存器使用极度危险。sp 被错误修改后,saved registers、return address、local variables、传入/传出参数以及整个调用链的栈帧都会遭到破坏。最常见的故障模式是 ret 跳转到错误地址,或函数返回后 caller 的局部变量值被意外覆盖。sp 只能按 ABI 约定移动(prologue 下移、epilogue 回退),绝不能参与通用算术计算。

常见问题

sp / x2 可以当临时寄存器用吗?

不应该。sp 是栈指针,错误修改会破坏栈帧、保存寄存器和返回地址。

RISC-V 标准 ABI 对 sp 有什么对齐要求?

标准 ABI 要求 sp 在过程入口按 128-bit 边界对齐,并在执行期间保持对齐。