MIT 6.S081 Lecture 5 Notes

People make mistakes. It’s why they put rubbers on the ends of pencils.

6.S081第五课,教授GBD调试与RISC-V的调用规范。阅读RISC-V Calling Convention,观看课程视频。之前第二节课讲过GDB使用,这节课只是更贴合 RISC-V 的指令与规范,便于日后Lab的调试。

RISC-V Calling Convention

当可能的时候,RISC-V调用传参会仅用寄存器。最多用 8 个整数寄存器 a0-a7 和 8 个浮点数寄存器 fa0-fa7

如果函数的参数被概念化为C结构体的字段,每个字段都具有指针对齐,则参数寄存器是该结构体前8个指针字的投影(副本)。如果参数 i < 8 是浮点类型,则在浮点寄存器fai中传递;否则,它将在整数寄存器ai中传递。但作为 union 字段或 struct 数组字段的浮点参数是在整数寄存器中传递的。此外,可变参数函数的浮点参数(除了那些在形参列表中显式命名的)通过整数寄存器传递。

比指针字小的参数被传递到参数寄存器的最低有效位中。相应地,在栈上传递的 sub-pointer-word 参数出现在指针字的较低地址中,因为RISC-V是小端存储。(即 比指针字小的参数 如果通过栈传递,也是出现在低地址)

当在栈上传递两个指针字大小的 primitive arguments 时,它们自然对齐。当它们在整数寄存器中传递时,它们位于一个对齐的偶数-奇数寄存器对中,偶数寄存器保存最低有效位。例如,在RV32中,函数 void foo(int, long long)a0 中传递第一个参数,在 a2a3 中传递第二个参数。a1中没有传递任何东西。

大于指针字大小两倍的参数通过引用传递。(参数size太大,通过 caller 在栈上开一块内存,然后传地址)

未在参数寄存器中传递的结构体字段在堆栈上传递。堆栈指针sp指向未传入寄存器的第一个参数。(参数寄存器不够用,通过栈来传)

函数返回值从整数寄存器 a0a1 以及浮点寄存器 fa0fa1 传递。只有当浮点值是 primitive 或仅由一个或两个浮点值组成的结构成员时,才会在浮点寄存器中返回浮点值。其他适合两个指针字的返回值在a0和a1中返回。更大的返回值完全在内存中传递;caller 分配这个内存区域,并将指向该区域的指针作为隐式的第一个参数传递给被调用者。

在标准的RISC-V调用规范中,堆栈向下增长,堆栈指针始终保持16字节对齐。

除了参数寄存器和返回值寄存器外,7个整数寄存器 t0-t6 和12个浮点寄存器 ft0-ft11 都是临时寄存器,它们在调用时是不稳定(volatile)的,如果以后使用,调用者必须保存它们。12个整数寄存器 s0-s11 和12个浮点寄存器 fs0-fs11 在调用时 preserved ,如果使用,则必须由被调用方保存。以下为寄存器其ABI名字、负责维护者与描述的表格。

reg name saver description
x0 zero   hardwired zero
x1 ra caller return address
x2 sp callee stack pointer
x3 gp   global pointer
x4 tp   thread pointer
x5-7 t0-2 caller temporary registers
x8 s0/fp callee saved register / frame pointer
x9 s1 callee saved register
x10-11 a0-1 caller function arguments / return values
x12-17 a2-7 caller function arguments
x18-27 s2-11 callee saved registers
x28-31 t3-6 caller temporary registers
f0-7 ft0-7 caller FP temporaries
f8-9 fs0-1 callee FP saved registers
f10-11 fa0-1 caller FP arguments/return values
f12-17 fa2-7 caller FP arguments
f18-27 fs2-11 callee FP saved registers
f28-31 ft8-11 caller FP temporaries
pc     program counter

RISC-V 在不支持浮点数寄存器扩展情况下,浮点数会存放于整数寄存器,规则与等大小 size 的整数相同。这种称为 “Soft-Float Calling Convention” 。

Conclusion

这节课主要讲RISV-V调用的规范,以及一些指令、寄存器与栈分配的知识。包括 call 如何跳转地址,相对jump与绝对jump ,调用 Leaf function 不用保存 ra 寄存器;但调用非 Leaf function 需要 caller 进行保存。都是学过的知识,与 xv6 基本思想大同小异。具体的RISC-V指令集架构可以看

  • RISC-V ISA specification: https://riscv.org/specifications/ Contains detailed information
  • RISC-V ISA Reference: https://rv8.io/isa Overview of instructions
  • RISC-V assembly language reference: https://rv8.io/asm Overview of directives, pseudo-instructions, and more

Reference

  1. MIT 6.S081 2021
  2. RISC-V Calling Convention
  3. Videos