Http://blogold.chinaunix.net/u1/40363/showart_434187.htmlchapter II register conventions
For the development of a CPU, it is very important to master the register conventions of the CPU in which it works.
The MIPS architecture provides 32 GPR (general purpose register ). The usage of these 32 registers is roughly as follows:
REGISTER NAME USAGE
$0 $ zero constant 0 (constant value 0)
$2-$3 $ v0-$ return value of v1 function call (values for results and expression evaluation)
$4-$7 $ a0-$ a3 function call parameters (arguments)
$8-$15 $ t0-$ t7 temporary (or casually used)
$16-$23 $ s0-$ s7 saved (or, if used, SAVE/RESTORE required) (saved)
$24-$25 $ t8-$ t9 temporary (or casually used)
$28 $ gp Global Pointer (Global Pointer)
$29 $ sp Stack Pointer (Stack Pointer)
$30 $ fp Frame Pointer (Frame Pointer)
(BNN: fp is stale acutally, and can be simply used as $ t8)
$31 $ ra return address)
Correct usage of a CPU register Convention is very important. Of course, C language developers do not need to CARE, because COMPILER will take care. However, the developers of KERNEL development or DRIVER development are ** required.
Generally, you can clearly see the usage of registers through objdump-d.
The following is a simple example:
~ /Vi Hello. c
"Hello. c" [New file]
/* Example to modify strate mips register convention
*-Author: BNN
* 11/29/2001
*/
Int addFunc (int, int );
Int subFunc (int );
Void main ()
{
Int x, y, z;
X = 1;
Y = 2;
Z = addFunc (x, y );
}
Int addFunc (int x, int y)
{
Int value1 = 5;
Int value2;
Value2 = subFunc (value1 );
Return (x + y + value2 );
}
Int subFunc (int value)
{
Return value --;
}
The above is a C program. The main () function calls an addition sub-function. Let's take a look at how the compiler generates code.
~ /Bnn: 74>/bin/mips-elf-gcc-c Hello. o Hello. c-mips3-mcpu = r4000-mgp32-mfp32-O1
~ /Bnn: 75>/bin/mips64-elf-objdump-d Hello. o
Hello. o: file format elf32-bigmips
Disassembly of section. text:
/* Main Function */
0000000000000000:
/* Create a stack frame by moving the stack pointer 8
* Bytes down and meantime update the sp value
*/
0: 27bdfff8 addiu $ sp, $ sp,-8
/* Save the return address to the current sp position .*/
4: afbf0000 sw $ ra, 0 ($ sp)
8: 0c000000 jal 0
/* Nop is for the delay slot */
C: 00000000 nop
/* Fill the argument a0 with the value 1 */
10: 24040001 li $ a0, 1
/* Jump the addFunc */
14: 0c00000a jal 28
/* Note here: Why we fill the second argument
* Behind the addFunc function call?
* This is all about the "-O1" compilation optimizaiton.
* With mips architecture, the specified uciton after jump
* Will also be fetched into the pipline and get
* Exectuted. Therefore, we can promise that
* Second argument will be filled with the value
* Integer 2.
*/
18: 24050002 li $ a1, 2
/* Load the return address from the stack pointer
* Note here that the result v0 contains the result
* AddFunc function call
*/
1c: 8fbf0000 lw $ ra, 0 ($ sp)
/* Return */
20: 03e00008 jr $ ra
/* Restore the stack frame */
24: 27bd0008 addiu $ sp, $ sp, 8
/* AddFunc Function */
0000000000000028:
/* Create a stack frame by allocating 16 bytes or 4
* Words size
*/
28: 27bdfff0 addiu $ sp, $ sp,-16
/* Save the return address into the stack with 8 bytes
* Offset. Please note that compiler does not save
* Ra to 0 ($ SP ).
* Think of why, in contrast of the previous powerpc
* Eabi Convention
*/
2c: afbf0008 SW $ Ra, 8 ($ SP)
/* We save the S1 Reg. value into the stack
* Because we will use S1 in this function
* Note that the 4,5, 6,7 ($ SP) positions will then
* Be occupied by this 32 bits size register
*/
30: afb10004 SW $ S1, 4 ($ SP)
/* Withe same reason, save S0 Reg .*/
34: afb00000 SW $ S0, 0 ($ SP)
/* Retrieve the argument 0 into s0 reg .*/
38: 0080802d move $ s0, $ a0
/* Retrieve the argument 1 into s1 reg .*/
3c: 00a0882d move $ s1, $ a1
/* Call the subFunc with a0 with 5 */
40: 0c000019 jal 64
/* In the delay slot, we load the 5 into argument a0 reg
* For subFunc call.
*/
44: 24040005 li $ a0, 5
/* S0 = s0 + s1; note that s0 and s1 holds the values
* X, y, respectively
*/
48: 02118021 addu $ s0, $ s0, $ s1
/* V0 = s0 + v0; v0 holds the return results of subFunc
* Call; And we let v0 hold the final results
*/
4c: 02021021 addu $ v0, $ s0, $ v0
/* Retrieve the ra value from stack */
50: 8fbf0008 lw $ ra, 8 ($ sp)
/*!!!! Restore the s1 reg. value */
54: 8fb10004 lw $ s1, 4 ($ sp)
/*!!!! Restore the s0 reg. value */
58: 8fb00000 lw $ s0, 0 ($ sp)
/* Return back to main func */
5c: 03e00008 jr $ ra
/* Update/restore the stack pointer/frame */
60: 27bd0010 addiu $ sp, $ sp, 16
/* SubFunc Function */
0000000000000064:
/* Return back to addFunc function */
64: 03e00008 jr $ ra
/* Taking advantage of the mips delay slot, filling
* Result reg v0 by simply assigning the v0 as the value
* Of a0.this is a bug from my c source
* Codes -- "value --". I shoshould write my codes
* Like "-- value", instead.
68: 0080102d move $ v0, $ a0
I hope you can calm down and understand the above Code. Make sure that the compiler saves them before using s0 and s1, and then RESTORE them, although in this example, the main function does not use s0 and s1.
Another point is: because we have added "-O1" optimization, the compiler uses "delay slot" to execute the required commands, instead of simply inserting a "nop" command there. Very beautiful.
Finally, let's take a look at a question to make everyone better understand the usage of registers:
* When writing a core scheduling context switch () routine, do we need to SAVE/RESTORE $ t0-$ t7? If not, why?
* When writing a clock interrupt processing routine, do we need to SAVE/RESTORE $ t0-$ t7? If so, why?