| Register Name | Alternate Name | Use |
|---|---|---|
| $0 | $zero | constant value 0 |
| $1 | $at | reserved by the assembler |
| $2 - $3 | $v0 - $v1 | expression evaluation and subprogram return value |
| $4 - $7 | $a0 - $a3 | the first four parameters - not preserved across calls |
| $8 - $15 | $t0 - $t7 | temporaries - not preserved across calls |
| $16 - $23 | $s0 - $s7 | saved values - preserved across calls |
| $24 - $25 | $t8 - $t9 | temporaries - not preserved across calls |
| $26 - $27 | $k0 - $k1 | reserved by the operating system |
| $28 | $gp | global pointer |
| $29 | $sp | stack pointer |
| $30 | $s8 | saved value - preserved across calls |
| $31 | $ra | return address |
| $f0 - $f2 | floating point subprogram return value | |
| $f4 - $f10 | temporaries - not preserved across calls | |
| $f12 - $f14 | the first 2 floating point parameters - not preserved across calls | |
| $f16 - $f18 | temporaries - not preserved across calls | |
| $f20 - $f30 | saved values - preserved across calls |
Register $0 is special in that it always has contents with value 0. If it is specified as a destination operand of an instruction then the result of the instruction is not stored.
The designations "preserved across calls" and "not preserved across calls" have significant implications for both caller and callee.
"Preserved across calls" means that the caller can count on the saved value registers having the same contents before and after a subprogram call. If the callee uses the saved value registers, the callee should take measures to save the register values and restore them before returning.
"Not preserved across calls" means that the caller cannot count on the temporary registers having the same contents before and after a subprogram call. Thus the the callee can use the temporary registers freely.
For simple programs, it is best to use memory or saved value registers for the main program variables and temporaries for subprogram local variables. For more complex programs, some subprograms will call other subprograms. Then great care is needed in using registers. The runtime stack can be used as a safe storage place for data when deeply nested subprogram calls occur. Use of the runtime stack is essential for recursive subprograms.
The primary purpose for the register use conventions described above is to
simplify the design of code with subprograms.
In MIPS assembly language, subprograms are called by using the
jal instruction.
This instruction instruction has a single operand, which is the label for
starting instruction in the subprogram.
The jal works like a branch instruction, with one additional
feature - it saves the PC in the return address register $ra.
The save is done after the PC is incremented so that $ra contains the
address of the instruction that follows the jal instruction.
When the subprogram is ready to return, it can use the jr
instruction, specifying $ra as a jump address.
Then instruction execution resumes where it left off before the call.
Most subprograms have parameters and many have returned values. In addition, subprograms use registers, creating the possibility of interfering with the callers use of registers. In order for subprograms to work, the calling code and the called subprogram must have a shared convention for the use of registers and dealing with parameters and returned values. The next section describes a relatively simple convention that is powerful enough to support recursion.
The convention described below works well for most subprograms with four or fewer parameters. For recursive subprograms with more than four parameters, it is easier to pass all parameters on the runtime stack. Fortunately, this is a rare situation.
jal.
To push data onto the runtime stack use the following code:
sw register_name, 0($sp) sub $sp, $sp, 4
To pop data from the runtime stack use the following code:
add $sp, $sp, 4 lw register_name, 0($sp)
Always use word loads and stores for pushes and pops, and always change
$sp by multiples of 4.
Otherwise, you will end up with unaligned memory references.
Also take great care to ensure that no matter how the subprogram code is
executed, it always does the same number of pushes and pops, and that pops
are done in reverse order to the pushes.
Otherwise, you will end with confusion.
The worst possibility is that the wrong value gets popped into $ra.
For an example of this register usage convention, see fact.s: An Example of a MAL Subprogram.