To maintain an orderly assignment of registers, the MIPS register conventions specify which registers are preserved when a procedure is called and which ones are not. General registers $16-$23 and floating registers $f20-$f30 are specified as saved registers when a procedure is called.
In order to preserve a register, it must be saved before and then restored after a procedure call. Registers which are not saved are considered temporary and their contents may be changed during the execution of a procedure.
Like the return address and the parameters to a procedure, the saved registers are part of the environment of a procedure. The technique for saving the return address in $ra on the system stack may be used for any register which must be saved.
Registers may be saved either by the calling program or by the procedure which is called. In the first case, the registers to be saved are pushed on the stack before the procedure is called and popped off and restored after the procedure returns.
Suppose that registers $s0-$s2 are in use when procedure P is called. These registers are pushed on the stack after any parameters which are passed to the procedure. Prior to the jal P instruction the stack will contain:
The MAL code to save and restore these registers in the calling program is:
. . add $sp, -12 # save $s0-$s2 sw $s0, 12($sp) sw $s1, 8($sp) sw $s2, 4($sp) jal P # call procedure P lw $s2, 4($sp) # restore $s0-$s2 lw $s1, 8($sp) lw $s0, 12($sp) add $sp, 12 . . P: sw $ra, ($sp) # save return address add $sp, -4 # body of procedure (may change $s0-$s2) add $sp, 4 lw $ra, ($sp) # restore return address jr $ra # return
Normally, the calling program only saves temporary registers $t0-$t9 which are in use at the time of the procedure call.
The return address must always be saved inside the called procedure because the return address is not stored in $ra until the jal is executed. In the second method of saving registers, if the procedure modifies any saved registers, then it must save and restore these registers along with $ra.
Suppose that procedure P uses registers $s0-$s2. Then these registers must be saved and restored by P as follows:
. . jal P # call procedure P . . P: sw $ra, ($sp) # save return address add $sp, -4 add $sp, -12 # save $s0-$s2 sw $s0, 12($sp) sw $s1, 8($sp) sw $s2, 4($sp) # body of procedure (changes $s0-$s2) lw $s2, 4($sp) # restore $s0-$s2 lw $s1, 8($sp) lw $s0, 12($sp) add $sp, 12 add $sp, 4 lw $ra, ($sp) # restore return address jr $ra # return
The second method is preferred because all register saving code occurs inside the procedure. However, either method may be used as long as the calling program and called procedure are in agreement.
In both methods, saving and restoring the active registers adds a significant number of instructions and memory accesses. To minimize this overhead, procedures should use the temporary registers $t0-$t9 whenever possible because they do not have to be saved.