x86-64 ISA 系统梳理

x86-64(又称 AMD64、Intel 64、x64)是目前桌面与服务器领域最主流的 64 位指令集架构,由 AMD 于 2000 年提出,2003 年随 Opteron/Athlon 64 发布。它向后兼容 16 位(8086)和 32 位(IA-32)x86 指令集。

1. 用例图 — x86-64 ISA 的参与者与功能

x86-64 ISA 用例图
图 1:x86-64 ISA 用例图 — 不同使用者与 ISA 的交互

不同使用者以不同方式与 x86-64 ISA 交互:

2. 活动图 — 指令执行流水线

x86-64 指令流水线活动图
图 2:x86-64 经典五级流水线活动图(现代 CPU 实际为超标量乱序执行)

指令在 CPU 内部的执行过程分为多个阶段,现代 x86-64 CPU 采用深度流水线、超标量、乱序执行(Tomasulo 算法)和推测执行。上图展示简化的经典五级流水线:取指 → 译码 → 执行 → 访存 → 写回。

3. 状态机图 — CPU 运行模式

x86-64 CPU 运行模式状态机
图 3:x86-64 CPU 运行模式状态机 — 实模式 / 保护模式 / 长模式 / SMM

x86-64 兼容多种历史运行模式。CPU 从上电复位开始于实模式,通过设置 CR0.PE 进入保护模式(32 位),然后通过设置 EFER.LME 和启用分页进入长模式(64 位)。系统管理模式(SMM)用于固件级电源管理和系统控制。

关键点:64 位长模式又分为两个子模式——64-bit mode(运行 64 位代码,使用 REX 前缀扩展 32 位指令)和兼容模式(运行旧的 16/32 位保护模式代码,无须模拟层)。

4. 序列图 — 系统调用流程

x86-64 系统调用序列图
图 4:x86-64 Linux 系统调用序列图(syscall 指令路径)

x86-64 提供两套快速系统调用指令:Intel 的 SYSCALL/SYSRET 和 AMD 的 SYSENTER/SYSEXIT。用户态程序将系统调用号放在 RAX 中,参数放在 RDI/RSI/RDX/R10/R8/R9 中,执行 syscall 指令即可陷入内核。RCX 保存返回地址,R11 保存 RFLAGS,内核通过 sysret 返回用户态。

5. 序列图 — 中断处理

x86-64 中断处理序列图
图 5:x86-64 中断处理序列图(硬件中断 + IDT 调度)

中断处理是操作系统核心机制。外部设备通过 APIC 向 CPU 发送中断向量号,CPU 在中断描述符表(IDT)中查找对应的门描述符,切换到内核栈,保存上下文,调用中断服务例程(ISR),最后通过 iretq 返回。

6. 活动图 — 函数调用约定

x86-64 函数调用约定活动图
图 6:x86-64 System V AMD64 ABI 函数调用约定活动图

x86-64 主要有两种调用约定:

7. 组件图 — x86-64 整体架构

x86-64 架构组件图
图 7:x86-64 ISA 整体架构组件图

x86-64 ISA 由多个核心子系统组成:指令集(含基础指令、扩展指令集、向量指令)、寄存器组、内存管理(分段/分页)、异常与中断机制、系统管理模式等。

8. 包图 — 指令集分类

x86-64 指令集分类包图
图 8:x86-64 指令集分类包图

x86-64 指令集非常丰富,可归为以下大类。`REX` 前缀(0x40-0x4F)用于在 64-bit 模式下扩展寄存器访问到 R8-R15 和 XMM8-XMM15,以及扩展操作数大小为 64 位。

9. 类图 — 寄存器文件

x86-64 寄存器文件类图
图 9:x86-64 寄存器文件类图

x86-64 相对于 IA-32 的主要变化是通用寄存器从 8 个扩展到 16 个,宽度从 32 位扩展到 64 位,并新增了 8 个 XMM 寄存器(XMM8-XMM15)。

10. 部署图 — x86-64 在计算机系统中的位置

x86-64 系统部署图
图 10:x86-64 CPU 在计算机系统中的部署图

11. 寄存器速查

通用寄存器(GPR)

