附录 1:x86-64 指令集速查表
本附录汇总了本书实现编译器时用到的 x86-64 指令和寄存器约定。
这里不是完整的指令集手册,而是为了便于查阅本书示例而整理的最小集合。
表中使用以下记法:
src、dst:两个大小相同的操作数,通常是寄存器或内存操作数;
r8、r16、r32、r64:分别表示 8 位、16 位、32 位、64 位寄存器;
imm:立即数;
reg1:reg2:把两个寄存器拼成一个更大的值,reg1 放高位,reg2 放低位。
整数寄存器列表
下表列出 x86-64 的通用整数寄存器,以及同一寄存器在不同位宽下的别名。
| 64 位 | 32 位 | 16 位 | 8 位 |
| RAX | EAX | AX | AL |
| RDI | EDI | DI | DIL |
| RSI | ESI | SI | SIL |
| RDX | EDX | DX | DL |
| RCX | ECX | CX | CL |
| RBP | EBP | BP | BPL |
| RSP | ESP | SP | SPL |
| RBX | EBX | BX | BL |
| R8 | R8D | R8W | R8B |
| R9 | R9D | R9W | R9B |
| R10 | R10D | R10W | R10B |
| R11 | R11D | R11W | R11B |
| R12 | R12D | R12W | R12B |
| R13 | R13D | R13W | R13B |
| R14 | R14D | R14W | R14B |
| R15 | R15D | R15W | R15B |
System V AMD64 ABI 中,常用寄存器约定如下。表中的“调用者保存”表示:函数调用后,被调用函数不保证这些寄存器仍保持原值;如果调用者后面还要使用它们,就必须在调用前自行保存。
| 寄存器 | 典型用途 | 调用者保存 |
| RAX | 返回值;可变参数函数中也用于传递向量参数个数 | 是 |
| RDI | 第 1 个整数/指针参数 | 是 |
| RSI | 第 2 个整数/指针参数 | 是 |
| RDX | 第 3 个整数/指针参数 | 是 |
| RCX | 第 4 个整数/指针参数 | 是 |
| R8 | 第 5 个整数/指针参数 | 是 |
| R9 | 第 6 个整数/指针参数 | 是 |
| R10 | 临时寄存器 | 是 |
| R11 | 临时寄存器 | 是 |
| RBP | 基址指针 | 否 |
| RSP | 栈指针 | 否 |
| RBX | 通用保存寄存器 | 否 |
| R12-R15 | 通用保存寄存器 | 否 |
调用函数时,执行 call 指令之前,栈指针 RSP 必须满足 ABI 要求的 16 字节对齐。没有正确对齐时,某些库函数可能崩溃。
内存访问
| 指令 | 含义 |
mov dst, [r64] | 从 r64 指向的地址读取值到 dst |
mov [r64], src | 把 src 的值写入 r64 指向的地址 |
push r64/imm | RSP -= 8,然后把值写入栈顶 |
pop r64 | 从栈顶读入 r64,然后 RSP += 8 |
函数调用
| 指令 | 含义 |
call label | 把返回地址压栈,然后跳转到 label |
call r64 | 把返回地址压栈,然后跳转到 r64 中保存的地址 |
ret | 从栈顶弹出返回地址,并跳转到该地址 |
leave | 等价于 mov rsp, rbp 后接 pop rbp |
条件分支
| 指令序列 | 含义 |
cmp reg1, reg2/imm
je label | 如果 reg1 == reg2/imm,跳转到 label |
cmp reg1, reg2/imm
jne label | 如果 reg1 != reg2/imm,跳转到 label |
cmp reg1, reg2/imm
jl label | 如果 reg1 < reg2/imm,跳转到 label(有符号比较) |
cmp reg1, reg2/imm
jle label | 如果 reg1 <= reg2/imm,跳转到 label(有符号比较) |
条件赋值
| 指令序列 | 含义 |
cmp reg1, reg2/imm
sete al
movzb eax, al | RAX = (reg1 == reg2/imm) ? 1 : 0 |
cmp reg1, reg2/imm
setne al
movzb eax, al | RAX = (reg1 != reg2/imm) ? 1 : 0 |
cmp reg1, reg2/imm
setl al
movzb eax, al | RAX = (reg1 < reg2/imm) ? 1 : 0(有符号比较) |
cmp reg1, reg2/imm
setle al
movzb eax, al | RAX = (reg1 <= reg2/imm) ? 1 : 0(有符号比较) |
整数与逻辑运算
| 指令 | 含义 |
add dst, src/imm | dst = dst + src/imm |
sub dst, src/imm | dst = dst - src/imm |
mul src | RDX:RAX = RAX * src(无符号) |
imul dst, src | dst = dst * src(有符号) |
div r32 | EAX = EDX:EAX / r32,EDX = EDX:EAX % r32 |
div r64 | RAX = RDX:RAX / r64,RDX = RDX:RAX % r64 |
idiv r32/r64 | div 的有符号版本 |
cqo | 把 RAX 符号扩展到 RDX:RAX |
and dst, src | dst = dst & src |
or dst, src | dst = dst | src |
xor dst, src | dst = dst ^ src |
neg dst | dst = -dst |
not dst | dst = ~dst |
shl dst, imm/CL | 逻辑左移。移位量可以是立即数,也可以放在 CL |
shr dst, imm/CL | 逻辑右移,高位补 0 |
sar dst, imm/CL | 算术右移,高位补符号位 |
lea dst, [src] | 计算地址表达式 [src],不访问内存,把地址结果放入 dst |
movsb dst, r8 | 把 8 位值符号扩展后写入 dst |
movzb dst, r8 | 把 8 位值零扩展后写入 dst |
movsw dst, r16 | 把 16 位值符号扩展后写入 dst |
movzw dst, r16 | 把 16 位值零扩展后写入 dst |