1. 程式人生 > >IA32暫存器與x86-64暫存器的區別

IA32暫存器與x86-64暫存器的區別

IA32暫存器

一個IA32CPU包含一組8個儲存32位值的通用暫存器,這些暫存器用來儲存整數資料和指標:

31-0 15-0 15-8 7-0 使用慣例
%eax %ax %ah %al 呼叫者儲存
%ecx %cx %ch %cl 呼叫者儲存
%edx %dx %dh %dl 呼叫者儲存
%ebx %bx %bh %bl 被呼叫者儲存
%esi %si 被呼叫者儲存
%edi %di 被呼叫者儲存
%ebp %bp 不得佔用
%esp %sp 不得佔用

第一行數字代表其下方對應的暫存器儲存的位數,例如暫存器%eax是32位的,從0到31正好32位,%ax是16位暫存器,從0到15正好是16位。注意,如果讀取%eax的8-15位則讀取的是%ah,其他情況類似。

程式可以獨立地讀取前四個暫存器的2個低位位元組,即可以獨立地讀取前四個暫存器的0-7位和8-15位。而後四個則不能。表中無代表對應位置無暫存器。

其中0-7位和8-15位的暫存器可以儲存一個位元組的資料,如char型資料;0-15位的暫存器可以儲存兩個位元組的資料,如short型別的資料;0-31位的暫存器可以儲存四個位元組的資料,如int型資料。還有如long型別的資料可以用暫存器%edx儲存高32位,用%eax儲存低32位的資料。

暫存器%eax,%ecx和%edx由呼叫者儲存,而暫存器%ebx,%esi和%edi由被呼叫者儲存。就是說如果一個函式呼叫另一個函式,由於暫存器數量有限(只有8個),如果函式中引數或區域性變數過多,暫存器就不夠用了,所以要把暫存器中儲存的值儲存到棧中防止資料丟失,然後該暫存器就可以用來儲存別的資料了,那把這個活交給誰呢?所以就規定前三個暫存器由呼叫者儲存,接下來的三個由被呼叫者儲存。

而暫存器%ebp和%esp有特殊用途,%ebp儲存幀指標(基地址,base pointer),%esp儲存棧指標(stack pointer)。程式棧中每呼叫一個函式就會建立一個棧幀,棧是向下增長的。暫存器%ebp儲存一個棧幀的起始地址(即基地址),一般來說函式中的區域性變數靠%ebp加上偏移量尋找;暫存器%esp儲存指向棧頂的指標。

暫存器%eax和暫存器%edx也有特殊用途,%eax一般用於儲存函式的返回值,也用來儲存64位資料的低32位;%edx用來儲存64位資料的高32位。

CPU中只有這八個暫存器嗎?顯然不是,IA32還有8個80位的浮點暫存器。還有很多特殊用途的暫存器,比如控制暫存器%cr0,%cr2,%cr3和%cr4;還有debug暫存器%dr0,%dr1,%dr2和%dr3;段暫存器%cs, %ds, %es, %fs, %gs和%ss;還有全域性和區域性描述符表的虛擬暫存器%gdtr(global descriptor table register)和%ldtr(local descriptor table register)。這些暫存器在作業系統中都是很重要的,在這裡不再贅述,想知道的可以上網查查。

x86-64暫存器

x86-64最初由AMD提出並命名,將IA32擴充套件到64位並且新增加了8個暫存器,到達16個通用目的暫存器。這大大提高了機器的效能。擴充套件到64位可以大大擴大機器可以使用的虛擬地址空間的大小。x86-64的暫存器無論只在數量上還是在使用方法上都與IA32有很大不同,先看下錶:

0-63 0-31 0-15 8-15 0-7 使用慣例
%rax %eax %ax %ah %al 儲存返回值
%rbx %ebx %bx %bh %bl 被呼叫者儲存
%rcx %ecx %cx %ch %cl 第4個引數
%rdx %edx %dx %dh %dl 第3個引數
%rsi %esi %si %sil 第2個引數
%rdi %edi %di %dil 第1個引數
%rbp %ebp %bp %bpl 被呼叫者儲存
%rsp %esp %sp %spl 棧指標
%r8 %r8d %r8w %r8b 第5個引數
%r9 %r9d %r9w %r9b 第6個引數
%r10 %r10d %r10w %r10b 呼叫者儲存
%r11 %r11d %r11w %r11b 呼叫者儲存
%r12 %r12d %r12w %r12b 被呼叫者儲存
%r13 %r13d %r13w %r13b 被呼叫者儲存
%r14 %r14d %r14w %r14b 被呼叫者儲存
%r15 %r15d %r15w %r15b 被呼叫者儲存

上表的格式說明和IA32一樣,可以看到x86-64共有16個暫存器(16行),可以獨立訪問前四個暫存器的8-15位,而其他暫存器不可以。

最後一列說明該暫存器的使用慣例,可以看到如果函式引數不超過6個則可以儲存在暫存器中而不用轉存在記憶體中,如果函式的引數的個數超過6個則其餘的引數須儲存在棧中。

還可以看見,x86-64並沒有幀指標,而只用棧指標(%rsp),作為替代,x86-64對棧位置的引用相對於棧指標(而IA32相對於幀指標%ebp),大多數函式在呼叫開始時分配所需要的整個棧儲存,並保持棧指標指向固定的位置。函式最多可以訪問當前棧指標值128個位元組的棧上的儲存空間,也就是說x86-64的程式可以使用當前棧指標之外128位元組範圍內的資料。

在IA32中棧指標會隨著值的壓入和彈出不斷前後移動,所以在IA32中通過幀指標(%ebp)來訪問棧中的資料。但是x86-64過程中的棧幀通常有固定的大小,在函式開始時通過減少棧指標(%rsp)來設定。在呼叫過程中棧指標保持固定的位置,使得可以通過相對於棧指標的偏移量來訪問資料。因此就不再需要幀指標了,可以把%rbp用作通用暫存器。

x86-64主要特性如下:

1.指標和長整數是64位長(IA32的指標是32位長),整數算術運算支援8,16,32,和64位資料型別。

2.通用目的暫存器由8個擴充套件到16個。

3.許多程式狀態都儲存在暫存器中而不是棧上(因為暫存器數量多了)。整型和指標型別的過程引數(最多6個)通過暫存器傳遞。減少了對棧的訪問,從而提高了效率。

4.如果可能,條件操作用條件傳送指令實現,這樣會比傳統的分支程式碼得到更好的效能。

5.浮點操作用面向暫存器指令集來實現,而不用基於IA32支援的基於棧的方法來實現。