【问题标题】:MIPS function inside a function函数内部的 MIPS 函数
【发布时间】:2011-01-18 21:32:30
【问题描述】:

我正在尝试让函数 vbsme 调用另一个名为 sad 的函数...以下关于保存寄存器和返回地址的过程是否正确?调用者应该保存寄存器 $t0-$t7,但我应该在哪里以及如何做呢?

vbsme: li $v0, 0   # reset $v0 
     li $v1, 0   # reset $v1
     li  $t0, 1   # i(row) = 1 
     li  $t1, 1   # j(col) = 1
     lw  $t2, 0($a0)  # row size
     lw  $t3, 4($a0)  # col size
     mul  $t4, $t2, $t3  # row * col
     li  $t5, 0   # element = 0
    loop:   bgeq  $t5, $t4, exit  # if element >= row * col then exit

     addi $sp, $sp, -16  # create space on the stack pointer
     sw $ra, -12($sp)  # save return address
     sw $s6, -8($sp)  # save return address
     sw $s7, -4($sp)  # save return address
     subi $s7, $t0, 1  # 1st parameter: i-1
     subi $s6, $t1, 1  # 2nd parameter: j-1
     jal  sad   # calculate the sum of absolute difference using the frame starting from row a0 and col a1
     lw $ra, -12($sp)  # restore return address
     lw $s6, -8($sp)  
     lw $s7, -4($sp)
     addi $sp, $sp, 16  # restore stack pointer
     jr $ra

【问题讨论】:

  • 我已经重写了我的答案,$s0..$s7 是调用者保存的寄存器

标签: assembly stack mips


【解决方案1】:

$sx 寄存器保证在函数调用中保持不变,因此它的被调用者(sum 函数)负责保存它们,只有当它要改变它们的值时。

另一方面,$tx 寄存器不能保证在函数调用中保持不变,因此调用者 (vbsme) 有责任保存它们。

您应该将 $sx 保存在被调用者堆栈中。

所以当你开始编写 sum 函数时,你应该节省堆栈空间 如果要保存 n 个寄存器,则保存 n*4。

堆栈中的空间通过减去指向堆栈底部的 $sp 寄存器来节省。在您的函数代码之前,您应该为该函数创建堆栈,在必要时保存所有调用者保存的寄存器、返回地址和全局指针寄存器

sum:
       #stack frame creation. Caller registers saved, 
       # return address and frame pointer

       subu $sp,$sp,36 #Save space in the stack for registers $s0, $s7 + $ra
       sw   $ra,32($sp)
       sw   $s0,0($sp)
       sw   $s1,4($sp)
       #and so on. Note that also you should save the $ra register only if you are
       # going to call another function

       #do something with $sx  

       #stack frame destruction
       #restore $sx and $ra registers
       lw  $ra,32($sp)
       lw  $s0,0($sp)
       lw  $s1,4($sp)
       ...
       lw  $s7,28($sp)

        jr $ra

顺便说一句,按照惯例,寄存器 $a0, $a3 应该保留您正在调用的函数的参数。另外,请注意,因为您使用的是 $s0、$s7 寄存器,所以您必须做一些额外的工作。约定说如果你不使用它们,那么你不应该保存它们,所以也许你可以使用 $tx(临时)寄存器来代替。

【讨论】:

  • 调用者不应该保存寄存器 $t0-$t7 而不是 $s 寄存器吗?
  • 如果求和函数vbsme之后需要做更多的指令怎么办?
  • 所以如果我将 $sx 保存在 sum 函数中,那么我也应该在 vbsme 中使用 s 寄存器?如何保存 $tx 寄存器?
  • 只有在 sum 函数中使用 $sx 寄存器时,才应该将它们保存在 sum 函数中。如果 sum 函数不会保存它们,则不需要保存。要保存 $tx 寄存器(vbsme 应该这样做),您应该在堆栈中节省空间,并将它们存储在 vbsme 堆栈中,如 sum 函数中的 $sx 寄存器所示
【解决方案2】:

亚历山大,

Tom 所说的非常正确,在汇编中进行编程需要注意的重要一点是一切都是按照惯例。虽然在 MIPS 中,通用约定是 Tom 指出的,但它并不是唯一的约定。例如,如果您正在使用宏(如果您要在汇编中编写超过 1 或 2 个函数,那么使用宏要容易得多),那么您可以在宏中定义您的调用约定。最简单的方法是让调用者保存堆栈,而不是让被调用者保存堆栈。这效率较低,因为有时(可能很多次)未使用的寄存器会被保存,但它会为您节省很多心痛,因为您的约定是一致应用的。

调用栈保存:

    sw $fp 0($sp) # save the old frame pointer
    addu $fp $sp $0 # move the frame pointer to point at top of frame
    subu $sp $sp 44 # move the stack pointer down 44
    sw $fp 40($sp) # save the old stack pointer
    sw $ra 36($sp) # save the return address
    sw $s0 32($sp) # save registers $s0 - $s7
    sw $s1 28($sp)
    sw $s2 24($sp)
    sw $s3 20($sp)
    sw $s4 16($sp)
    sw $s5 12($sp)
    sw $s6 8($sp)
    sw $s7 4($sp)

函数调用

    jal my_func

调用堆栈恢复

    subu $sp $fp 44 # move the stack pointer to the orginal unmodified bottom
    lw $ra 36($sp) # load the return address
    lw $s0 32($sp) # load registers $s0 - $s7
    lw $s1 28($sp)
    lw $s2 24($sp)
    lw $s3 20($sp)
    lw $s4 16($sp)
    lw $s5 12($sp)
    lw $s6 8($sp)
    lw $s7 4($sp)
    lw $fp 44($sp) # load the old frame pointer
    lw $sp 40($sp) # load the old stack pointer

你的功能:

my_func:
    do some stuff
    jr $ra             # return to the previous function

正如我所说,应用此约定的最佳方式是使用宏。我在 SPIM 中做了一个项目(你可能正在使用,也可能没有)为一门课。作为项目的一部分,我们为 SPIM 编写了一个宏引擎(它也有其他很酷的东西),你可以在这里得到它: http://github.com/timtadh/mpp 我还建议查看http://github.com/timtadh/jist,这是一个基于 SPIM 编写的玩具操作系统。它将让您了解如何使用宏引擎。

干杯

【讨论】:

    猜你喜欢
    • 2014-06-22
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 2012-06-17
    • 2017-12-20
    • 2011-11-11
    • 2013-04-08
    • 2015-11-07
    相关资源
    最近更新 更多