在 Assembly 的过程调用中将寄存器保存在堆栈上
Save registers on a stack in a procedure call in Assembly
我正在学习有关汇编的入门课程,我遇到了以下代码。我不确定堆栈在这里是如何工作的。这是应该被翻译成汇编的 C 代码:(对不起,我忘了提到我们正在使用 MIPS)
int leaf_example (int g, h, i, j) {
int f;
f = (g + h) - (i + j);
return f;
}
在class中,教授说正确的翻译应该是:(我在评论中添加了我的问题)
leaf_example:
addi $sp, $sp, -12 # Why do we need to allocate 3 bytes for each register?
sw $t1, 8($sp) # I don't really understand the meaning of this line and the next two lines
sw $t0, 4($sp)
sw $s0, 0($sp)
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
lw $s0, 0($sp) # After we sw the registers on the stack, why do we need to load them back?
lw $t0, 4($sp)
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
我想我对$s寄存器和$t寄存器的区别有一个模糊的理解:在调用函数时,$s寄存器的内容保持不变,但不保证$t寄存器的内容保持不变.但是这个事实与将寄存器压入堆栈有什么关系呢?非常感谢您的帮助!
没有保证,只有约定。您可以编写一个破坏 $s 寄存器或做其他坏事的函数。惯例是preserve $s0-$s7, $gp, $sp and $fp。如果不这样做,很可能会导致调用链上的某个函数以某种方式失败(如果评分,则会扣分)。
所以如果你要使用 $s 寄存器,你必须以某种方式备份它们,并在返回之前恢复它们。
这就是代码的作用,尽管我认为称其为“ 正确的翻译”具有误导性。这是一种方式。但它也保留了 $t 寄存器,虽然它不是必须的。它还可以将 $s0 保存在 $t 寄存器中。它甚至可以首先不使用 $0,这更简单。它只将结果放在 $s0 中 立即 将它移到 $v0 无论如何,它可以直接把它放在那里然后一切都变得容易了:
leaf_example:
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $v0, $t0, $t1
jr $ra
没有更多的废话。
但也许目标是展示叶函数的一般工作原理。或者您可能必须严格遵守一些特定的翻译规则,例如始终专门为局部变量分配寄存器并始终为 return x
语句生成 $v0 的移动。
addi $sp, $sp, -12 Allocate space for 3-4byte registers
sw $t1, 8($sp) Store the t1 register 8 bytes from the current SP location
sw $t0, 4($sp) Store the t0 register 4 bytes . . . .
sw $s0, 0($sp) Store the s0 register at the end of the stack.
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
// Now put the registers back the way they were before the function was called.
// If you don't do that, the caller will find the register values have changed.
lw $t0, 4($sp) // This reverses the process above.
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
不是恢复S0寄存器。所以它可能一开始就不需要保存它。
我正在学习有关汇编的入门课程,我遇到了以下代码。我不确定堆栈在这里是如何工作的。这是应该被翻译成汇编的 C 代码:(对不起,我忘了提到我们正在使用 MIPS)
int leaf_example (int g, h, i, j) {
int f;
f = (g + h) - (i + j);
return f;
}
在class中,教授说正确的翻译应该是:(我在评论中添加了我的问题)
leaf_example:
addi $sp, $sp, -12 # Why do we need to allocate 3 bytes for each register?
sw $t1, 8($sp) # I don't really understand the meaning of this line and the next two lines
sw $t0, 4($sp)
sw $s0, 0($sp)
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
lw $s0, 0($sp) # After we sw the registers on the stack, why do we need to load them back?
lw $t0, 4($sp)
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
我想我对$s寄存器和$t寄存器的区别有一个模糊的理解:在调用函数时,$s寄存器的内容保持不变,但不保证$t寄存器的内容保持不变.但是这个事实与将寄存器压入堆栈有什么关系呢?非常感谢您的帮助!
没有保证,只有约定。您可以编写一个破坏 $s 寄存器或做其他坏事的函数。惯例是preserve $s0-$s7, $gp, $sp and $fp。如果不这样做,很可能会导致调用链上的某个函数以某种方式失败(如果评分,则会扣分)。
所以如果你要使用 $s 寄存器,你必须以某种方式备份它们,并在返回之前恢复它们。
这就是代码的作用,尽管我认为称其为“ 正确的翻译”具有误导性。这是一种方式。但它也保留了 $t 寄存器,虽然它不是必须的。它还可以将 $s0 保存在 $t 寄存器中。它甚至可以首先不使用 $0,这更简单。它只将结果放在 $s0 中 立即 将它移到 $v0 无论如何,它可以直接把它放在那里然后一切都变得容易了:
leaf_example:
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $v0, $t0, $t1
jr $ra
没有更多的废话。
但也许目标是展示叶函数的一般工作原理。或者您可能必须严格遵守一些特定的翻译规则,例如始终专门为局部变量分配寄存器并始终为 return x
语句生成 $v0 的移动。
addi $sp, $sp, -12 Allocate space for 3-4byte registers
sw $t1, 8($sp) Store the t1 register 8 bytes from the current SP location
sw $t0, 4($sp) Store the t0 register 4 bytes . . . .
sw $s0, 0($sp) Store the s0 register at the end of the stack.
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
add $v0, $s0, $zero
// Now put the registers back the way they were before the function was called.
// If you don't do that, the caller will find the register values have changed.
lw $t0, 4($sp) // This reverses the process above.
lw $t1, 8($sp)
addi $sp, $sp, 12
jr $ra
不是恢复S0寄存器。所以它可能一开始就不需要保存它。