3.9 跳转指令

跳转指令使程序能够改变指令的执行顺序,是实现分支和循环的基础。跳转指令分为无条件跳转条件跳转

无条件跳转:jmp

jmp 指令总是执行跳转,不受任何条件限制。

直接跳转

跳转目标由标号(label)指定,在汇编代码中以 .L 开头命名。

jmp     .Ldone              # 跳转到 .Ldone 标号处继续执行
 
.Ldone:
    # 跳转到这里继续执行
    ret

间接跳转

跳转目标从寄存器或内存中读取,实现动态跳转。详见 。

jmp     *%rax               # 以 %rax 中的值作为目标地址跳转
jmp     *(%rdi)             # 从内存中读取目标地址并跳转
jmp     *12(%rbp, %rdi, 8)  # 从计算出的内存地址读取目标

间接跳转的语法特征:* 前缀表示间接跳转。

条件跳转指令

条件跳转根据 的组合决定是否改变控制流。

有符号数条件跳转

指令含义条件C 比较
je (或 jz)相等时跳转ZF = 1a == b
jne (或 jnz)不等时跳转ZF = 0a != b
js负数时跳转SF = 1a < 0
jns非负时跳转SF = 0a >= 0
jl (或 jnge)小于时跳转 (less)SF ≠ OFa < b
jge (或 jnl)大于等于时跳转SF = OFa >= b
jle (或 jng)小于等于时跳转(SF ≠ OF) | ZFa <= b
jg (或 jnle)大于时跳转 (greater)(SF = OF) & ZF = 0a > b

无符号数条件跳转

指令含义条件
jb (或 jnae, jc)低于时跳转 (below)CF = 1
jae (或 jnb, jnc)高于等于时跳转CF = 0
jbe (或 jna)低于等于时跳转CF = 1 | ZF = 1
ja (或 jnbe)高于时跳转 (above)CF = 0 & ZF = 0

条件跳转操作示例

# C 代码: if (a >= b) goto true_branch;
 
# 假设 %rdi = a, %rsi = b
cmpq    %rsi, %rdi          # 计算 a - b
jge     .L_true             # 如果 a >= b,跳转
 
# a < b 的情况继续执行
# ...
 
.L_true:
# a >= b 的情况
# ...

条件跳转不能作为间接跳转

条件跳转不支持间接跳转(即不能 je *%rax),只有无条件跳转 jmp 支持间接跳转。

跳转指令的编码

大多数跳转指令使用 PC 相对寻址(PC-relative addressing),即目标地址编码为当前指令地址的偏移量。

# 汇编代码
je      .Ltarget
 
# 机器码示例 (假设 Ltarget 在当前指令后 0x42 字节处)
# 0x7E 0x42
# 其中 0x7E = je 的 opcode,0x42 = 相对于下一条指令的偏移

短跳转 vs 近跳转

  • 短跳转(short jump):偏移在一个字节内(-128~127),2 字节指令
  • 近跳转(near jump):偏移在四个字节内,6 字节指令
  • 汇编器自动选择合适的编码格式

分支的基本模式

if-else 模式

# C 代码
# if (a >= b) {
#     result = a - b;
# } else {
#     result = a + b;
# }
 
# 汇编
    cmpq    %rsi, %rdi      # 比较 a (%rdi) 和 b (%rsi)
    jl      .Lelse          # 如果 a < b,跳转到 else
    subq    %rsi, %rdi      # a >= b: result = a - b
    movq    %rdi, %rax
    jmp     .Ldone
.Lelse:
    addq    %rsi, %rdi      # a < b: result = a + b
    movq    %rdi, %rax
.Ldone:
    ret

条件表达式模式

C 语言的三目运算符 ?: 通常编译为条件传送或条件跳转:

# C 代码: long abs = (x >= 0) ? x : -x;
 
# 使用条件跳转的实现
    testq   %rdi, %rdi
    jge     .Lpos
    negq    %rdi
.Lpos:
    movq    %rdi, %rax
    ret

goto 模式

C 语言的 goto 对应汇编中的无条件跳转:

// C 代码(使用 goto 辅助翻译)
long fact_goto(long n) {
    long result = 1;
    if (n <= 1) goto done;
loop:
    result *= n;
    n--;
    if (n != 1) goto loop;
done:
    return result;
}
# 对应的汇编
fact_goto:
    movq    $1, %rax        # result = 1
    cmpq    $1, %rdi        # 比较 n 和 1
    jle     .Ldone          # 如果 n <= 1,跳转到 done
.Lloop:
    imulq   %rdi, %rax      # result *= n
    subq    $1, %rdi        # n--
    cmpq    $1, %rdi        # 比较 n 和 1
    jne     .Lloop          # 如果 n != 1,继续循环
.Ldone:
    ret

条件跳转的性能考虑

现代 CPU 使用分支预测(branch prediction)来推测执行。预测错误会导致流水线冲刷(pipeline flush),造成性能损失。

  • 预测正确:约 0-2 个周期损失
  • 预测错误:约 15-20 个周期损失

为减少分支预测失败,编译器会尝试将简单分支转换为 。

跳转指令总结

类型指令格式用途
无条件跳转jmpjmp .Ltargetjmp *%reg循环/switch/goto
条件跳转je/jne/jl/jge/...je .Ltargetif-else/循环条件
条件跳转(无符号)jb/jae/jbe/jaja .Ltarget无符号比较

相关笔记

    • 跳转指令的判断依据
    • 分支的完整实现
    • 循环中跳转的模式
    • 间接跳转的应用

链接到