Recursion 理解MIPS中的递归

Recursion 理解MIPS中的递归,recursion,mips,Recursion,Mips,我在复习以前的作业,并试图遵循递归函数背后的逻辑。它只是将输入传递给“事实”,确定>=1,然后再次传递给循环。这是在循环中,我变得困惑。我理解调用函数时,通常明智的做法是使用sw保存数据,并在完成后使用lw加载数据。但是,循环调用fact(再次调用循环),直到输入=1;参数获取(n–1) 日航实况#呼叫实况(n-1) lw$a0,0($sp)#从日航返回:恢复参数n lw$ra,4($sp)#恢复返回地址 addi$sp$sp,8#将堆栈指针调整到pop 2项 mul$v0,$a0,$v0#返回

我在复习以前的作业,并试图遵循递归函数背后的逻辑。它只是将输入传递给“事实”,确定>=1,然后再次传递给循环。这是在循环中,我变得困惑。我理解调用函数时,通常明智的做法是使用sw保存数据,并在完成后使用lw加载数据。但是,循环调用fact(再次调用循环),直到输入<1。正如您在下面的代码中所看到的,它不断地将$ra和输入保存在同一位置。请注意,在实际完成递归之前,不会使用lw代码

这如何不覆盖旧数据?旧数据仅仅是被推后的吗?如果是这样的话,当递归被多次使用时,仅仅弹出两项就足够了吗?此代码中$v0的用途是什么

fact: slti $t0, $a0, 1 # test for n < 1, n is user input
      beq $t0, $zero, L1 # if n >= 1, go to L1
      li $v0, 1 # return 1
      jr $ra # return to instruction after jal

L1:   addi $sp, $sp, -8 # adjust stack for 2 items
      sw $ra, 4($sp) # save the return address
      sw $a0, 0($sp) # save the argument n
      addi $a0, $a0, -1 # n >= 1; argument gets (n – 1)
      jal fact # call fact with (n – 1)
      lw $a0, 0($sp) # return from jal: restore argument n
      lw $ra, 4($sp) # restore the return address
      addi $sp, $sp, 8 # adjust stack pointer to pop 2 items
      mul $v0, $a0, $v0 # return n * fact (n – 1)
      jr $ra # return to the caller
事实:slti$t0,$a0,1#测试n<1,n是用户输入
beq$t0,$zero,L1#如果n>=1,则转到L1
李$v0,1#返回1
jr$ra#日航后返回指令
L1:addi$sp$sp,-8#调整两个项目的堆栈
sw$ra,4($sp)#保存回信地址
sw$a0,0($sp)#保存参数n
addi$a0,$a0,-1#n>=1;参数获取(n–1)
日航实况#呼叫实况(n-1)
lw$a0,0($sp)#从日航返回:恢复参数n
lw$ra,4($sp)#恢复返回地址
addi$sp$sp,8#将堆栈指针调整到pop 2项
mul$v0,$a0,$v0#返回n*事实(n-1)
jr$ra#返回给来电者

这是堆栈操作的经典示例

每次函数执行时,它都会增加堆栈并将
$ra
$a0
存储在新分配的位置

您可以看到,
$ra
$a0
是相对于
$sp
(堆栈指针)分配的

每次函数执行时,堆栈都会通过从
$sp
中减去
8
或为两个单词留出足够的空间来展开。类似地,当函数退出时,通过将
8
添加到
$sp
来收缩堆栈


这样想:

假设我们要计算5

每次调用
fact
时,堆栈都存储
$a0
$ra
。调用
fact
5次后,堆栈将如下所示:

$a0: 5
$ra: back to main
----
$a0: 4
$ra: back to fact
---- 
$a0: 3
$ra: back to fact
----
$a0: 2
$ra: back to fact
----
$a0: 1
$ra: back to fact
----
一旦基本情况执行(
$a0
=1),堆栈开始收缩。
$a0
=1堆栈在
$v0
中返回
1
,当我们加载
$a0
时,我们得到
2
,因为我们已经收缩了堆栈。所以我们将
2
乘以
1
,然后返回该值。在下一个堆栈帧中重复相同的过程,我们取
$v0
中返回的
2
,并将其乘以从堆栈中加载的
3
,然后在
$v0
中返回
6

希望您能从这里看到:

addi $a0 $zero 5
jal fact

将在
$v0

中返回
120
,这是我最初看到的情况,但在调用lw或addi$sp,$sp,8之前使用jal fact。这不会导致堆栈上的内容被覆盖吗?不会。
jal
只是一个跳转,它也会将当前PC放入
$ra
。如果函数要执行8次,它将首先扩展8次,然后收缩8次。好的,那么mul$v0,$a0,$v0呢?因为这是在jal和lws之后,这不意味着我们只是将n乘以1吗?
$a0
的值在
mul
语句之前重新加载。
$v0
的值是由调用
事实
决定的。您能给我一个如何工作的示例吗?例如,假设我们通过的数字是5。一步一步会发生什么?