Skip to content
Published at:

附录 D. 寄存器 ABI 速查表

RISC-V 定义 32 个通用整数寄存器(x0-x31)和 32 个浮点寄存器(f0-f31)。应用二进制接口(ABI)规定了寄存器在函数调用中的角色和保存责任。Caller-saved(调用者保存):被调函数可随意修改,调用者需在调用前自行保存。Callee-saved(被调者保存):被调函数如需使用,必须在函数入口保存并在返回前恢复原值。


整数寄存器(x0-x31)

寄存器ABI 名称用途保存约定备注
x0zero硬连线 0写入丢弃,读取恒为 0
x1ra返回地址Callerjal / jalr 自动写入
x2sp栈指针Callee指向栈底(向低地址增长)
x3gp全局指针位置无关代码中指向 GOT
x4tp线程指针线程局部存储(TLS)基址
x5t0临时 0Caller函数调用中不保留
x6t1临时 1Caller
x7t2临时 2Caller
x8s0 / fp保存 0 / 帧指针Callee通常用于指向当前栈帧
x9s1保存 1Callee
x10a0参数 0 / 返回值 0Caller第一参数,第一返回值
x11a1参数 1 / 返回值 1Caller第二参数,第二返回值
x12a2参数 2Caller
x13a3参数 3Caller
x14a4参数 4Caller
x15a5参数 5Caller
x16a6参数 6Caller
x17a7参数 7Caller也用于系统调用号
x18s2保存 2Callee
x19s3保存 3Callee
x20s4保存 4Callee
x21s5保存 5Callee
x22s6保存 6Callee
x23s7保存 7Callee
x24s8保存 8Callee
x25s9保存 9Callee
x26s10保存 10Callee
x27s11保存 11Callee
x28t3临时 3Caller
x29t4临时 4Caller
x30t5临时 5Caller
x31t6临时 6Caller

寄存器分类汇总

类别寄存器说明
硬连线x0 (zero)恒为 0
返回地址x1 (ra)函数返回地址
栈/全局/线程x2(sp), x3(gp), x4(tp)特殊用途指针
临时 (Caller)x5-x7(t0-t2), x10-x17(a0-a7), x28-x31(t3-t6)共 15 个,调用者负责保存
保存 (Callee)x8-x9(s0-s1/fp), x18-x27(s2-s11)共 12 个,被调者负责保存
参数/返回值x10-x11(a0-a1)前 2 个参数,前 2 个返回值
参数x12-x17(a2-a7)第 3-8 个参数

浮点寄存器(f0-f31)

寄存器ABI 名称用途保存约定备注
f0ft0临时 0Caller
f1ft1临时 1Caller
f2ft2临时 2Caller
f3ft3临时 3Caller
f4ft4临时 4Caller
f5ft5临时 5Caller
f6ft6临时 6Caller
f7ft7临时 7Caller
f8fs0保存 0Callee
f9fs1保存 1Callee
f10fa0参数 0 / 返回值 0Caller第一个浮点参数和返回值
f11fa1参数 1 / 返回值 1Caller第二个浮点参数和返回值
f12fa2参数 2Caller
f13fa3参数 3Caller
f14fa4参数 4Caller
f15fa5参数 5Caller
f16fa6参数 6Caller
f17fa7参数 7Caller
f18fs2保存 2Callee
f19fs3保存 3Callee
f20fs4保存 4Callee
f21fs5保存 5Callee
f22fs6保存 6Callee
f23fs7保存 7Callee
f24fs8保存 8Callee
f25fs9保存 9Callee
f26fs10保存 10Callee
f27fs11保存 11Callee
f28ft8临时 8Caller
f29ft9临时 9Caller
f30ft10临时 10Caller
f31ft11临时 11Caller

浮点寄存器分类汇总

类别寄存器数量
临时 (Caller)f0-f7(ft0-ft7), f10-f17(fa0-fa7), f28-f31(ft8-ft11)20
保存 (Callee)f8-f9(fs0-fs1), f18-f27(fs2-fs11)12
参数/返回值f10-f11(fa0-fa1)2

函数调用约定

参数传递规则

