【问题标题】:MIPS stack visualizationMIPS 堆栈可视化
【发布时间】:2017-02-04 13:13:44
【问题描述】:

下面是阶乘函数的 c 代码。 我清楚地了解递归函数的工作原理。 但我无法想象或不知道如何在 MIPS 堆栈上工作。

MIPS 代码

我的问题是, 我们如何为递归的每一步在堆栈中保存递归函数(保存返回地址和参数)?

我知道,对于每个递归函数,我们都在保存参数和返回地址,然后重新调整堆栈指针。

谁能帮我想象一下这个过程是如何工作的。

【问题讨论】:

  • 你解释得差不多了。每次进行调用时,您都将这两个值存储在堆栈中。它是一个堆栈,所以它是一个线性内存块。我不知道如何使它更清楚。
  • 只是每个递归调用的一些图片会很棒,所以我对这个想法有 100% 的信心

标签: assembly stack mips


【解决方案1】:

前提 MIPS 中的堆栈的工作原理与任何其他架构中的堆栈一样,因此您只需 Google 即可。


假设堆栈指向内存的任意位置:

|  xxx   |                xxx = Undefined 
|  xxx   | <- $sp
|        |
|        | | Free area, stack moves down
|        | V

现在只需模拟对fact(3) 的调用。
您粘贴了代码的图像,因此此处不会显示任何代码。一个可复制粘贴的代码会使这个答案更清楚。

fact 的每次调用都会按此顺序推送返回地址和参数。
假设 fact 位于 0x1000,而对 fact 的调用位于 0x1ffc。

fact(3)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | <- $sp          Argument of fact(3) 
|        | 

fact(2) called from fact(3)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
|    2   | <- $sp          Argument of fact(2)

fact(1) called from fact(2)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
|    2   | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
|    1   | <- $sp          Argument of fact(1)

fact(0) called from fact(1)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
|    2   | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
|    1   | Argument of fact(1)
| 0x1028 | Return address to L1+8 label
|    0   | <- $sp          Argument of fact(0)

fact(0) 返回 1 并从堆栈中删除两项。
fact(0) 是叶调用,即最后一次调用,因此没有其他调用更改$ra,此外$a0(参数)是不需要,因此保存在堆栈中的这两个值只是通过递增$sp 来丢弃。

Just before "jr $ra" in fact(0)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
|    2   | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
|    1   | <- $sp           Argument of fact(1)

其他返回从堆栈中恢复$a0$ra的值以计算n*fact(n-1)并返回给它们的调用者。

Just before "jr $ra" in fact(1)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
|    2   | <- $sp           Argument of fact(2)

Just before "jr $ra" in fact(2)

|  xxx   | 
|  xxx   |
| 0x2000 | Return address to caller of fact(3)
|    3   | <- $sp            Argument of fact(3)

Just before "jr $ra" in fact(3)

|  xxx   | 
|  xxx   | <- $sp

注意堆栈是如何返回到递归链末尾的原始位置的。

【讨论】:

  • fact 的角度来看,进入fact 时的xxx 值是“未定义的”,但可能更恰当地表述为“由调用者拥有”,因为它们可能具有调用者的值放在那里,不能改变。对于fact 的级别 1 调用,它们是级别 0 的堆栈帧。
  • 感谢@Craig 的注释,我打算将“未定义”作为“未在此上下文中定义”,因为(fact 的)调用者堆栈不是讨论的一部分。我相信一旦他们正确理解堆栈的工作原理,OP 就会意识到你自己所说的事实:)
猜你喜欢
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-27
  • 2010-11-16
  • 2020-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多