64 位低 32 位低 16 位低 8 位用途(System V ABI)
RAXEAXAXAL返回值 / 累加器
RBXEBXBXBL被调用者保存
RCXECXCXCL第 4 参数(syscall: 返回地址)
RDXEDXDXDL第 3 参数 / 除法高位
RSIESISISIL第 2 参数 / 源指针
RDIEDIDIDIL第 1 参数 / 目标指针
RSPESPSPSPL栈指针
RBPEBPBPBPL基址指针(被调用者保存)
R8R8DR8WR8B第 5 参数
R9R9DR9WR9B第 6 参数
R10R10DR10WR10B第 4 参数(syscall 时)
R11R11DR11WR11Bsyscall 时保存 RFLAGS
R12–R15R12D–R15D——被调用者保存

特殊寄存器

寄存器用途
RIP指令指针(64 位,不能直接访问)
RFLAGS状态与控制标志寄存器(64 位)
CS/DS/SS/ES/FS/GS段寄存器(64 位模式下 FS/GS 仍用于 TLS)

SIMD 寄存器

寄存器数量宽度说明
MMX0–MMX7864 bit别名映射到 x87 FPU 栈(已过时)
XMM0–XMM1516128 bitSSE / SSE2 / SSE3 / SSSE3 / SSE4
YMM0–YMM1516256 bitAVX / AVX2(低 128 bit 即 XMM)
ZMM0–ZMM3132512 bitAVX-512(需要 CPU 支持)

系统寄存器(内核专用)