当函数参数同时包含整数和浮点类型时,按照 RISC-V LP64D ABI:

参数位置整数寄存器浮点寄存器说明
参数 1a0 (x10)fa0 (f10)整数在 a0,浮点在 fa0
参数 2a1 (x11)fa1 (f11)
参数 3a2 (x12)fa2 (f12)
参数 4a3 (x13)fa3 (f13)
参数 5a4 (x14)fa4 (f14)
参数 6a5 (x15)fa5 (f15)
参数 7a6 (x16)fa6 (f16)
参数 8a7 (x17)fa7 (f17)
参数 9+栈传递栈传递sp+0, sp+8, ...

混合参数:如 func(int a, double b, int c),a 用 a0,b 用 fa0,c 用 a1。整型和浮点各自独立编号。

返回值规则

返回值大小寄存器说明
8/16/32/64 位整数a0 (x10)符号/零扩展至 64 位
128 位整数a0+a1 (x10+x11)低于 64 位在 a0
浮点数 (float)fa0 (f10)
浮点数 (double)fa0 (f10)
结构体(<=2*XLEN)a0+a1按整型寄存器分解传递
大型结构体调用者分配内存,a0 传入指针

栈帧布局

高地址
+-------------------+
|   调用者栈帧       |
|   ...             |
|   varargs(如有)  |  <- 被调函数的可选可变参数
+-------------------+
|   ra 保存区        |  <- 返回地址(如有必要)
|   fp 保存区        |  <- 帧指针(如有必要)
|   s0-s11 保存区    |  <- Callee-saved 寄存器
|   局部变量         |
|   溢出参数区       |  <- 超过 8 个的参数
+-------------------+ <- sp(栈指针,16 字节对齐)
低地址

栈对齐与红区

约定规则
栈对齐16 字节边界(sp 必须满足 sp % 16 == 0
红区(Red Zone)sp 以下 2048 字节内,信号处理程序不会修改,可被函数用作临时空间
调用入口 sp16 字节对齐
调用返回 sp必须恢复到调用前的值

Linux RISC-V 系统调用约定(LP64)

项目规则
系统调用号存入 a7 (x17)
参数 1a0 (x10)
参数 2a1 (x11)
参数 3a2 (x12)
参数 4a3 (x13)
参数 5a4 (x14)
参数 6a5 (x15)
返回值a0 (x10);错误时返回 -errno(负值)
指令ecall
临时破坏a1t0-t6内容可能被破坏

常用 Linux RISC-V 系统调用号(64 位)

系统调用调用号 (a7)说明
sys_read63读取文件
sys_write64写入文件
sys_openat56打开文件
sys_close57关闭文件
sys_exit93退出进程
sys_brk214调整堆顶
sys_mmap222内存映射
sys_munmap215取消内存映射
sys_clone220创建线程
sys_nanosleep101休眠

完整系统调用号参考:/usr/include/asm-generic/unistd.h 或 Linux 内核 include/uapi/asm-generic/unistd.h


寄存器保存约定速记

被调函数入口/出口模板

asm
# 函数入口(Prologue)
addi sp, sp, -N        # 分配栈空间
sd ra, 0(sp)           # 保存返回地址(如需调用子函数)
sd s0, 8(sp)           # 保存 callee-saved 寄存器
# ... 保存其他 s2-s11
# 函数体 ...

# 函数出口(Epilogue)
ld ra, 0(sp)           # 恢复返回地址
ld s0, 8(sp)           # 恢复 callee-saved 寄存器
# ... 恢复其他 s2-s11
addi sp, sp, N         # 释放栈空间
ret                    # jalr x0, ra, 0

寄存器保存决策树

函数会修改 s0-s11 中的任何一个?
  └─ 是 → 必须在入口保存原值,出口恢复
  └─ 否 → 不需要

函数会调用其他函数(非叶函数)?
  └─ 是 → ra 必须在入口保存(sd ra, offset(sp))
  └─ 否 → 叶函数无需保存 ra

函数会使用 x5-x7, x10-x17, x28-x31 中的寄存器?
  └─ 是 → 调用者需在调用前保存(被调函数不需要处理)
  └─ 否 → 无需处理