编译器相关x86指令

发布于 作者: Ethan

1. 数据移动:mov

mov dest, src

作用:把 src 的值复制到 dest

常见形式:

mov rax, rdi      ; 寄存器 -> 寄存器
mov rax, [rdi]    ; 内存 -> 寄存器(load)
mov [rax], rdi    ; 寄存器 -> 内存(store)
mov rax, 13       ; 立即数 -> 寄存器
mov rax, [rsp-8]  ; 栈上取值

课程里要点

  1. mov 本质就是“拷贝值”。
  2. 方括号 [] 表示解引用地址,也就是访问内存。
  3. mov [rax], [rdi] 这种内存到内存形式在 x86 里不允许,必须借助中间寄存器。
  4. mov 不会更新 flags(这点很重要,因为后面条件跳转依赖 flags)。

2. 算术:add sub imul

add dest, src

作用:dest = dest + src

add rax, rdi
add rdx, 13

除了改目标操作数,还会更新 RFLAGS


sub dest, src

作用:dest = dest - src

sub rax, rdi
sub rdx, 13

也会更新 RFLAGS


imul

课程里提到它属于“会更新 flags 的算术指令”那一类。 作用:有符号整数乘法

在这门课的语境里,你只需要抓住:

  • 它是整数乘法
  • 它和 add/sub 一样会影响 flags
  • 编译表达式乘法时会用到它

不需要记完整 ISA 的各种复杂变体。


3. 比较与 flags:cmp test

这门课里,条件控制流的核心不是“if 指令”,而是:

  1. 先执行某条会设置 flags 的指令
  2. 再执行条件跳转 jcc

cmp a, b

作用:像做 a - b 一样设置 flags,但不保存结果

cmp rax, rcx

你可以理解成:

  • 不是为了得到减法结果
  • 而是为了让 ZF/SF/OF 变成后续 je/jle/jg... 能读的条件状态

常见用途

cmp rax, 0
je  some_label

意思就是:如果 rax == 0,跳转。


test a, b

作用:像做 a & b 一样设置 flags,但不保存结果

test rax, 1

常用来检测某些 bit 是否被置位。 课程里它主要用于标签/tag 检查这类场景。

直觉

  • cmp 更像“比较大小/相等”
  • test 更像“看某些位是不是开着”

4. 条件码与跳转:jmp jcc je jne jl jle jg jge


jmp loc

作用:无条件跳转

jmp done

效果就是直接把 RIP 改成 loc,下一条执行 loc 对应的指令。 这就是汇编里的“goto”。


jcc loc

这是一个家族,不是一条具体指令。 cc = condition code,表示“满足某个条件就跳”。

