函数内的 MIPS 函数

发布于 2024-08-22 04:52:45 字数 997 浏览 2 评论 0原文

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

I am trying to have the function vbsme call another function called sad... is the following procedure correct about saving the registers and return address?? the caller is supposed to save register $t0-$t7, but where and how should I do that?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

另类 2024-08-29 04:52:45

$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(临时)寄存器来代替。

$sx registers are guaranteed to be unchanged accross function calls, so its the callee (sum function) the responsible of saving them, only if its going to change their value.

$tx registers, on the other hand, are not guaranteed to be unchanged over function calls, so its the responsability of the caller (vbsme) to save them.

You should save $sx in the callee stack.

So when you start coding the sum function, you should save space in the stack
If you want to save n registers, then save n*4.

Space in the stack is saved by subtracting on the $sp register, which points to the base of the stack. Before your function code, you should create the stack for that function, saving all caller-saved registers, return address and global pointer registers when neccesary

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

By the way, by convention, registers $a0, $a3 should keep the arguments to the function you are calling. Also, note that because you are using the $s0, $s7 registers, you have to do some extra work. Convention says that if you don't use them, then you shouldn't save them, so maybe you could use the $tx (temporary) registers instead.

千纸鹤 2024-08-29 04:52:45

亚历山大,

汤姆所说的非常正确,在汇编中进行编程时需要注意的重要一点是一切都是按照惯例。 Tom 指出,在 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 之上编写的。它将让您了解如何使用宏引擎。

干杯

Alexander,

What Tom said is pretty much correct the important thing to note with doing programming in assembly is everything is by convention. While in MIPS the common convention is what Tom noted it is not the only convention. For instance if you are using macro's (which if you are going to be writing more than 1 or 2 functions in assembly it is a much easier to use macros) then you can define your calling convention in the macro. The easiest way to do this is instead of having the callee save the stack is to have the caller save the stack. This is less efficient because sometimes (perhaps many times) unused registers will be saved, however it will save you a lot of heartache because your convention is applied consistently.

Caller Stack Save:

    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)

Function call

    jal my_func

Caller Stack Restore

    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

Your function:

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

As I said the best way to apply this convention is to use macros. I did a project in SPIM (which you may being using or you may not be) for a class. As part of the project we wrote a macro engine (it does other cool stuff as well) for SPIM you can get it here:
http://github.com/timtadh/mpp
I would also recommend checking out http://github.com/timtadh/jist which is a toy operating system written on top of SPIM. It will give you a sense of how to use the macro engine.

cheers

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文