MIPS架构中调用保存与非调用保存寄存器的差异及$ra疑问
MIPS架构中调用保存寄存器与非调用保存寄存器的差异及$ra机制解析
核心差异
- 调用保存寄存器($s0-$s7、$ra):这类寄存器的上下文需要被保留。如果当前函数要调用其他函数,必须先把这些寄存器的值存入栈中,等被调用函数执行完毕返回后,再从栈里恢复原值——这样上层函数的原有数据不会被破坏。
- 非调用保存寄存器($t0-$t9、$a0-$a3、$v0-$v1):这类寄存器的上下文无需保留。被调用函数可以直接使用这些寄存器,不用提前保存值,因为调用者本来就不指望这些寄存器的值在函数调用后还能维持原样。
$ra(返回地址寄存器)的核心机制
$ra是专门存储当前函数执行完毕后返回地址的调用保存寄存器,核心逻辑如下:
- 当执行
jal(跳转并链接)指令调用函数时,CPU会自动把当前指令的下一条指令地址存入$ra,这样函数执行完后能通过jr $ra回到调用者的正确位置。 - 但如果当前函数还要嵌套调用其他函数,新的
jal指令会直接覆盖$ra中原有的返回地址——这时候必须手动把$ra的值先保存到栈上,否则原来的返回地址会丢失,函数执行完无法正确返回。
举个嵌套调用的示例代码:
main: jal func1 # 执行此指令时,$ra被设为main中jal的下一条指令地址 # ...后续代码 func1: addi $sp, $sp, -4 # 栈指针下移,预留栈空间 sw $ra, 0($sp) # 将当前$ra(指向main的返回地址)存入栈 jal func2 # 调用func2,此时$ra被覆盖为func1中jal的下一条指令地址 lw $ra, 0($sp) # 从栈恢复原来的$ra值 addi $sp, $sp, 4 # 栈指针上移,释放栈空间 jr $ra # 返回main func2: # 作为叶子函数(不调用其他函数),无需保存$ra jr $ra # 返回func1
$ra使用的关键注意点
- 只有函数内部存在嵌套调用(即要执行
jal调用其他函数)时,才需要保存$ra到栈;如果是叶子函数(不调用其他函数),直接用jr $ra返回即可,无需保存。 - 保存和恢复$ra的操作必须成对执行,保证栈的平衡,否则会导致程序崩溃或执行异常。
内容的提问来源于stack exchange,提问作者whykay
相关产品推荐
相关产品推荐

