汇编(MIPS)正确使用:寄存器与堆栈



在编写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。

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

首先,您通常希望使用$zero而不是$0, $v0–$v1而不是$2–$3,等等…它们比普通数字更容易理解(和记忆),汇编程序也理解这种符号,所以这边没有问题。这也将你的代码从数字中抽象出来,所以如果标准发生了变化(例如$0不再是$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

还不能评论,所以我正在"回复"一个评论。

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

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

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

最新更新