3.8 条件码
条件码是 CPU 维护的一组单比特标志位,记录最近算术或逻辑操作的结果属性。它们为 和 提供判断依据。
四个核心条件码
| 标志位 | 全称 | 含义 | 置位条件 |
|---|---|---|---|
| CF | Carry Flag(进位/借位) | 无符号运算溢出 | 最高位产生进位或借位 |
| ZF | Zero Flag(零标志) | 结果为零 | 运算结果为 0 |
| SF | Sign Flag(符号标志) | 结果为负 | 结果的最高位(符号位)为 1 |
| OF | Overflow Flag(溢出标志) | 有符号运算溢出 | 有符号补码运算产生正溢出或负溢出 |
条件码的详细说明
CF(进位标志):
- 在无符号加法中,若结果超出无符号范围(),CF = 1
- 在无符号减法中,若不够减(产生借位),CF = 1
- 示例:
0xFF + 0x01 = 0x100→ CF = 1
ZF(零标志):
- 如果结果为 0,ZF = 1
- 示例:
0x42 - 0x42 = 0→ ZF = 1
SF(符号标志):
- SF = 结果的最高位(有符号数的符号位)
- 示例(8 位):
0x7F + 0x01 = 0x80→ SF = 1(结果为负数 -128)
OF(溢出标志):
- 当有符号运算产生”错误”的符号时置位
- 两个正数相加得负数 → 正溢出(OF = 1)
- 两个负数相加得正数 → 负溢出(OF = 1)
- 示例(8 位):
0x7F + 0x01 = 0x80(127 + 1 = -128)→ OF = 1
设置条件码的指令
1. 算术运算指令
中的 addq、subq、imulq、andq、orq、xorq 等都会设置条件码。
addq %rax, %rbx # 设置 CF, ZF, SF, OF
subq $1, %rcx # 设置条件码
imulq %rdx, %r8 # 设置条件码(imulq 主要关注 OF、CF)2. inc/dec 指令
incq 和 decq 会设置 ZF 和 SF,但不会修改 CF。
incq %rax # 设置 ZF、SF、OF,但不改 CF
decq %rbx # 设置 ZF、SF、OF,但不改 CF3. 逻辑操作
andq、orq、xorq 将 CF 和 OF 清零,设置 ZF 和 SF。
xorq %rax, %rax # rax = 0; CF = 0, OF = 0, ZF = 1, SF = 04. 只设置条件码的指令:cmp 与 test
cmp 和 test 只设置条件码,不修改目的操作数。
| 指令 | 等效运算 | 用途 |
|---|---|---|
cmpq S, D | D - S(相减) | 比较两个值 |
testq S, D | D & S(按位与) | 测试为零或符号 |
cmpq:比较指令
cmpq %rsi, %rdi # 计算 rdi - rsi,设置条件码等价于 subq 但不写回结果:
| cmpq 结果 | 对应条件 | 设置的标志 |
|---|---|---|
D == S | 相等 | ZF = 1 |
D != S | 不等 | ZF = 0 |
D < S (有符号) | 小于 | SF ≠ OF |
D > S (有符号) | 大于 | SF = OF 且 ZF = 0 |
D < S (无符号) | 低于 | CF = 1 |
D > S (无符号) | 高于 | CF = 0 且 ZF = 0 |
testq:测试指令
testq %rax, %rax # 计算 rax & rax(检查 rax 是否为 0/负)
testq $0xFF, %rdi # 检查 rdi 的低 8 位常用模式:
# 检查寄存器是否为零
testq %rax, %rax
je .Lzero # 如果 %rax == 0,跳转到 .Lzero
# 检查寄存器是否为负
testq %rax, %rax
js .Lnegative # 如果 %rax < 0,跳转到 .Lnegative
# 检查某一位是否设置
testq $0x100, %rdi # 检查第 8 位
jne .Lset # 如果该位为 1,跳转条件码的读取方式
条件码不能被程序员直接读取,但可以通过三种间接方式使用:
1. 条件跳转
根据条件码组合判断是否跳转。详见 。
cmpq %rsi, %rdi
jge .Lgreater # 如果 rdi >= rsi (有符号),跳转2. 条件传送
使用 cmov 系列指令根据条件码选择性地移动数据。详见 。
cmpq %rsi, %rdi
cmovge %rsi, %rdi # 如果 rdi >= rsi,rdi = rsi3. 条件码设置指令:set 系列
set 系列指令根据条件码组合将单字节设置为 0 或 1。
| 指令 | 条件 | 条件码组合 |
|---|---|---|
sete | 相等 (equal) | ZF |
setne | 不等 (not equal) | ~ZF |
sets | 负数 (sign) | SF |
setns | 非负 (not sign) | ~SF |
setg | 大于 (greater, 有符号) | ~(SF^OF) & ~ZF |
setge | ≥ (greater or equal, 有符号) | ~(SF^OF) |
setl | 小于 (less, 有符号) | SF^OF |
setle | ≤ (less or equal, 有符号) | (SF^OF) | ZF |
seta | 高于 (above, 无符号) | ~CF & ~ZF |
setae | ≥ (above or equal, 无符号) | ~CF |
setb | 低于 (below, 无符号) | CF |
setbe | ≤ (below or equal, 无符号) | CF | ZF |
# C 代码: char lt = (a < b);
# 假设 %rdi = a, %rsi = b
cmpq %rsi, %rdi # 比较 a 和 b
setl %al # al = (a < b) ? 1 : 0 (有符号比较)
movzbq %al, %rax # 零扩展到 64 位返回条件码在分支中的典型模式
# C 代码
# if (a >= b) { ... }
# 汇编
cmpq %rsi, %rdi # 比较 a (%rdi) 和 b (%rsi)
jge .L_true # 如果 a >= b,跳转到 true 分支
# else 分支代码...
jmp .L_done
.L_true:
# true 分支代码...
.L_done:条件码总结
| 标志 | 名称 | 相关指令判断 |
|---|---|---|
| CF | 进位 | 无符号比较 (a < b → CF=1) |
| ZF | 零 | 相等判断 (a == b → ZF=1) |
| SF | 符号 | 负数判断 (a < 0 → SF=1) |
| OF | 溢出 | 有符号溢出检测 |
关键理解:
- 无符号比较依赖 CF 和 ZF
- 有符号比较依赖 SF、OF 和 ZF
cmp和test是为了设置条件码而设计的专用指令
相关笔记
-
- 条件码驱动的跳转
-
- set 指令和 cmov 条件传送
-
- 设置条件码的算术指令
链接到
- 上一个知识点:3.7 算术运算指令
- 下一个知识点:3.9 跳转指令
- 相关知识点:4.7 SEQ处理器、4.8 SEQ各阶段分析