$ra 注册被调用者保存还是调用者保存在 mips 中? [英] Whether $ra register callee saved or caller saved in mips?

查看:30
本文介绍了$ra 注册被调用者保存还是调用者保存在 mips 中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读过保留的寄存器是调用者保存的,非保留的寄存器是被调用者保存的.但在我看来,$ra,一个保留的寄存器,是调用者保存的,因为调用者保存了它必须返回的地址.任何人都可以解释我缺少什么吗?

I've read that preserved registers are caller saved and non preserved registers are callee saved. But it seems to me that $ra, a preserved register, is caller saved as the caller saves the address to which it have to return. Can any one explain what I am missing?

推荐答案

我读过保留的寄存器是调用者保存的,未保留的寄存器是被调用者保存的.

I've read that preserved registers are caller saved and non preserved registers are callee saved.

这可能不是最好的陈述方式,而且可能是造成混乱的根源.这是一个更好的方法:

That may not be the best way to state things and may be the source of the confusion. Here is a better way:

一个函数(即被调用者)必须保留 $s0-$s7全局指针 $gp栈指针 $sp和帧指针 $fp

所有其他寄存器都可以由它认为合适的函数更改.

All other registers may be changed by a function as it sees fit.

例如,当 fncA 调用 fncB 时,它会:

For example, when fncA calls fncB, it does:

jal    fncB

返回地址放在[hardwired]寄存器$ra

The return address is placed in the [hardwired] register $ra

最后,通常fncB通过jr $ra返回.

但是,fncB 可以在 jr 指令中使用 any 寄存器,所以它可以:

But, fncB may use any register in the jr instruction, so it could do:

move $v0,$ra
li   $ra,0
jr   $v0

调用者保留被调用者$ra真的没有意义.$ra 是被调用函数 [通常] 找到返回地址的地方,但如果愿意,它可以移动它.

Preserving $ra by callee for caller really has no meaning. $ra is where the called function will [normally] find the return address, but it can move it around, if it wishes.

fncA中,它可以:

jal   fncB
jal   fncB

$ra 值在这两种情况下都会不同,所以为了调用者的利益而谈论保留 $ra 是没有意义的[因为有 none].

The $ra value will be different in both cases, so it makes no sense to talk of preserving $ra for caller's benefit [as there is none].

但在我看来 $ra,一个保留的寄存器

But it seems to me that $ra, a preserved register

保存??调用者不需要这个值[也不关心它会发生什么,只要被调用者返回到正确的地方].被调用的函数不必为调用者保留$ra.它必须为自己保留返回地址 [但不一定在 $ra] 中.

Preserved? By whom? Caller doesn't need the value [nor care about what happens to it as long as callee returns to the correct place]. A called function does not have to preserve $ra for caller. It has to preserve the return address [but, not necessarily in $ra] for itself.

因此,将 $ra 视为由调用方 被调用方

Thus, it's probably incorrect to think of $ra as preserved by caller or callee

... 是调用者保存的,因为调用者保存了它必须返回的地址.

... is caller saved as the caller saves the address to which it have to return.

当调用者[via jal] $ra中设置返回地址时,它真的没有节省 在保存寄存器 [在堆栈上] 的意义上.

When caller [via jal] sets the return address in $ra, it really isn't saving it in the sense of saving registers [on the stack].

如果fncB 调用另一个函数fncC,它通常会保留$ra 并通常将其保存在堆栈中.但是,如果需要,它可以以其他方式保存寄存器内容.

If fncB calls another function fncC it usually preserves $ra and it usually saves it on the stack. But, it can preserve the register contents in other ways if it desires.

此外,可以使用 jalr 指令代替 jal [并且 is 用于非常大的地址跨度].所以,fncA 可以:

Also, the jalr instruction could be used instead of jal [and is for very large address spans]. So, fncA could do:

la    $t0,fncB
jalr  $t0

但是,这实际上只是以下内容的简写:

But, this is really just a shorthand for:

la    $t0,fncB
jalr  $ra,$t0

但是,如果 fncB 知道它是如何被调用的(即我们以不同的方式编写函数),我们可以使用不同的寄存器来保存返回地址:

But, if fncB is aware of how it's being called (i.e. we write the function differently), we could use a different register to hold the return address:

la    $t0,fncB
jalr  $t3,$t0

这里 $t3 将保存返回地址.这是一个非标准的调用约定(即不符合 ABI).

Here $t3 will hold the return address. This is a non-standard calling convention (i.e. not ABI conforming).

我们可能有一个完全符合 ABI 的函数 fncD.但是,它可能会调用其他函数不会调用的几个内部函数(例如 fncD1、fncD2、...).fncD 可以自由地使用它选择的任何非标准调用约定来调用这些函数.

We might have a function fncD that fully conforms to the ABI. But, it might call several internal functions that no other function will call (e.g. fncD1, fncD2, ...). fncD is at liberty to call these functions with whatever non-standard calling conventions it chooses.

例如,它可以使用 $t0-$t6 作为函数参数而不是 $a0-$a3.如果 fncD 在外边缘保留 $s0-s7,这些可以用作 fncD1 的函数参数.

