3.8 条件码

条件码是 CPU 维护的一组单比特标志位,记录最近算术或逻辑操作的结果属性。它们为 和 提供判断依据。

四个核心条件码

标志位全称含义置位条件
CFCarry Flag(进位/借位)无符号运算溢出最高位产生进位或借位
ZFZero Flag(零标志)结果为零运算结果为 0
SFSign Flag(符号标志)结果为负结果的最高位(符号位)为 1
OFOverflow 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. 算术运算指令

中的 addqsubqimulqandqorqxorq 等都会设置条件码。

addq    %rax, %rbx          # 设置 CF, ZF, SF, OF
subq    $1, %rcx            # 设置条件码
imulq   %rdx, %r8           # 设置条件码(imulq 主要关注 OF、CF)

2. inc/dec 指令

incqdecq 会设置 ZF 和 SF,但不会修改 CF

incq    %rax                # 设置 ZF、SF、OF,但不改 CF
decq    %rbx                # 设置 ZF、SF、OF,但不改 CF

3. 逻辑操作

andqorqxorq 将 CF 和 OF 清零,设置 ZF 和 SF。

xorq    %rax, %rax          # rax = 0; CF = 0, OF = 0, ZF = 1, SF = 0

4. 只设置条件码的指令:cmp 与 test

cmptest 只设置条件码,不修改目的操作数。

指令等效运算用途
cmpq S, DD - S(相减)比较两个值
testq S, DD & 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 = rsi

3. 条件码设置指令: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溢出有符号溢出检测

关键理解

  • 无符号比较依赖 CFZF
  • 有符号比较依赖 SFOFZF
  • cmptest 是为了设置条件码而设计的专用指令

相关笔记

    • 条件码驱动的跳转
    • set 指令和 cmov 条件传送
    • 设置条件码的算术指令

链接到