课程里明确讲到的有:

  • je:equal
  • jne / jne 的概念(讲义里重点写的是 ne
  • jl:less than
  • jle:less or equal
  • jg:greater than
  • jge:greater or equal

je loc

如果“相等”就跳。 本质上是检查 ZF = 1

cmp rax, rcx
je equal_case

jne loc

如果“不相等”就跳。 本质上检查 ZF = 0


jl loc

如果“小于”就跳。 这是有符号比较下的小于。 它不是简单看一个标志位,而是根据 SFOF 组合判断。


jle loc

如果“小于等于”就跳。


jg loc

如果“大于”就跳。


jge loc

如果“大于等于”就跳。


这门课里你真正该记的模式

不是死记 flags 公式,而是记这个套路:

cmp a, b
jcc label

也就是:

  • cmp 负责产生比较状态
  • jcc 负责根据状态选分支

这就是源语言里 if 的底层实现。


5. 位运算与布尔实现:and or setne


and dest, src

作用:按位与。

and rax, r10

课程里强调:

  • 这是bitwise and
  • 不是源语言层面的逻辑 and

但如果布尔值被规约成只有 0/1,那么按位与就和逻辑与等价。

课程里的用途

  1. 实现布尔运算
  2. 做 tag mask,比如检查最低几位标签

or dest, src

作用:按位或。

or rax, 0b11

课程里经常用它来:

  • 给运行时值打 tag
  • 构造布尔/数组等带标签的值

setne al

这是课上实现 intToBool 时出现的关键指令。

作用: 如果“not equal”条件成立,就把目标字节设成 1;否则设成 0

cmp rax, 0
mov rax, 0
setne al

这个序列的意思是:

  • 先比较 rax 和 0
  • 先把整个 rax 清零
  • 如果 rax != 0,就把低 8 位 al 设成 1

所以结果就是:

  • 原值非 0 -> rax = 1
  • 原值等于 0 -> rax = 0

这正好实现“把任意整数规约成布尔 0/1”。

为什么要先 mov rax, 0

因为 setne al 只写低 8 位 al,不清空高位。 先清零,才能保证最终整个 rax 是干净的 01


6. 栈操作:push pop


push arg

作用:把一个值压栈。

语义上等价于:

sub rsp, 8
mov [rsp], arg

也就是:

  1. 栈顶往下挪 8 字节
  2. 把值写到新的栈顶

pop reg

作用:从栈顶弹出一个值。

语义上等价于:

mov reg, [rsp]
add rsp, 8

也就是:

  1. 先读出栈顶值
  2. 再把 rsp 往上恢复 8 字节

课程里为什么这两个重要

因为后面的 call/ret 可以看成是:

  • call = push return_address + jmp
  • ret = pop return_address + jmp

7. 过程调用:call ret


call loc

作用:调用函数。

课程里的理解方式非常重要:

call loc

可以近似看成两步:

  1. 把“下一条指令地址”压栈
  2. 跳转到 loc

也就是“push return_address + jmp loc”。


ret

作用:从函数返回。

可以近似看成:

  1. 从栈顶弹出返回地址
  2. 跳到这个地址

也就是“pop addr + jmp addr”。


为什么 call/ret 是课程核心

因为这门课后半段开始做:

  • 非尾调用
  • extern function
  • System V AMD64 调用约定
  • 参数传递
  • 栈对齐

所有这些都围绕 call/ret 展开。


8. 调用约定里反复出现、但不是“新指令”的东西

这部分容易混淆,所以单独说。


rsp

不是指令,是寄存器。 它是栈指针。

在这门课里有两种视角:

  1. base pointer 视角:拿它当当前栈帧基准,访问局部变量
  2. top-of-stack 视角:配合 push/pop/call/ret 看成真正的栈顶

rax

通常是返回值寄存器。 也常被当 scratch register。


参数寄存器

SysV AMD64 下前六个参数放在:

  • rdi
  • rsi
  • rdx
  • rcx
  • r8
  • r9

这不是指令,但你在阅读课程里的 assembly 时必须认识。


9. 课程里还“出现”但要小心区分的,不一定是 x86 指令

有些名字你会在讲义里看到,但它们不是原生 x86 指令,而是:

  • SSA 操作
  • 伪代码
  • 运行时 helper

比如:

  • load(p, off)
  • store(p, off, v)
  • allocateArray(n)
  • assertInt
  • assertBool
  • assertArray
  • assertInBounds

这些在后端会被翻译成真正的 x86,例如:

  • load -> mov dest, [addr]
  • store -> mov [addr], src
  • allocateArray -> call 某个 runtime 函数

所以它们不是 x86 指令本体

10. flags

  • OF (Overflow Flag):溢出标志。算术结果发生有符号溢出时为 1,否则为 0。
  • SF (Sign Flag):符号标志。结果为负时为 1,否则为 0。
  • ZF (Zero Flag):零标志。结果为 0 时为 1,否则为 0。

明确两点:

  1. mov 不影响 flags
  2. addsubimul 这类算术指令会更新 flags;cmpsub 一样设置 flags 但不写回结果;test 像按位 and 一样设置 flags 但不写回结果。

另外,课程里条件跳转就是围绕这三个 flag 讲的,条件码公式也明确给了:

  • eZF
  • ne~ZF
  • lOF ^ SF
  • le(OF ^ SF) | ZF
  • g~((OF ^ SF) | ZF)
  • ge~(OF ^ SF)