Nir*_*iru 3 architecture mips cpu-registers
我读过保留的寄存器是调用者保存的,非保留的寄存器是被调用者保存的。但在我看来,$ra,一个保留的寄存器,是调用者保存的,因为调用者保存了它必须返回的地址。任何人都可以解释我缺少什么吗?
我读过保留的寄存器是调用者保存的,非保留的寄存器是被调用者保存的。
这可能不是陈述事物的最佳方式,并且可能是混淆的根源。这是一个更好的方法:
的函数(即,被叫方)必须保留
$s0-$s7,全局指针$gp,堆栈指针$sp,和帧指针$fp
所有其他寄存器都可以由它认为合适的函数更改。
例如,当fncA调用 时fncB,它会:
jal fncB
Run Code Online (Sandbox Code Playgroud)
返回地址放在[hardwired]寄存器中 $ra
最后,通常fncB通过 返回jr $ra。
但是,fncB可以在指令中使用任何寄存器jr,所以它可以:
move $v0,$ra
li $ra,0
jr $v0
Run Code Online (Sandbox Code Playgroud)
$ra被调用者为调用者保留真的没有意义。$ra是被调用函数将[通常]找到返回地址的地方,但如果愿意,它可以移动它。
在 中fncA,它可以:
jal fncB
jal fncB
Run Code Online (Sandbox Code Playgroud)
$ra两种情况下的价值都会不同,因此谈论$ra为调用者的利益而保留是没有意义的[因为没有]。
但在我看来 $ra,一个保留的寄存器
保存?通过谁?调用者不需要该值[也不关心它会发生什么,只要被调用者返回到正确的位置]。被调用函数并没有要保存$ra的调用者。它必须为自己保留返回地址[但不一定在$ra]。
因此,认为$ra由调用者或被调用者保留可能是不正确的
... 是调用者保存的,因为调用者保存了它必须返回的地址。
当调用者 [via jal] 在 中设置返回地址时$ra,它确实没有保存在保存寄存器 [在堆栈上] 的意义上它。
如果fncB调用另一个函数,fncC它通常会保留$ra并通常将其保存在堆栈中。但是,如果需要,它可以以其他方式保存寄存器内容。
此外,jalr指令可以被用来代替jal[和是非常大的地址跨度。所以,fncA可以这样做:
la $t0,fncB
jalr $t0
Run Code Online (Sandbox Code Playgroud)
但是,这实际上只是以下内容的简写:
la $t0,fncB
jalr $ra,$t0
Run Code Online (Sandbox Code Playgroud)
但是,如果fncB知道它是如何被调用的(即我们以不同的方式编写函数),我们可以使用不同的寄存器来保存返回地址:
la $t0,fncB
jalr $t3,$t0
Run Code Online (Sandbox Code Playgroud)
这里$t3将保存返回地址。这是一个非标准的调用约定(即不符合 ABI)。
我们可能有一个fncD完全符合 ABI的函数。但是,它可能会调用其他函数不会调用的几个内部函数(例如fncD1, fncD2, ...)。fncD可以自由地使用它选择的任何非标准调用约定来调用这些函数。
例如,它可以$t0-$t6用于函数参数而不是$a0-$a3. 如果fncD保留$s0-s7在外边缘,则这些可用于 的函数参数fncD1。
该只说是绝对硬寄存器$zero和$ra。因为$ra这只是因为它在jal指令中是硬连线/隐含的。如果我们只使用jalr,我们可以$ra像普通寄存器一样释放$t0。
其余的寄存器不是由 CPU 架构决定的,而只是由 ABI 约定决定的。
如果我们用 100% 汇编程序编写程序,编写我们自己的所有函数,我们可以使用我们希望的任何约定。例如,我们可以使用$t0堆栈指针寄存器而不是$sp. 那是因为 mips 架构没有推送/弹出指令,其中$sp寄存器是隐式的。它只有lw/sw并且我们可以使用我们想要的任何寄存器。
这是一个程序,演示了您可以执行的一些标准和非标准操作:
.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
Run Code Online (Sandbox Code Playgroud)
这个程序的输出是:
fncjal1
fncjal2
fncjalx
fncjal1
fncjal2
fncjalr1
fncjalr1
fncjalr2
exit
Run Code Online (Sandbox Code Playgroud)