寄存器用途
CR0PE(保护启用)、PG(分页启用)、WP、TS、EM、MP、NE 等
CR2页错误线性地址(#PF 异常时自动加载)
CR3页表物理基址(PML4 基址)
CR4PAE、OSXSAVE、SMEP、SMAP、FSGSBASE 等
CR8任务优先级寄存器(TPR)
IDTR中断描述符表寄存器(基址 + 界限)
GDTR全局描述符表寄存器
EFERMSR 0xC0000080:LME(长模式启用)、LMA(激活)、SCE(syscall 启用)、NXE
STAR/LSTAR/CSTAR/SFMASKMSR:syscall/sysret 相关配置

x87 FPU 栈

寄存器数量宽度说明
ST(0)–ST(7)880 bitx87 浮点栈寄存器;80 位扩展双精度

12. 调用约定

System V AMD64 ABI(Linux / macOS / FreeBSD)

方面约定
整数参数RDI, RSI, RDX, RCX, R8, R9(左到右),超出 6 个走栈
浮点参数XMM0–XMM7(左到右),超出走栈或 RAX 整型传
返回值RAX(≤8 字节整数/Ptr)、RDX:RAX(128 位整数)、XMM0(float/double)、ST(0)(long double 80-bit)
栈对齐16 字节对齐(调用 call 前 RSP % 16 == 0)
红色区域RSP 以下 128 字节可直接被函数使用(不需移动 RSP),仅限 leaf function
被调用者保存RBX, RBP, R12–R15
调用者保存RAX, RCX, RDX, RSI, RDI, R8–R11, XMM0–XMM15, ST(0)–ST(7)

Microsoft x64 Calling Convention(Windows)

方面约定
整数参数RCX, RDX, R8, R9(左到右),超出 4 个走栈
浮点参数XMM0–XMM3(左到右),超出走栈
返回值RAX(整数/Ptr)、XMM0(float/double)
栈对齐16 字节对齐(call 前 RSP % 16 == 0)
影子空间每个函数调用必须预留 32 字节栈空间(即使参数 ≤ 4 个),用于被调用者保存寄存器参数(RCX/RDX/R8/R9)
被调用者保存RBX, RBP, RDI, RSI, R12–R15, XMM6–XMM15
调用者保存RAX, RCX, RDX, R8–R11, XMM0–XMM5

13. 寻址模式

x86-64 在 64-bit 模式下支持以下寻址模式。基址和变址可以用任意 GPR(16 个均可),比例因子为 1/2/4/8。

模式语法有效地址计算
立即数imm—
寄存器reg—
直接寻址[disp32]disp32 (RIP-relative in 64-bit mode)
基址[base]base
基址+偏移[base + disp]base + disp
基址+变址[base + index]base + index
变址×比例[index * scale]index × scale
基址+变址×比例[base + index * scale]base + index × scale
RIP 相对[rip + disp32]RIP + disp32(64-bit 模式独有,用于位置无关代码 PIC)
注意:64-bit 模式下 CS/DS/ES/SS 段基址视为 0(扁平地址空间),仅 FS/GS 可设非零基址(用于 TLS 等)。分段机制在长模式下被简化,实际内存保护由分页完成。

REX 前缀编码

REX 前缀(1 字节,0x40-0x4F)用于 64-bit 模式扩展寄存器寻址:

+---+---+---+---+---+---+---+---+
| 0 | 1 | 0 | 0 | W | R | X | B |
+---+---+---+---+---+---+---+---+
  W: 操作数宽度 0=32bit 1=64bit
  R: 扩展 ModRM.reg(与 ModRM reg 构成 4 位)
  X: 扩展 SIB.index
  B: 扩展 ModRM.rm 或 SIB.base

REX.W=1:  使用 64-bit 操作数
REX.R=1:  reg 字段扩展至 R8-R15
REX.X=1:  index 字段扩展至 R8-R15
REX.B=1:  rm/base 字段扩展至 R8-R15

14. EFLAGS/RFLAGS 标志位

位名称含义
0CF进位标志(无符号溢出)
2PF奇偶标志(结果低字节中 1 的个数为偶数)
4AF辅助进位标志(BCD 运算)
6ZF零标志(结果为零)
7SF符号标志(结果最高位)
8TF单步陷阱标志(调试用)
9IF中断使能标志
10DF方向标志(字符串操作方向)
11OF溢出标志(有符号溢出)
12–13IOPLI/O 权限级别
14NT嵌套任务标志
16RF恢复标志(屏蔽调试异常)
17VM虚拟 8086 模式
18AC对齐检查
19VIF虚拟中断标志
20VIP虚拟中断挂起
21IDCPUID 支持标识

15. 汇编示例

Hello World(Linux, System V ABI)

; hello.asm — Linux x86-64, NASM syntax
; nasm -f elf64 hello.asm -o hello.o && ld hello.o -o hello

section .data
  msg db "Hello, x86-64!", 10
  len equ $ - msg

section .text
  global _start

_start:
  mov rax, 1        ; syscall: write
  mov rdi, 1        ; fd = stdout
  mov rsi, msg      ; buf
  mov rdx, len      ; count
  syscall

  mov rax, 60       ; syscall: exit
  xor rdi, rdi      ; status = 0
  syscall

函数调用(C 调用约定)

; 调用 C 函数 add(a, b) 并返回结果
; int add(int a, int b) { return a + b; }

extern printf

section .data
  fmt db "sum=%d", 10, 0

section .text
  global main

main:
  push rbp
  mov rbp, rsp

  mov edi, 42       ; a = 42
  mov esi, 58       ; b = 58
  call sum

  mov rsi, rax      ; 返回值 -> printf 第2参数
  mov rdi, fmt      ; 格式串 -> 第1参数
  xor eax, eax      ; AL=0 表示无浮点参数传给 varargs
  call printf

  xor eax, eax
  pop rbp
  ret

sum:
  ; int sum(int a, int b) — a=edi, b=esi
  lea eax, [rdi + rsi]
  ret

SIMD 向量加法(SSE2)

; 四个单精度浮点数并行相加
; c[i] = a[i] + b[i], i=0..3

section .data
  align 16
  a dd 1.0, 2.0, 3.0, 4.0
  b dd 5.0, 6.0, 7.0, 8.0
  c dd 0, 0, 0, 0

section .text
  global _start

_start:
  movaps xmm0, [a]       ; 加载 4 个 float → XMM0
  movaps xmm1, [b]       ; 加载 4 个 float → XMM1
  addps  xmm0, xmm1      ; 并行加法(4 个 float 一次性完成)
  movaps [c], xmm0       ; 存结果

  mov rax, 60
  xor rdi, rdi
  syscall

原子操作(lock cmpxchg)

; 原子地比较并交换 — 实现无锁同步原语
; lock cmpxchg [mem], reg

section .text
  global atomic_inc

atomic_inc:
  ; void atomic_inc(uint64_t *ptr);
  ; ptr 在 rdi 中
  mov rax, [rdi]        ; 读取旧值
.retry:
  mov rdx, rax          ; 保存旧值
  inc rdx               ; 新值 = 旧值 + 1
  lock cmpxchg [rdi], rdx  ; [rdi] == rax ? [rdi] = rdx, ZF=1 : rax = [rdi], ZF=0
  jnz .retry            ; 失败则重试
  ret

RDTSC 性能计时

; 读取时间戳计数器 — 高精度性能测量
rdtscp_benchmark:
  ; 返回时钟周期数到 rax:rdx
  rdtsc                 ; 读 TSC → EDX:EAX
  shl rdx, 32
  or rax, rdx           ; 合并为 64 位值
  ret