Terms
Caller: the function making the call. Callee: the function that is being called.
callee-saved registers: The registers that a function promises to leave unchanged. Including s0-s11 (saved registers) , sp(stack pointer).
caller-saved registers: The registers that a function does not promise to leave unchanged. Including a0-a7(argument registers) , t0-t6 (temporary registers) ,ra(return address).
The callers perspective
For callee-saved register, it’s appering to be unchanged.
For caller-saved register, such a guarantee does not exist:
This is a common calling convention bug: when a function returns, you cannot rely on the values in any non-preserved register.
One way to avoid this bug is to save values in the non-preserved registers on the stack before calling the function, then restore the values in the non-preserved registers after the function returns:
The callee perspective
A function cannot noticably change any preserved registers
Notice that we also saved the value of ra
on the stack. Remember that the ra
register stores the address that we’ll jump back to after this function finishes executing. Saving the value of ra
on the stack is necessary if we decide to call another function inside this function. If we make a function call at the “do stuff” comment, then that function call will overwrite ra
, and we’ll lose the address that we were supposed to jump back to.
Coding advice
- For a function that not call other functions, we perfer use caller saved register since it’s no need to s/w on stack.
- Save the value of ra at the start of a function and restore it at the end of a function.