For example, it may use $t0-$t6 for function arguments instead of $a0-$a3. If fncD preserves $s0-s7 at the outer edge, these could be used for function arguments to fncD1.

唯一 绝对硬连线的寄存器是$zero$ra.对于$ra,这是唯一,因为它是硬连线/隐含在jal 指令中.如果我们只使用jalr,我们可以像$t0一样将$ra释放为一个普通的寄存器.

The only registers that are absolutely hardwired are $zero and $ra. For $ra this is only because it is hardwired/implicit in the jal instruction. If we only used jalr, we could free up $ra as an ordinary register like $t0.

其余的寄存器由 CPU 架构决定,而仅由 ABI 约定决定.

The rest of the registers are not dictated by the CPU architecture, but merely the ABI convention.

如果我们用 100% 汇编程序编写程序,编写我们自己的所有函数,我们可以使用任何我们希望的约定.例如,我们可以使用 $t0 作为我们的堆栈指针寄存器而不是 $sp.那是因为 mips 架构没有 push/pop 指令,其中 $sp 寄存器是隐式的.它只有 lw/sw,我们可以使用任何我们想要的寄存器.

If we wrote a program in 100% assembler, wrote all our own functions, we could use any convention we wished. For example, we could use $t0 as our stack pointer register instead of $sp. That's because the mips architecture has no push/pop instructions where the $sp register is implicit. It only has lw/sw and we can use whatever register we want.

下面的程序演示了您可以执行的一些标准和非标准操作:

Here is a program that demonstrates some of the standard and non-standard things you can do:

    .data
msg_jal1:   .asciiz     "fncjal1\n"
msg_jal2:   .asciiz     "fncjal2\n"
msg_jalx:   .asciiz     "fncjalx\n"
msg_jaly:   .asciiz     "fncjaly\n"
msg_jalz:   .asciiz     "fncjalz\n"
msg_jalr1:  .asciiz     "fncjalr1\n"
msg_jalr2:  .asciiz     "fncjalr2\n"
msg_post:   .asciiz     "exit\n"

    .text
    .globl  main
main:
    # for the jal instruction, the return address register is hardwired to $ra
    jal     fncjal1

    # but, once called, a function may destroy it at will
    jal     fncjal2

    # double level call
    jal     fncjalx

    # jalr takes two registers -- this is just a shorthand for ...
    la      $t0,fncjalr1
    jalr    $t0

    # ... this
    la      $t0,fncjalr1
    jalr    $ra,$t0

    # we may use any return address register we want (subject to our ABI rules)
    la      $t0,fncjalr2
    jalr    $t3,$t0

    # show we got back alive
    li      $v0,4
    la      $a0,msg_post
    syscall

    li      $v0,10                  # syscall for exit program
    syscall

# fncja11 -- standard function
fncjal1:
    li      $v0,4
    la      $a0,msg_jal1
    syscall
    jr      $ra                     # do return

# fncja12 -- standard function that returns via different register
fncjal2:
    li      $v0,4
    la      $a0,msg_jal2
    syscall

    # grab the return address
    # we can preserve this in just about any register we wish (e.g. $a0) as
    # long as the jr instruction below matches
    move    $v0,$ra

    # zero out the standard return register
    # NOTES:
    # (1) this _is_ ABI conforming
    # (2) caller may _not_ assume $ra has been preserved
    # (3) _we_ need to preserve the return _address_ but we may do anything
    #     we wish to the return _register_
    li      $ra,0

    jr      $v0                     # do return

# fncja1x -- standard function that calls another function
fncjalx:
    # preserve return address
    addi    $sp,$sp,-4
    sw      $ra,0($sp)

    li      $v0,4
    la      $a0,msg_jalx
    syscall

    jal     fncjal1
    jal     fncjal2

    # restore return address
    lw      $ra,0($sp)
    addi    $sp,$sp,4

    jr      $ra                     # do return

# fncja1y -- standard function that calls another function with funny return
fncjaly:
    # preserve return address
    addi    $sp,$sp,-4
    sw      $ra,0($sp)

    li      $v0,4
    la      $a0,msg_jaly
    syscall

    jal     fncjal1
    jal     fncjal2

    # restore return address
    lw      $a0,0($sp)
    addi    $sp,$sp,4

    jr      $a0                     # do return

# fncjalz -- non-standard function that calls another function
fncjalz:
    move    $t7,$ra                 # preserve return address

    li      $v0,4
    la      $a0,msg_jalz
    syscall

    jal     fncjal1
    jal     fncjal2

    jr      $t7                     # do return

# fncjalr1 -- standard function [called via jalr]
fncjalr1:
    li      $v0,4
    la      $a0,msg_jalr1
    syscall
    jr      $ra                     # do return

# fncjalr2 -- non-standard function [called via jalr]
fncjalr2:
    li      $v0,4
    la      $a0,msg_jalr2
    syscall
    jr      $t3                     # do return

这个程序的输出是:

fncjal1
fncjal2
fncjalx
fncjal1
fncjal2
fncjalr1
fncjalr1
fncjalr2
exit

这篇关于$ra 注册被调用者保存还是调用者保存在 mips 中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