在Assembly中的过程调用中将寄存器保存在堆栈中

时间:2015-01-27 19:54:37

标签: assembly stack mips

我正在参加大会的介绍课程,我遇到了以下代码。我不确定堆栈是如何工作的。这是应该被转换成汇编的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寄存器的内容不能保证保持不变。但是这个事实如何将寄存器推入堆栈呢?非常感谢你的帮助!

2 个答案:

答案 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寄存器。所以它可能不需要首先保存它。