过程的执行步骤
将参数放在过程可以访问的位置
调用者将参数放在$a0-$a3
寄存器中。
将控制转交给过程
通过jal
指令,跳转到过程开始所在的指令。此时会把下一条指令的地址(即 PC+4
)存储在$ra
寄存器中,供控制返回时跳转。
程序计数器(PC):寄存器,其值为程序中正在被执行的指令的地址。
获得过程所需的存储资源
为避免寄存器使用冲突,需要将寄存器的值换出到存储器(压栈),返回之前,将寄存器还原(出栈)。
栈指针存储在$sp
寄存器中。
leaf_example:
addi $sp, $sp, -12 // 申请3个空间
// 将 $s0、$s1、$s2三个寄存器的值压栈
sw $s0, 8($sp)
sw $s1, 4($sp)
sw $s2, 0($sp)
// 自由使用这三个存储器
...
// 返回前,将三个存储器的值还原
lw $s2, 0($sp)
lw $s1, 4($sp)
lw $s0, 8($sp)
addi $sp, $sp, 12 // 释放申请的3个空间
jr $ra
执行需要的任务
将结果的值放在调用程序可以访问的位置
被调用者将结果放在$v0
、$v1
寄存器中。
将控制返回初始点
调用寄存器跳转指令jr
,跳转到初始点,其位置在$ra
寄存器中。
例:计算阶乘
int fact (int n) {
if (n > 1) {
return 1;
} else {
return (n * fact(n - 1))
}
}
fact:
// 申请2个空间存储返回地址寄存器$ra和参数寄存器$a0
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
// 如果n < 1,$t0的值为1,如果$t0的值为0(即n>=1),则跳转到L1
slti $t0, $a0, 1
beq $t0, $zero, L1
// 如果 n < 1,返回1(存储在$v0寄存器中)
addi $v0, $zero, 1
addi $sp, $sp, 8 // 释放申请的寄存器空间
// 返回
jr $ra
L1:
// 将参数n的值减1,重新调用fact
addi $a0, $a0, -1
jal fact
fact返回处:
// 释放申请的栈空间
lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
//计算乘法
mul $v0, $a0, $v0
jr $ra