對于在一個CPU上進行開發,掌握其工作的CPU的寄存器約定是非常重要的。
MIPS體系結構提供了32個GPR(GENERAL PURPOSE REGISTER)。這32個寄存器的用法大緻如下:
REGISTER NAME USAGE
$0 $zero 常量0(constant value 0)
$2-$3 $v0-$v1 函數調用傳回值(values for results and expression evaluation)
$4-$7 $a0-$a3 函數調用的前幾個參數(arguments)
$8-$15 $t0-$t7 臨時變量。子程式使用時無需儲存,從上層函數來看,函數調用前後,寄存器的值可能會發生變化。
$16-$23 $s0-$s7 需要儲存的變量。子程式使用時,必須儲存原始值,并在傳回前恢複,從上層函數來看,這些寄存器的值沒有變化。
$24-$25 $t8-$t9 臨時變量
$26-$27 $k0-$k1 保留給中斷或自陷處理程式使用;因而值可能随時發生變化
$28 $gp 全局指針(Global Pointer) 。利用gp做基址,對于gp指針前後32K範圍的資料存取,隻需要一條指令就可以完成。通常的做法是将一些小的全局資料(static extern)等,放在一起,用gp作指針。
$29 $sp 堆棧指針(Stack Pointer) 。MIPS通常隻在多重調用時,子程式出口和入口才調整堆棧指針,這個過程由子程式負責完成。
$30 $fp 幀指針(Frame Pointer) (BNN:fp is stale acutally, and can be simply used as $t8)
$31 $ra 傳回位址(return address)
對一個CPU的寄存器約定的正确用法是非常重要的。當然對C語言開發者不需要關心,因為COMPILER會TAKE CARE。但對于KERNEL的開發或DRIVER開發的人就**必須**清楚。
一般來講,你通過objdump -d可以清醒的看到寄存器的用法。
下面通過我剛才寫的一個簡單例子來講解:
~/ vi Hello.c
"Hello.c" [New file]
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--;
}
上面是一個C程式,main()函數調用一個加法的子函數。讓我們來看看編譯器是如何産生代碼的。
~/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:
0000000000000000 :
0: 27bdfff8 addiu $sp,$sp,-8
4: afbf0000 sw $ra,0($sp)
8: 0c000000 jal 0
c: 00000000 nop
10: 24040001 li $a0,1
14: 0c00000a jal 28
18: 24050002 li $a1,2
1c: 8fbf0000 lw $ra,0($sp)
20: 03e00008 jr $ra
24: 27bd0008 addiu $sp,$sp,8
0000000000000028 :
28: 27bdfff0 addiu $sp,$sp,-16
2c: afbf0008 sw $ra,8($sp)
30: afb10004 sw $s1,4($sp)
34: afb00000 sw $s0,0($sp)
38: 0080802d move $s0,$a0
3c: 00a0882d move $s1,$a1
40: 0c000019 jal 64
44: 24040005 li $a0,5
48: 02118021 addu $s0,$s0,$s1
4c: 02021021 addu $v0,$s0,$v0
50: 8fbf0008 lw $ra,8($sp)
54: 8fb10004 lw $s1,4($sp)
58: 8fb00000 lw $s0,0($sp)
5c: 03e00008 jr $ra
60: 27bd0010 addiu $sp,$sp,16
0000000000000064 :
64: 03e00008 jr $ra
/* Taking advantage of the mips delay slot, filling the
* result reg v0 by simply assigning the v0 as the value
*of a0. This is a bug from my c source
* codes--"value--". I should write my codes
* like "--value", instead.
68: 0080102d move $v0,$a0
希望大家靜下心來把上面的代碼看懂。一定要注意編譯器為什麼在使用s0和s1之前要先把她們SAVE起來,然後再RESTORE,雖然在這個例子中雖然main 函數沒用s0和s1。
另外的一點是:由于我們加了“-O1”優化,編譯器利用了“delay slot"來執行那些必須執行的指令,而不是簡單的塞一個”nop"指令在那裡。非常的漂亮。
最後,考大家一個問題,為了使得大家更加了解寄存器的用法:
*在寫一個核心排程context switch()例程時,我們需要SAVE/RESTORE$t0-$t7嗎?如果不,為什麼?
*在寫一個時鐘中斷處理例程時,我們需要SAVE/RESTORE$t0-$t7嗎?如果是,為什麼?
本文來自CSDN部落格,轉載請标明出處:http://blog.csdn.net/yoursy/archive/2008/05/06/2399721.aspx