x86-64寄存器名称
x86-64寄存器起名:8位,%al;16位,%ax;32位,%eax;64位,%rax
x86-64寄存器作用
%rax: 返回值
%rbx: 被调用者保存
%rcx: 第4个参数
%rdx: 第3个参数
%rsi: 第2个参数
%rdi: 第1个参数
%rbp: 被调用者保存
%rsp: 栈指针
%r8: 第5个参数
%r9: 第6个参数
%r10: 调用者保存
%r11: 调用者保存
%r12: 被调用者保存
%r13: 被调用者保存
%r14: 被调用者保存
%r15: 被调用者保存
操作数种类
操作数分三种:立即数,寄存器,内存引用
立即数表示
汇编立即数表示:以$
开头,跟着标准C表示法,如$-577
,$0x1F
。不同的指令允许的立即数范围不同
寻址方式
寻址方式:立即数寻址,立即数作为值;寄存器寻址,寄存器值作为值;绝对寻址,立即数对应内存地址的内存值;间接寻址,寄存器值对应内存地址的内存值;变址寻址,运算表达式对应内存地址的内存值
表达式:(s取值1,2,4,8)
$Imm: Imm,立即数寻址
ra: R[ra],寄存器寻址
Imm: M[Imm],绝对寻址
(ra): M[R[ra]],间接寻址
Imm(rb): M[Imm + R[rb]],(基址+偏移量)寻址
(rb, ri): M[R[rb] + R[ri]],变址寻址
Imm(rb, ri): M[Imm + R[rb] + R[ri]],变址寻址
(,ri, s): M[R[ri] * s],比例变址寻址
Imm(,ri, s): M[Imm + R[ri] * s],比例变址寻址
(rb,ri, s): M[R[rb] + R[ri] * s],比例变址寻址
Imm(rb,ri, s): M[Imm + R[rb] + R[ri] * s],比例变址寻址
mov指令
movb, movw, movl, movq 分别表示把1,2,4,8字节的数据从源位置复制到目标位置。x86-64 限制源位置和目标位置不能两个都是内存。大部分指令都会有这4种变种,但如pushq,popq,leaq等指令没有
movz,movs是0扩展和符号扩展指令,将较小的数从寄存器或内存转移到寄存器中。
leap指令
地址的赋值
leaq S,D: 将S所表示的地址值赋值给D,经常用来做一些简单的算法。比如z = x+4*y可能表示为leaq (%rdi, %rsi, 4), %rax
位移指令
sarq,shrq表示算术右移和逻辑右移。salq, shlq表示算术左移和逻辑左移,但由于补码的特性,他们的操作结果是一样的。
乘法指令
*x86-64还为128位操作提供有限支持,当imulq和mulq为双操作数时,是64位乘法;当为单操作数时,另一个乘数将视为%rax,而结果将存放在%rdx(高64位),%rax(低64位)中。
跳转指令
jmp表示跳转指令,一般汇编用一个标号来指定,如jmp .L1。也可以利用寄存器,如jmp *%rax表示跳转到%rax的值的地址,jmp *(%rax)表示跳转到%rax值对应的内存值的内存地址去。和SET一样,也有je,js,jle等指令
call指令
call指令用于函数调用,此时将会把将紧跟着当前地址后面的那条指令的地址压入栈,并吧要跳转的函数地址设置到PC。退出时用ret。
AT&T
一元操作中,操作数既是源也是目的。操作数可以是内存地址。
二元操作中,源操作是第一个,目标操作是第二个。如subq %rax, %rdx等价与%rdx = %rdx – %rax。如果第二个操作数是内存地址,则必须读出,执行,再写入。注:此时应该会生成多条机器指令,则该操作不是原子操作。否则i++可以写成addq $1, (%rax)岂不是原子操作?所以推断该操作不是原子操作。
入栈及出栈
x86-64中,栈中低地址是栈顶,高地址是栈底。pushq时%rsp减小,popq时%rsp增大。
tips
-
GCC当switch的情况较多,且跨度较小的时候,会使用地址跳转表来翻译switch
-
当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就回在栈上分配空间,这个部分称为过程的栈帧