ABI 调用约定

RISC-V ABI 调用约定在线查询

本页是 RISC-V ABI 调用约定在线查询页面,用于快速查看函数调用时参数如何通过 a0-a7 传递、返回值如何通过 a0/a1 返回、ra/sp/s0 等特殊寄存器的作用、caller-saved 与 callee-saved 的保存责任,以及栈帧和 16 字节对齐规则。适合阅读反汇编、编写 RISC-V 汇编、调试函数调用和理解编译器生成代码。

一句话记住
传参进 a0-a7,结果回 a0;a*/t*/ra 调用方自保,s*/sp 被调方归还;栈帧分配后退出前必须恢复。
寄存器角色分配

参数寄存器

a0 – a7 (x10 – x17)

标准整数调用约定中,前 8 个整型或指针参数放入 a0-a7;多余参数通过栈传递。a0-a7 不跨调用保留,调用后可能被覆盖。

返回值寄存器

a0, a1 (x10, x11)

整数标量返回值使用 a0,必要时可使用 a0/a1 两个参数/返回值寄存器。ret 只负责按返回地址跳回,不会自动生成返回值。

调用方保存寄存器

t0 – t6 (x5 – x7, x28 – x31), ra (x1), a0 – a7 (x10 – x17)

如果调用方在 call 之后还需要这些寄存器的值,必须在 call 前自己保存。被调函数可以自由覆盖它们。

被调方保存寄存器

s0 – s11 (x8 – x9, x18 – x27), sp (x2)

调用方可以信任 s* 跨调用保持不变。如果被调函数要修改这些寄存器,必须在入口保存并在返回前恢复。sp 进入函数可下移分配栈帧,退出前必须恢复到进入时的位置。

特殊用途寄存器

zero (x0), ra (x1), sp (x2), gp (x3), tp (x4), fp/s0 (x8)

zero 硬连线为 0,写入无效。ra 存返回地址,call 写入,ret 按 ra 跳回。sp 在标准 ABI 中按 128-bit(16 字节)边界对齐。gp 与 tp 是不可分配寄存器,通常由平台、运行时或 TLS 机制维护。fp/s0 可选用作帧指针。

调用约定细则

参数传递

整数调用约定使用 a0-a7 传递前 8 个整型或指针参数。启用硬浮点 ABI 时,浮点实参可使用 fa0-fa7;否则按整数调用约定或栈传递。

返回值

标量结果通常放在 a0;需要两个寄存器时用 a0/a1。无法按寄存器返回的聚合值由调用方提供返回对象地址,约定细节以 psABI 的 flattening 和返回规则为准。

保存规则

caller-saved(a*, t*, ra):调用方负责保存。callee-saved(s*, sp):被调方借了必须还。跨调用需要保持的值优先放在 s* 或栈上。

栈帧

栈向低地址增长。进入函数时 sp 下移分配栈帧,退出时恢复到入口位置。标准 ABI 要求 sp 在过程入口处保持 128-bit(16 字节)对齐,并在过程执行期间保持对齐。

补充要点

系统调用

ecall 是 ISA 定义的环境调用指令;具体系统调用号、参数寄存器和错误返回值属于执行环境或操作系统 ABI。常见 Linux RISC-V 用户态 ABI 约定使用 a7 放 syscall 号,a0-a5 放参数,a0 返回结果。

16 字节栈对齐

psABI 将标准栈对齐写成 128-bit 边界,也就是 16 字节。手写汇编如果会调用其他函数,分配局部栈空间后仍要让 sp 保持这个对齐。

帧指针(可选)

s0/fp 不是每个函数都必须设置。编译器在需要调试回溯或栈内偏移动态变化时才会生成帧指针。手写汇编时可以不用 fp,只要正确维护 sp。

官方规范来源

本页参考以下官方 RISC-V 文档组织架构、ABI、CSR 和伪指令说明;平台或操作系统 ABI 差异仍需按对应规范核验。