问题
I find that in RISC-V, ra
is caller saved, in MIPS, ra
is callee, which means in RISC-V callee can directly change the value in ra
without save, but since ra
has changed, how callee return back to caller?
回答1:
The usage of RISC V ra and MIPS $ra is effectively the same regardless of the designation.
Since both caller (who needs to return to their caller) and (a non-leaf) callee need to repurpose the return address register, the value in that register needs to be preserved. The only logical way to do that is to preserve the register once on entry and restore it once on exit just like the s/$s preserved, callee-saves registers.
However, once thus saved, the return address register may be repurposed by functions for other uses and any such usage would follow caller saves conventions (unlike the $s registers, which are guaranteed to be preserved across a call).
So, effectively, ra/$ra can behave, at different times, both as callee saves, and caller saves.
A caller cannot rely on a value placed into ra/$ra surviving a function call (as they could with $s registers), thus is is caller saves. Yet, when a callee preserves ra/$ra, it preserves it just like it does the $s callee-saves registers — namely in prologue/epilogue.
By contrast, $t registers, if preserved by the caller so as to survive a function call, would have to be preserved after each update to the value (e.g. minimally after the first initialization), and this is caller saves behavior. These registers are initialized first, then preserved, whereas $s registers are preserved first, then initialized.
ra/$ra has behaviors of both callee and caller saves: it needs to be preserved before being initialized and reused/repurposed, which is a callee saves approach, yet, a variable placed into $ra would not survive a function call, and so to survive a function call, would need to be initialized then preserved.
回答2:
I find that in RISC-V,
ra
is caller saved
The fact that ra
is a caller-saved register means that the caller can't assume that its value is preserved when the control flow returns to it. Therefore, if the caller wants to preserve ra
, it has to save ra
before transferring the control to the callee.
Transferring control to subroutines can be achieved by jal
and jalr
. They both load the address of the following instruction – the return address – into the destination register (usually ra
). So, in the general case:
ra
is clobbered at the moment of calling a subroutine.ra
contains the return address to go back to the current subroutine's caller.
The first point implies that the register ra
isn't be preserved between calls. So, if a subroutine wants to maintain ra
– the return address to its caller subroutine – when calling a subroutine, it must save ra
before performing the call.
in RISC-V callee can directly change the value in
ra
without save, but sincera
has changed, how callee return back to caller?
If the callee loses the return address to its caller, there is no way to return to the caller. That's why ra
has to be saved before a call because it is clobbered when performing a call.
来源:https://stackoverflow.com/questions/59693334/why-ra-is-caller-saved-in-risc-v