为什么 $ra 在 RISC-V 中被 Caller Saved
Why $ra is Caller Saved in RISC-V
我发现在RISC-V中,ra
是caller saved,在MIPS中,ra
是callee,也就是说在RISC-V中callee可以直接改变[=10=中的值] 没有保存,但由于 ra
已更改,被调用者如何 return 返回给调用者?
I find that in RISC-V, ra
is caller saved
ra
是一个调用者保存的寄存器这一事实意味着当控制流 return 到它时,调用者不能假设它的值被保留。因此,如果调用者想要保存ra
,它必须在将控制权转移给被调用者之前保存ra
。
可以通过jal
和jalr
将控制转移到子程序。它们都将后续指令的地址——return地址——加载到目标寄存器(通常是ra
)。所以,在一般情况下:
ra
在调用子程序时被破坏。
ra
包含返回到当前子例程调用者的 return 地址。
第一点暗示寄存器 ra
不会在调用之间保留。所以,如果一个子程序想要维护 ra
——它的调用者子程序的 return 地址——当调用一个子程序时,它必须在执行调用之前保存 ra
。
in RISC-V callee can directly change the value in ra
without save, but since ra
has changed, how callee return back to caller?
如果被调用者丢失了给调用者的return地址,就没有办法给调用者return。这就是为什么 ra
必须在调用之前保存的原因,因为它在执行调用时会被破坏。
无论指定如何,RISC V ra 和 MIPS $ra 的用法实际上是相同的。
由于调用者(需要 return 到他们的调用者)和(非叶)被调用者都需要重新调整 return 地址寄存器的用途,因此需要保留该寄存器中的值.这样做的唯一合乎逻辑的方法是在进入时保留一次寄存器并在退出时恢复一次,就像 s/$s 保留的,被调用者保存寄存器一样。
但是,一旦这样保存,return 地址寄存器可能会被函数重新用于其他用途,并且任何此类用法都将遵循调用者保存约定(不像 $s 寄存器,它保证在整个一个电话)。
因此,实际上,ra/$ra 可以在不同时间表现为被调用方保存和调用方保存。
调用者不能依赖于函数调用后存入 ra/$ra 的值(因为它们可以使用 $s 寄存器),因此调用者保存。然而,当被调用者保存 ra/$ra 时,它会像保存 $s 被调用者保存寄存器一样保存它——即在 prologue/epilogue.
中
相比之下,$t 寄存器,如果由调用者保留以便在函数调用中存活,则必须在每次更新值后保留(例如,至少在第一次初始化之后),这是调用者保存行为。这些寄存器是先初始化后保存,而$s寄存器是先保存后初始化。
ra/$ra 具有被调用者和调用者保存的行为:它需要在初始化之前被保存,reused/repurposed,这是一个被调用者保存方法,然而,一个变量放在 $ra 中不会在函数调用中存活,因此在函数调用中存活,需要先初始化然后保存。
我发现在RISC-V中,ra
是caller saved,在MIPS中,ra
是callee,也就是说在RISC-V中callee可以直接改变[=10=中的值] 没有保存,但由于 ra
已更改,被调用者如何 return 返回给调用者?
I find that in RISC-V,
ra
is caller saved
ra
是一个调用者保存的寄存器这一事实意味着当控制流 return 到它时,调用者不能假设它的值被保留。因此,如果调用者想要保存ra
,它必须在将控制权转移给被调用者之前保存ra
。
可以通过jal
和jalr
将控制转移到子程序。它们都将后续指令的地址——return地址——加载到目标寄存器(通常是ra
)。所以,在一般情况下:
ra
在调用子程序时被破坏。ra
包含返回到当前子例程调用者的 return 地址。
第一点暗示寄存器 ra
不会在调用之间保留。所以,如果一个子程序想要维护 ra
——它的调用者子程序的 return 地址——当调用一个子程序时,它必须在执行调用之前保存 ra
。
in RISC-V callee can directly change the value in
ra
without save, but sincera
has changed, how callee return back to caller?
如果被调用者丢失了给调用者的return地址,就没有办法给调用者return。这就是为什么 ra
必须在调用之前保存的原因,因为它在执行调用时会被破坏。
无论指定如何,RISC V ra 和 MIPS $ra 的用法实际上是相同的。
由于调用者(需要 return 到他们的调用者)和(非叶)被调用者都需要重新调整 return 地址寄存器的用途,因此需要保留该寄存器中的值.这样做的唯一合乎逻辑的方法是在进入时保留一次寄存器并在退出时恢复一次,就像 s/$s 保留的,被调用者保存寄存器一样。
但是,一旦这样保存,return 地址寄存器可能会被函数重新用于其他用途,并且任何此类用法都将遵循调用者保存约定(不像 $s 寄存器,它保证在整个一个电话)。
因此,实际上,ra/$ra 可以在不同时间表现为被调用方保存和调用方保存。
调用者不能依赖于函数调用后存入 ra/$ra 的值(因为它们可以使用 $s 寄存器),因此调用者保存。然而,当被调用者保存 ra/$ra 时,它会像保存 $s 被调用者保存寄存器一样保存它——即在 prologue/epilogue.
中相比之下,$t 寄存器,如果由调用者保留以便在函数调用中存活,则必须在每次更新值后保留(例如,至少在第一次初始化之后),这是调用者保存行为。这些寄存器是先初始化后保存,而$s寄存器是先保存后初始化。
ra/$ra 具有被调用者和调用者保存的行为:它需要在初始化之前被保存,reused/repurposed,这是一个被调用者保存方法,然而,一个变量放在 $ra 中不会在函数调用中存活,因此在函数调用中存活,需要先初始化然后保存。