整理一下IA32处理器和x86-64处理器中通用寄存器的差异。
IA32既支持32位处理器,也向后兼容16位寄存器。IA32把16位的通用寄存器,标志寄存器和指令指针寄存器扩充为32位。段寄存器仍为16位。IA32 CPU中包含一组8个32位通用寄存器,用来存储整数数据和指针。它们的名字以%e开头,可以理解为对16位的extend。
这8个寄存器分别为eax, ebx, ecx, edx, esi, edi, ebp和esp,其中eax,ebx,ecx,edx为数据寄存器,esi,edi为变址寄存器,ebp和esp为指针寄存器。eax,ebx,ecx,edx的低16位可以像以前的16位寄存器一样单独当成16位寄存器使用,例如eax的低16位ax,并且这个低16位寄存器的高8位和低8位可以分别当成两个8位寄存器使用,例如ax的低8位al和高8位ah,ebx,ecx,edx同eax。寄存器的名字反映了它的用途。八个通用寄存器的名字和一般用途如下(除ESP和EBP外用途仅供参考,具体看使用者):
- EAX 一般作为累加器(add)
- EBX 一般作为基地址寄存器(base)
- ECX 一般作为计数寄存器(count)
- EDX 一般用来存放数据(data)
- ESP 一般作为栈指针寄存器(stack pointer)
- EBP 一般作为基指针寄存器(base pointer)
- ESI 一般作为源变址寄存器(source index)
- EDI 一般作为目标变址寄存器(destinatin index)
下图即为IA32通用寄存器
在x86-64中,所有的寄存器都扩充到了64位,不仅位数上发生变化,通用寄存器的数量也从原有的8个增加到16个。标识符上,从原来的%eax变成了%rax,但是为了保持向后兼容性,%eax仍然可用,并且指向%rax的低32位。寄存器的发展中仍然保持着对上一般本的兼容。
x86-64中有了更多数量的寄存器,可以把堆栈数据直接放在寄存器中代替使用存储器,从而加快运算速度。x86-64的16个寄存器分别是%rax, %rbx, %rcx, %rdx, %rdi, %rsi, %rsp, %rbp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15。
16个寄存器的名字和一般用途如下:
- %rax 作为函数返回值使用。
- %rsp 栈指针寄存器,指向栈顶
- %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
- %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
- %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
下图为x86-64通用寄存器
下面我们写一个简单的C函数来看一下gcc生成的32位和64位汇编代码的区别:
C语言代码:(保存为simple.c)
void
swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
下面我们使用gcc -O1 -S -ma32 simple.c来生成32位汇编代码:
.file "simple.c"
.text
.globl swap
.type swap, @function
swap:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 8(%ebp), %eax
movl (%eax), %eax
movl %eax, -4(%ebp)
movl 12(%ebp), %eax
movl (%eax), %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 12(%ebp), %eax
movl -4(%ebp), %edx
movl %edx, (%eax)
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size swap, .-swap
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
再使用gcc -S -ma64 simple.c 来生成64位汇编代码:
.file "simple.c"
.text
.globl swap
.type swap, @function
swap:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movq %rsi, -32(%rbp)
movq -24(%rbp), %rax
movl (%rax), %eax
movl %eax, -4(%rbp)
movq -32(%rbp), %rax
movl (%rax), %edx
movq -24(%rbp), %rax
movl %edx, (%rax)
movq -32(%rbp), %rax
movl -4(%rbp), %edx
movl %edx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size swap, .-swap
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
参考:1. 《深入理解计算机系统》
2. 淘宝博客http://www.searchtb.com/2013/03/x86-64_register_and_function_frame.html