MIT6828 学习笔记 010 (lec 5)
RISC-V 调用约定、栈帧和 GDB
有时(比如 lab syscall )需要一些 C 语言无法表达的代码
RISC-V 抽象计算机:没有像 C 语言一样的流程控制、变量的概念、类型等等;基本的 ISA :程序计数器、 32 个通用寄存器 (x0-x31)
| 寄存器 | 名字 | 保存者 | 描述 |
|---|---|---|---|
x0 | zero | hardwired zero | |
x1 | ra | 调用方 | 返回地址 |
x2 | sp | 被调用方 | 栈指针 |
x3 | gp | 全局指针 | |
x4 | tp | 线程指针 | |
x5-x7 | t0-t2 | 调用方 | 临时寄存器 |
x8 | s0/fp | 被调用方 | 保存寄存器/栈指针 |
x9 | s1 | 被调用方 | 保存寄存器 |
x10-x11 | a0-a1 | 调用方 | 函数参数/返回值 |
x12-x17 | a2-a7 | 调用方 | 函数参数 |
x18-x27 | s2-s11 | 被调用方 | 保存寄存器 |
x28-x31 | t3-t6 | 调用方 | 临时寄存器 |
pc | 程序计数器 |
比如下面的代码:
1 | int sum_to(int n) { |
用汇编实现:
1 | # sum_to(n) |
ret 的语义:
1 | ret := |
其他函数如何调用这个 sum_to :
1 | main: |
call 的语义:
1 | call label := |
嵌套调用:
1 | sum_then_double: |
会导致死循环: main 处 call 使 ra 指向自己的下一行(第 9 行下面),然后进入 sum_then_double , sum_then_double 的 call 又使 ra 指向自己的下一行(第 3 行),然后进入 sum_to , sum_to 进行 ret 进入 ra 指向的语句(第 3 行),第 5 行的 ret 又会进入 ra 指向的语句第 3 行,导致死循环。
尝试解决:把 ra 放进其他寄存器——无论如何都要用 call&ret 所以只是把问题延后了。答案:使用栈
1 | sum_then_double: |
问:上面用 a0 接收参数,把返回值放进了 a0 ,为啥不用别的,比如从 t2 接收参数,把返回值放进 t3 ?答:这就是调用约定, a0-a7 用于传递参数,放不下的话把剩下的放进栈里; a0, a1 保存返回值。
手写出来的汇编代码应该遵循调用约定,就像 GCC 编译器编译出的代码也是遵循调用约定的,这样才能使每个人的代码都能互相操作。
汇编和 C 语言互相调用:遵守调用约定,在 C 代码写出函数原型,编译器就能知道怎么调用汇编代码。