我正在参加大会的介绍课程,我遇到了以下代码。我不确定堆栈是如何工作的。这是应该被转换成汇编的C代码:(抱歉,我忘了提到我们正在使用MIPS)
int leaf_example (int g, h, i, j) {
int f;
f = (g + h) - (i + j);
return f;
}
在课堂上,教授说正确的翻译应该是:(我在评论中添加了我的问题)
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寄存器的内容不能保证保持不变。但是这个事实如何将寄存器推入堆栈呢?非常感谢你的帮助!
答案 0 :(得分:2)
没有保证,只有惯例。你可以写一个破坏$ s寄存器或做其他坏事的函数。惯例是preserve $s0-$s7, $gp, $sp and $fp。如果不这样做,很可能会导致调用链中的某个功能以某种方式失败(如果被评分,则会扣除积分)。
因此,如果你要使用$ s寄存器,你必须以某种方式备份它们,并在返回之前恢复它们。
这就是代码正在做的事情,虽然在我看来称“ 正确的翻译”是误导性的。这是一种方式。但它也保留了$ t寄存器,而不需要。它还可以在$ t寄存器中节省$ s0。它甚至可以首先不使用$ 0 ,这甚至更简单。它只将结果输入$ s0 立即将移动到$ v0,它可以直接将它放在那里然后一切都变得容易:
leaf_example:
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $v0, $t0, $t1
jr $ra
不再犹豫不决。
但也许目标是展示叶子功能如何发挥作用。或者您可能必须严格遵循某些特定的转换规则,例如,始终专门为本地变量分配寄存器,并始终为return x
语句生成$ v0的移动。
答案 1 :(得分:1)
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寄存器。所以它可能不需要首先保存它。