3

在编写 MIPS 程序时,我听说保持寄存器清洁通常是一种很好的做法,即在程序结束时将寄存器的值清除为 0。所以我的第一个问题是如何/为什么这是必要的/良好的做法?

其次,如果我们有一个函数调用,其参数(p1, p2) 存储在$4 和$5 中;在我们的函数定义结束时,是否有必要清除 $4 和 $5 的值或更好地保留它们在函数调用开始时的状态?

我还看到了将参数推送到堆栈的示例,如下所示:

addi $29, $29, -8
sw $4, 0($29)
sw $5, 4($29)
; At the end of our program:
addi $29, $29, 8

何时以及为什么这是必要的/良好的做法?

最后,如果我们要在程序中使用一些常量,比如 4 和 1,将它们保存在寄存器中还是堆栈上更好?例如:

lis $8
.word 4
lis $9
.word 1

然后我们可能会以某种方式将这些值用于我们的程序,然后将它们清除为 0。

或者我们可以通过来回移动堆栈指针来选择将它们存储在堆栈中。哪种方法更好?

4

2 回答 2

3

首先,您通常想使用 $zero而不是$0$v0–$v1而不是$2–$3等......它们比普通数字更容易理解(和记忆),并且汇编程序也理解这种表示法,所以这方面没有问题。这也将您的代码从数字中抽象出来,因此如果标准发生变化(例如,$zero 不再是 $0 而是 $256),您的代码仍然可以正确组装。

在每个 CPU 架构中,关于如何使用寄存器进行调用以及如何在函数中使用寄存器,都有一些约定。这称为调用约定。您可以在此处查看 MIPS 调用约定的简要说明。

我听说保持寄存器清洁通常是一种很好的做法,即在程序结束时将寄存器的值清除为 0。所以我的第一个问题是如何/为什么这是必要的/良好的做法?

我个人从未听说过这种做法,但我不太同意。更好的做法是在程序开始之前恢复以前的值。

其次,如果我们有一个函数调用,其参数(p1, p2) 存储在$4 和$5 中;在我们的函数定义结束时,是否有必要清除 $4 和 $5 的值或更好地保留它们在函数调用开始时的状态?

您应该将它们保留在函数调用开始时的状态。对于调用者来说,你修改这个寄存器是没有意义的。

堆栈上的空间是为 $a0-$a3 保留的,以防被调用者需要保存其参数,但调用者不会将寄存器存储在那里。

我还看到了像这样将参数推送到堆栈的示例。何时以及为什么这是必要的/良好的做法?

如果根据定义,堆栈是临时存储。如果你想备份寄存器值(例如你想使用 $aX 或 $sX),你把它们放在那里。和以前一样:

堆栈上的空间是为 $a0-$a3 保留的,以防被调用者需要保存其参数

addi $sp, $sp, -8

移动堆栈指针寄存器(按照惯例,它是 29 美元)。这在堆栈中保留了 8 个字节。

sw $a0, 0($sp)
sw $a1, 4($sp)

将 $a0 保存到堆栈顶部,将 $a1 保存为堆栈中的第二个(请记住,堆栈向较低地址增长)。这将填充保留空间(8 个字节)。这称为函数入口协议

; At the end of our program:
; You forgot, and it's important:
lw $a0, 0($sp)
lw $a1, 4($sp)
addi $sp, $sp, 8

您恢复保存的寄存器并将堆栈指针放回原始值。然后调用者将保持其堆栈和参数不变。这被称为函数出口协议

如果我们要在程序中使用一些常量,比如 4 和 1,将它们保存在寄存器中还是堆栈上更好?

两者都不。常量应该用作直接操作数:

li $t0, C   ; Load 16-bit constant into $t0
lui $t0, C  ; Load upper 16-bit half-word of $t0
于 2013-03-02T03:31:25.097 回答
1

还不能发表评论,所以我正在“回答”评论。

在上述答案中提到的约定中,有一些寄存器必须保存在堆栈中 - 在过程中使用时“应该”保存 $s0-$s7 寄存器。这意味着将值保存到堆栈中,如果您发现自己需要使用它们。当输出 ($v0, $v1) 和输入 ($a0-$a3) 寄存器不足以满足您的程序的目的时,通常会发生这种情况。

$ra(返回地址寄存器)在使用过程时也应该保存到堆栈中,如果您正在使用调用另一个过程的过程或者您的代码将中断,则需要保存。

如果您需要在过程中使用临时寄存器,可以使用 $t0-$t7 而不保留它们的值,正如它们的名称(临时)所暗示的那样。这在循环计数器或其他非保留值的情况下通常很有用。

于 2019-11-16T20:50:08.917 回答