函数内部的MIPS函数

时间:2010-02-19 17:42:41

标签: assembly stack mips

我试图让函数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

2 个答案:

答案 0 :(得分:2)

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

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

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

因此,当您开始对sum函数进行编码时,应该节省堆栈中的空间 如果要保存n个寄存器,则保存n * 4。

通过在$ sp寄存器上减去来保存堆栈中的空间,$ 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(临时)寄存器。

答案 1 :(得分:1)

亚历山大,

汤姆所说的非常正确,在汇编中进行编程时要注意的重要事项是一切都是按照惯例。虽然在MIPS中常见的约定是汤姆指出它不是唯一的惯例。例如,如果您正在使用宏(如果您要在汇编中编写超过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之上编写的玩具操作系统。它将让您了解如何使用宏引擎。

欢呼声