4

假设我们正在检查一个只有一个局部变量 x 的函数 f 的 MIPS 汇编代码:

void f(void) {
int x;
...
}

我有两个问题:

  1. f 的函数 prologue 和 epilogue 对寄存器 $sp、$ra 和 $fp 做了什么,假设 $ra 和 $fp 是函数修改的唯一被调用者保存的寄存器。

  2. f 的 MIPS 汇编代码如何访问变量 x。

我的尝试:函数序言将寄存器 $ra 和 $fp 保存到调用堆栈中。Epilogue 函数通过将这些寄存器从堆栈中弹出来恢复这些寄存器,并将控制权返回到 $ra 中的地址。不确定 MIPS 如何访问变量 x,但我知道局部变量也存储在堆栈中。

4

3 回答 3

6

(a) f 的函数 prologue 和 epilogue 对寄存器 $sp、$ra 和 $fp 做了什么,假设 $ra 和 $fp 是函数修改的唯一被调用者保存的寄存器。

$fp是“帧指针”,也称为“基指针”,$sp是“堆栈指针”,$ra是“返回地址”)

要解释如何访问“ int x ”,了解它的存储方式和存储位置很重要。由于“ int x ”是一个局部变量,MIPS 将通过减去 32 位整数的字节数 (4) 为堆栈上的整数分配适当的空间(或者如果有空间,则使用 markgz 所说的方法)从堆栈指针。调用者的返回地址也被保存(另外 4 个字节),以便函数可以链接回调用者:

sub $sp, $sp, 8   #4 bytes for $ra + 4 bytes for 'int x' = 8 bytes allocated
sw $ra, 4($sp)    #note the order, $ra is always first
sw [int x], 0($sp)

或者

addi $sp, $sp, -8  #an alternate to the code above
sw $ra, 4($sp)
sw [int x], 0($sp)

同样,在函数调用结束时,函数将通过释放堆栈上的空间将寄存器恢复给调用者:

lw [int x], 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8

我在使用帧指针 ( $fp )方面没有很多经验,但是如果堆栈指针 ( $sp ) 在过程中更改值,则它不能再用作参考点,因此 ( $fp ) 需要它的位置($sp 只是另一个寄存器)。

(b) f 的 MIPS 汇编代码如何访问变量 x。

要访问“ int x ”,函数“ f ”可以将变量加载到临时寄存器中。

lw $t0, 0($sp)  #it can be any temporary register

由于局部变量不会在函数调用之间保留,它们可以存储在临时寄存器中。本质上,“ push ”指令将是“ sw ”(“存储字”),而“ pop ”指令将是“ lw ”(“加载字”)。

另外,我知道 MIPS 可能会很痛苦,而这份参考表确实帮助了我。

于 2012-06-14T22:44:49.473 回答
0

在此处查看 MIPS 调用约定。通常,函数中的局部变量将保存在临时(调用者保存)寄存器 $t0 - $t9 中。如果函数本身调用一个函数,则局部变量会保存在堆栈中。

MIPS中没有pushorpop指令,因此函数序言将堆栈指针递减足够的字数以允许该函数的所有堆栈存储需求,而函数尾声则撤消了这一点。

于 2012-06-14T20:47:56.983 回答
0

f 的函数 prologue 和 epilogue 对寄存器 $sp、$ra 和 $fp 做了什么,假设 $ra 和 $fp 是函数修改的唯一被调用者保存的寄存器。

$sp- 堆栈指针 $fp- 帧指针 $ra- 返回地址

函数序言和尾声是指您在函数开始时将返回地址寄存器(以及任何其他需要的寄存器)保存到堆栈中,并在函数结束时将值返回到它们各自的寄存器。

您可能已经注意到,MIPS 架构没有堆栈,您必须创建它(您可能需要查看您正在使用的模拟器或特定处理器的手册,因为为此召开的地址会有所不同)。

您将在主程序中保存 $sp 中指定的地址:

daddi $sp, $sp, 0x400 # this is the convention address for WinMIPS64's stack 

您可能出于多种原因想要使用堆栈,例如:

  • $ra在嵌套函数的情况下保存返回地址寄存器( )
  • 对于$fp, 根据问题
  • $s0-$s7如果您在函数中使用它们,则保存寄存器。按照惯例,这些应该在使用时始终保存到堆栈中(在这种情况下不适用,但其他人可能正在阅读这个,所以)。

为此,您将使用上述用户所做的代码(代码可能会根据您使用的 MIPS 版本略有不同,请查看手册)。

要推送到堆栈,您必须使用上述代码模拟堆栈函数(为数据腾出空间,将数据保存在堆栈上)。请记住,当您按上述方式 PUSH 到堆栈时,您还必须 POP 等量以将其返回$sp到其原始位置。您以相反的顺序执行此操作(再次,如上面的用户所示)。

另外:因为 MIPS 中函数的调用指令(jal f - 跳转和链接)会自动将返回地址保存在单个寄存器中($ra- 返回地址,还记得吗?),如果您使用递归或者如果您正在从第一个函数调用第二个函数,否则你会丢失主程序的返回地址,因为它会被下一次调用“踩到”。

$fp帧指针,例如,如果您已将许多寄存器值保存到堆栈中,并且您希望访问不同的操作数而不移动$sp仍指向堆栈顶部的操作数,则将使用帧指针。为此,您将加载 on 的值$sp$fp然后添加位移值以四处移动。

它允许您从堆栈加载值,同时将它们保持$sp在同一个位置,因此允许您跟踪推送和弹出操作。

f 的 MIPS 汇编代码如何访问变量 x。

按照惯例,MIPS 具有用于参数和返回值的特定寄存器。在 MIPS 中,要传递给函数的参数保存在寄存器中$a0-$a3,返回值保存在$v0-$v1. 可以在函数中使用的临时寄存器是,$t0-$t9但它们在从函数返回时不保留它们的值。但是,由于您可能会用完寄存器,因此您可能需要使用堆栈。按照惯例,$s0-$s7保存的寄存器的值通过使用堆栈来保存。

PS重要提示:我使用的是 WinMIPS64 模拟器。MIPS 汇编语言在寄存器大小、指令集和堆栈地址方面有所不同,请参考您的特定版本手册并检查哪些值适用于您的情况,因为这会影响您的代码。

于 2019-11-10T15:32:12.547 回答