- 函数调用的栈布局
RISC-V的函数调用约定,尽可能优先使用寄存器来传递参数。默认有8个整数寄存器a0-a7和8个浮点寄存器fa0-fa7可以用,其中前两个(a0,a1)、(fa0,fa1)也用来返回值传递。
函数参数是结构体时,则参数每一个字段按指针长度对齐,参数寄存器中保存的就是结构体最前面8个指针字长的参数数据。
浮点类型的参数将通过浮点寄存器fai传递,否则通过整数寄存器ai传递。
union类型的一部分或者struct数组的浮点参数将通过整数寄存器传递。
变长函数(variadic fuction)的所有参数(包括浮点)都通过整数寄存器传递。
比一个指针字长小的参数,通过参数寄存器的LSB传递。对应的通过栈传递的小于指针字的参数将出现在指针字的较低地址上(RISC-V默认为小端存储器系统)。
指针字的两倍长的参数通过栈传递的时候数据是自然对齐的,通过整数寄存器传递时,它们被放置在对齐的偶-奇( even-odd)寄存器对中, 其中的偶数编号寄存器保存了LSB。例如在RV32中,函数void foo(int, long long)通过a0传递它 的第一个参数,通过a2、a3传递第二个参数。寄存器a1中没有东西。 大小大于两倍指针字的参数,通过引用(reference)传递。
结构体中不能通过寄存器传递的部分参数将通过栈传递,栈指针sp指向第一个没有在寄存器中的参数。
函数的返回值存放在整数寄存器a0、a1或者浮点寄存器fa0、fa1中。不过只有当它们是原始参数或者是只包含1个或者2个浮点值的struct的时候,浮点值才通过浮点寄存器返回,其他两个指针字长类型的返回值放入a0和a1,更大的则全部通过存储器返回(此时这些存储器由调用者分配,并将它作为第一个隐藏参数传递给被调用者)。
在RISC-V调用约定标准中,栈是向下增长并且栈指针总是16字节对齐。
除了参数寄存器和返回值寄存器之外,7个整数寄存器t0-t6和12个浮点寄存器ft0-ft11是临时寄存器,它们在可以在调用过程中被修改,如果后面还有使用的话,则主调者必须先保存起来。12个整数寄存器s0-s11和12个浮点寄存器fs0-fs11在调用过程后被保持不变,如果被调者需要用的话,使用前需要先保存,使用后恢复。下表指明了在调用约定中每个整数寄存器和浮点寄存器扮演的角色。