calling convention
Entry sequence (the function prologue)
a few instructions at the beginning of a function, which prepare the stack and registers for use within the function.
Exit sequence (the function epilogue)
a few instructions at the end of a function, which restore the stack and registers to the state expected by the caller, and return to the caller. Some calling conventions clean the stack in the exit sequence.
temp register
在riscvcard中, 标注为Temoporaries
的寄存器,都被callee认为caller没有使用的寄存器,callee可以随意使用且不需要恢复。
saved register
在riscv中,s0-s11是saved register(也称为callee-saved register), 作为callee,认为这些reg是被caller使用过的寄存器,应该在调用过程结束前将这些寄存器的值恢复。(恢复是指进入调用过程前,需要先将这些寄存器的值保存,以便调用过程结束时恢复这些寄存器原本的值)
riscv calling convention 指定了哪一个register是被调用函数的caller保存的,哪一个register是被callee保存的。venus提供了一个
-cc
的命令来检查和calling convention
相关的bug。
使用cc前,注意:
- cc不能检查到所有的
calling convention
问题,有些需要自己去手动的找- cc只能检查在.globl声明的函数导致的
calling convention
问题,有些函数如果是被一个在.globl
中声明的函数调用的,那么cc可能不会报错。
violation msgs
cc总共能提供三种错误信息:Setting of a saved register (s0) which has not been saved!
/Save register s0 not correctly restored before return! Expected <hex value>, Actual <hex value>
/Usage of unset register t0
例如,有如下这样一段调用过程
.globl func
main:
jal func
li a0, 10 # Code for exit ecall
ecall
func:
# Paste the example definition of func
Setting of a saved register (s0) which has not been saved!
func函数写了saved register但是没有保存恢复,例如
func:
li s0, 100 # === Error reported here ===
li s1, 128 # === Error reported here ===
ret
func函数往s0,s1写了值,首先没有保存这些saved register的值,其次没有恢复。
正确的过程是:在prologue保存这些值,在epilogue恢复这些值。
To fix this, make sure you
func
stores the values of any registers it overwrites by clearing space on the stack and storing them in memory in the prologue, and then restoring their values and restoring the stack pointer in the epilogue.
正确的过程代码:实际上就是压栈桢,保存寄存器;恢复寄存器,弹栈桢
func:
# BEGIN PROLOGUE
# Each clobbered register (4 bytes each) needs to be stored
addi sp, sp, -8
sw s0, 0(sp)
sw s1, 4(sp)
# END PROLOGUE
li s0, 100
li s1, 128
# BEGIN EPILOGUE
lw s1, 4(sp)
lw s0, 0(sp)
addi sp, sp, 8
# END EPILOGUE
ret
Save register s0 not correctly restored before return! Expected
没有恢复saved register的值,其实只是上一个错误的一个子问题,例如:
func:
# BEGIN PROLOGUE
addi sp, sp, -8
sw s0, 0(sp)
sw s1, 4(sp)
# END PROLOGUE
li s0, 100
li s1, 128
# BEGIN EPILOGUE
# Forgot to restore the values of s0 and s1!
addi sp, sp, 8
# END EPILOGUE
ret # === Error reported twice here ===
cc会在ret报错,修改方法同上一个error
Usage of unset register t0
callee希望读取一个没有被caller设置的寄存器,在callee自身也没有初始化这个值
实际上,callee不应该知道caller对saved register是如何设置的,应该保持一种完全无知的状态
错误如下
func:
mv a0, t0 # === Error reported here ===
ret
Working with multi files
The .globl
声明函数是全局可见的,可以给其他.s使用。
Venus supports the .import
directive (not technically part of the RISC-V spec) to access other assembly files, similar to the C include
directive. It will only make labels marked .globl
available.
System calls
ecall可以发起系统调用(比如中断、异常)、访问文件系统、写入console。
发起系统调用需要设置正确的syscall代码,例如
li a0, 1 # syscall number for printing integer
li a1, 1024 # the integer we're printing
ecall # issue system call
系统调用部分,参考
https://man7.org/linux/man-pages/man2/syscall.2.html#architecture-calling-conventions
https://github.com/ThaumicMekanism/venus/wiki/Environmental-Calls