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

时间:2013-03-02 01:10:16

标签: assembly stack mips cpu-registers

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

其次, 如果我们有一个函数调用,参数(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。

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

2 个答案:

答案 0 :(得分:2)

首先,you usually want to use $zero代替$0$v0–$v1而不是$2–$3等......他们更容易理解(并记住)比普通数字和汇编程序也理解这种表示法,所以这方面没有问题。此外,这会从数字中抽象出您的代码,因此,如果标准更改(例如,$ 0不再是$ 0但是$ 256),您的代码仍将正确汇编。

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

  

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

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

  

其次,如果我们有一个函数调用,其中存储了参数(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

答案 1 :(得分:1)

还无法发表评论,因此我正在“回答”评论。

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

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

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