天天看點

MIPS寄存器約定

對于在一個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

繼續閱讀