risc-v C程序调用规范 (calling conventions)

risc-v C程序调用规范 (calling conventions)

在上一篇文章里,已经介绍了如何搭建xv6的运行环境,在这一篇文章介绍一下risc-v C调用规范。

  1. 首先看一下C语言下的数据类型在risc-v下所占据的大小,如下图:
    risc-v C程序调用规范 (calling conventions)
    可以看出除了long和void*的大小是和平台有关,其他类型无论是在32位还是64位平台上,他们的大小都是一样的。

  2. 函数调用规范

在risc-v平台上,有8个整形寄存器(a0a7),8个浮点寄存器(fa0fa7)。

a.如果函数的参数是结构体成员,那么每个参数要按照所在平台上指针类型大小对齐,比如32位平台4字节对齐,64位平台8字节对齐。由于至多有8个寄存器,所以结构体至多8个成员会被放在寄存器中,剩余的部分被存放在栈上,sp指向第一个没有被存放在寄存器上的结构体成员。如果第i个参数是整形类型,那么就存放在整形寄存器ai上,如果第i个参数是浮点数类型,那么就存放在浮点寄存器fai上。联合体中的浮点成员或者结构体中的数组成员也是通过整形寄存器来传递的。变长参数函数的浮点参数传递也是通过整形寄存器来传递(明确声明浮点寄存器传递的除外),最典型的变长参数函数比如printf。

b. 小于1个指针字长的参数被存在寄存器的低位,比如char类型的参数被存在整形寄存器的低8位。同理小于1个指针字长的参数存在栈上也是存放在内存的低地址上,这和risc-v的内存系统是小端系统是一致的。如果参数是基本类型并且是指针字长的两倍,那么偶数寄存器存放的是低字节,比如void foo(int a,long long b)那么寄存器a0存放a,寄存器a2和a3存放b,a2存放参数b的低位,跳过了a1。如果参数是基本类型大于指针字长的两倍,那么这些参数的传递是通过引用。

c. 函数返回值如果是基本类型或者只包含一两个成员的基本结构体的成员,存放在相应的整形寄存器a0和a1,浮点寄存器fa0和fa1. 大于两个指针字长的返回值存放在内存上(该内存有调用者分配,并且把指针作为第一个参数传递给被调者)。

d. 栈是从高地址向低地址方向的, 并且是16字节对齐的。

e. 寄存器t0t6,ft0ft11被称为临时寄存器,由调用者保存。s0~s11, fs0~fs11由被调者保存。