【问题标题】:function call mips函数调用 mips
【发布时间】:2012-06-17 19:28:18
【问题描述】:

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

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

我有两个问题:

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

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

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

【问题讨论】:

  • 你可以在代码上运行一个 C 编译器,看看它生成了什么......

标签: c mips


【解决方案1】:

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

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

要解释如何访问“int x”,了解它的存储方式和存储位置很重要。由于 'int x' 是一个局部变量,MIPS 将通过减去字节数( 4) 用于堆栈指针的 32 位整数。调用者的返回地址也被保存(另外 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'('store word'),而 'pop' 指令将是 ' lw'('加载单词')。

另外,我知道 MIPS 可能会很痛苦,而 reference sheet 确实帮助了我。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      假设 $ra 和 $fp 是函数修改的唯一被调用者保存的寄存器,f 的函数 prologue 和 epilogue 对寄存器 $sp、$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 的情况下访问不同的操作数的堆栈。为此,您需要将$sp 的值加载到$fp 上,然后添加一个位移值来移动。

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-27
        • 2011-01-18
        • 1970-01-01
        • 2020-07-28
        • 2013-11-07
        • 1970-01-01
        • 2011-01-18
        相关资源
        最近更新 更多