$ra 注册被调用者保存还是调用者保存在 mips 中? [英] Whether $ra register callee saved or caller saved in 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屋!