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支持的基于栈的方法来实现。

原文地址:https://www.cnblogs.com/rx719523850/p/5967854.html