$ra register callee saved 还是 caller saved in mips?
Whether $ra register callee saved or caller saved in mips?
我读到保留寄存器是调用者保存的,非保留寄存器是被调用者保存的。但在我看来,$ra 是一个保留的寄存器,它是调用者保存的,因为调用者将它必须保存到的地址 return。任何人都可以解释我错过了什么吗?
当您使用指令 jal
或 jalr
调用子例程时,return 地址存储在 $ra
中,因此如果您已经在子例程中,您将丢失该值,因此当 return 使用 ret
指令时,您可能有一个 Segmentation fault
。所以在调用一个子程序之前(或者更一般地说,在使用 jalr
或 jal
之前),你应该保存 $ra
寄存器
I've read that preserved registers are caller saved and non preserved registers are callee saved.
这可能不是陈述事物的最佳方式,可能是混淆的根源。这是一个更好的方法:
A function (i.e. callee) must preserve $s0-$s7
, the global pointer $gp
, the stack pointer $sp
, and the frame pointer $fp
所有其他寄存器都可以由函数根据需要更改。
例如,当 fncA
调用 fncB
时,它会:
jal fncB
return地址放在[hardwired]寄存器中$ra
最后,通常,fncB
returns 通过 jr $ra
.
但是,fncB
可以在 jr
指令中使用 any 寄存器,因此它可以:
move $v0,$ra
li $ra,0
jr $v0
由callee为caller保留$ra
真的没有任何意义。 $ra
是被调用函数 [通常] 找到 return 地址的地方,但如果需要,它可以移动它。
在 fncA
中,它可以做:
jal fncB
jal fncB
$ra
值在两种情况下都会不同,因此为了调用者的利益而谈论保留 $ra
是没有意义的 [因为有 none ].
But it seems to me that $ra, a preserved register
保留? 谁?调用者不需要该值 [也不关心它会发生什么,只要被调用者 return 到达正确的位置即可]。被调用的函数 而不是 必须为调用者保留 $ra
。它必须为自己保留 return 地址 [但不一定在 $ra
] 中。
因此,认为 $ra
由调用者 或 被调用者
保存可能是不正确的
... is caller saved as the caller saves the address to which it have to return.
当调用者[通过jal
] 设置 $ra
中的return地址时,它确实不是保存它是在[堆栈上]保存寄存器的意义上。
如果 fncB
调用另一个函数 fncC
它通常会保留 $ra
并且通常将其保存在堆栈中。但是,它可以根据需要以其他方式保留寄存器 内容。
此外,可以使用 jalr
指令代替 jal
[并且 是 用于非常大的地址跨度]。所以,fncA
可以做:
la $t0,fncB
jalr $t0
但是,这实际上只是一个 shorthand 用于:
la $t0,fncB
jalr $ra,$t0
但是,如果 fncB
知道它是如何被调用的(即我们以不同的方式编写函数),我们可以使用不同的寄存器来保存 return 地址:
la $t0,fncB
jalr $t3,$t0
此处 $t3
将保存 return 地址。这是一个非标准调用约定(即不符合 ABI)。
我们可能有一个完全符合 ABI 的函数 fncD
。但是,它可能会调用几个其他函数不会调用的内部函数(例如 fncD1, fncD2, ...
)。 fncD
可以自由地使用它选择的任何非标准调用约定来调用这些函数。
例如,函数参数可能使用 $t0-$t6
而不是 $a0-$a3
。如果 fncD
在外边缘保留 $s0-s7
,这些可用于 fncD1
.
的函数参数
只有个绝对硬连线的寄存器是$zero
和$ra
。对于 $ra
这只是 因为它在 jal
指令中是 hardwired/implicit。如果我们只使用 jalr
,我们可以将 $ra
释放为像 $t0
.
这样的普通寄存器
其余寄存器不是由CPU体系结构规定的,而仅仅是ABI约定。
如果我们用 100% 的汇编程序编写程序,编写我们自己的所有函数,我们可以使用我们希望的 任何 约定。例如,我们可以使用 $t0
作为我们的堆栈指针寄存器而不是 $sp
。那是因为 mips 架构有 no push/pop 指令,其中 $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
这个程序的输出是:
fncjal1
fncjal2
fncjalx
fncjal1
fncjal2
fncjalr1
fncjalr1
fncjalr2
exit
我读到保留寄存器是调用者保存的,非保留寄存器是被调用者保存的。但在我看来,$ra 是一个保留的寄存器,它是调用者保存的,因为调用者将它必须保存到的地址 return。任何人都可以解释我错过了什么吗?
当您使用指令 jal
或 jalr
调用子例程时,return 地址存储在 $ra
中,因此如果您已经在子例程中,您将丢失该值,因此当 return 使用 ret
指令时,您可能有一个 Segmentation fault
。所以在调用一个子程序之前(或者更一般地说,在使用 jalr
或 jal
之前),你应该保存 $ra
寄存器
I've read that preserved registers are caller saved and non preserved registers are callee saved.
这可能不是陈述事物的最佳方式,可能是混淆的根源。这是一个更好的方法:
A function (i.e. callee) must preserve
$s0-$s7
, the global pointer$gp
, the stack pointer$sp
, and the frame pointer$fp
所有其他寄存器都可以由函数根据需要更改。
例如,当 fncA
调用 fncB
时,它会:
jal fncB
return地址放在[hardwired]寄存器中$ra
最后,通常,fncB
returns 通过 jr $ra
.
但是,fncB
可以在 jr
指令中使用 any 寄存器,因此它可以:
move $v0,$ra
li $ra,0
jr $v0
由callee为caller保留$ra
真的没有任何意义。 $ra
是被调用函数 [通常] 找到 return 地址的地方,但如果需要,它可以移动它。
在 fncA
中,它可以做:
jal fncB
jal fncB
$ra
值在两种情况下都会不同,因此为了调用者的利益而谈论保留 $ra
是没有意义的 [因为有 none ].
But it seems to me that $ra, a preserved register
保留? 谁?调用者不需要该值 [也不关心它会发生什么,只要被调用者 return 到达正确的位置即可]。被调用的函数 而不是 必须为调用者保留 $ra
。它必须为自己保留 return 地址 [但不一定在 $ra
] 中。
因此,认为 $ra
由调用者 或 被调用者
... is caller saved as the caller saves the address to which it have to return.
当调用者[通过jal
] 设置 $ra
中的return地址时,它确实不是保存它是在[堆栈上]保存寄存器的意义上。
如果 fncB
调用另一个函数 fncC
它通常会保留 $ra
并且通常将其保存在堆栈中。但是,它可以根据需要以其他方式保留寄存器 内容。
此外,可以使用 jalr
指令代替 jal
[并且 是 用于非常大的地址跨度]。所以,fncA
可以做:
la $t0,fncB
jalr $t0
但是,这实际上只是一个 shorthand 用于:
la $t0,fncB
jalr $ra,$t0
但是,如果 fncB
知道它是如何被调用的(即我们以不同的方式编写函数),我们可以使用不同的寄存器来保存 return 地址:
la $t0,fncB
jalr $t3,$t0
此处 $t3
将保存 return 地址。这是一个非标准调用约定(即不符合 ABI)。
我们可能有一个完全符合 ABI 的函数 fncD
。但是,它可能会调用几个其他函数不会调用的内部函数(例如 fncD1, fncD2, ...
)。 fncD
可以自由地使用它选择的任何非标准调用约定来调用这些函数。
例如,函数参数可能使用 $t0-$t6
而不是 $a0-$a3
。如果 fncD
在外边缘保留 $s0-s7
,这些可用于 fncD1
.
只有个绝对硬连线的寄存器是$zero
和$ra
。对于 $ra
这只是 因为它在 jal
指令中是 hardwired/implicit。如果我们只使用 jalr
,我们可以将 $ra
释放为像 $t0
.
其余寄存器不是由CPU体系结构规定的,而仅仅是ABI约定。
如果我们用 100% 的汇编程序编写程序,编写我们自己的所有函数,我们可以使用我们希望的 任何 约定。例如,我们可以使用 $t0
作为我们的堆栈指针寄存器而不是 $sp
。那是因为 mips 架构有 no push/pop 指令,其中 $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
这个程序的输出是:
fncjal1
fncjal2
fncjalx
fncjal1
fncjal2
fncjalr1
fncjalr1
fncjalr2
exit