Ветвление (branching) — это переход исполнения программы на другую инструкцию, обычно по условию. Вот основные виды ветвления (на примере x86):
JMP labelJcc label (JE, JNE, JL, JG и др.)CALL label, RETLOOP label, LOOPE, LOOPNEJMP [reg]INT N, IRETЖелающие подробности по архитектурам — читайте далее.
После арифметической/логической операции процессор выставляет флаги в регистре EFLAGS:
| Название | Описание |
|---|---|
| ZF (Zero Flag) | Результат 0? (1 если да) |
| SF (Sign Flag) | Знак результата (1=минус) |
| CF (Carry Flag) | Перенос (важно при беззнаковых) |
| OF (Overflow Flag) | Переполнение (важно при знаковых) |
| PF (Parity Flag) | Четность (не часто используется) |
| Инструкция | Условие | Обозначение |
|---|---|---|
| JE/JZ | ZF = 1 | Равно («Jump if Equal») |
| JNE/JNZ | ZF = 0 | Не равно |
| JL | (SF ≠ OF) | Меньше (signed) |
| JLE | (ZF=1 or SF ≠ OF) | Меньше или равно (signed) |
| JG | (ZF=0 and SF=OF) | Больше (signed) |
| JGE | (SF=OF) | Больше или равно (signed) |
| JB | CF = 1 | Меньше (unsigned) |
| JBE | CF=1 or ZF=1 | Меньше или равно (unsigned) |
| JA | (CF=0 and ZF=0) | Больше (unsigned) |
| JAE | CF = 0 | Больше или равно (unsigned) |
| CALL, RET | Вызов и возврат процедуры | |
| JMP [рег] | Косвенный переход |
JMP somewhere
mov eax, -3 ; eax = FFFFFFFDh (32-битный -3) mov ebx, 1 ; ebx = 00000001h cmp eax, ebx ; вычитание: -3 - 1 = -4 (0xFFFFFFFCh) jg more_label ; если eax > ebx (signed)? jl less_label ; если eax < ebx (signed)? je equal_label ; если eax == ebx? mov ecx, 123 jmp end more_label: mov ecx, 1 jmp end less_label: mov ecx, 2 jmp end equal_label: mov ecx, 3 end:
Анализ флагов:
'cmp eax, ebx':'jg': требует ZF=0 и SF=OF (1 ≠ 0 – не равно, перехода нет)'jl': SF ≠ OF? (1 ≠ 0 — переход будет!)mov eax, 7 mov ebx, 7 cmp eax, ebx je eq_label ; ZF=1, переход будет jne neq_label ; ZF=0, переход не будет
mov cx, 5 loop_start: ; ... тело цикла loop loop_start
call my_function ; ... my_function: ; действия ret
jmp [ebx] ; ebx — адрес новой инструкции
| Флаг | Назначение |
|---|---|
| N (Negative) | Результат отрицательный? (MSB=1) |
| Z (Zero) | Результат 0? |
| C (Carry) | Перенос/заём для беззнаковых |
| V (Overflow) | Переполнение для знаковых |
Инструкции, которые изменяют флаги — обычно с S: 'ADDS', 'SUBS', 'CMP', 'ANDS' и др.
Любая инструкция ветвления (и почти любая другая) может иметь суффикс условия:
| Суффикс | Условие | Описание |
|---|---|---|
| EQ | Z=1 | Равно |
| NE | Z=0 | Не равно |
| LT | N ≠ V | Меньше (signed) |
| GT | Z=0 и N=V | Больше (signed) |
| GE | N=V | Больше или равно (signed) |
| LE | Z=1 или N ≠ V | Меньше или равно (signed) |
| CS/HS | C=1 | Беззнаково >= |
| CC/LO | C=0 | Беззнаково < |
MOV R0, #5 MOV R1, #8 CMP R0, R1 ; R0 - R1 = -3 (Negative) BEQ label_eq ; Z=1 ? BNE label_ne ; Z=0 ? BLT label_lt ; N ≠ V ?
* После CMP:
* BEQ — не сработает (Z=0) * BNE — выполнится (Z=0) * BLT — N ≠ V (1 ≠ 0) — выполнится
BL myfunc ; вызов с занесением адреса возврата в LR ; ... myfunc: ; ... BX LR ; возврат
CMP R0, #3 BHI default LDR PC, [PC, R0, LSL #2] ; PC ← (таблица адресов: case0, case1...) .word case0 .word case1 .word case2 .word case3 case0: ; ... BX LR default:
Всё ветвление идёт по значению регистров.
| Инструкция | Что выполняет |
|---|---|
| beq $a, $b, label | Переход, если равны ($a = $b) |
| bne $a, $b, label | Переход, если не равны |
| slt $r, $a, $b | $r = 1, если $a < $b (signed), иначе 0 |
| sltu $r, $a, $b | $r = 1, если $a < $b (unsigned) |
| jal, jr $ra | Вызов/возврат процедуры |
li $t0, -2 li $t1, 1 slt $at, $t0, $t1 # $at = ($t0 < $t1) ? 1 : 0 bnez $at, less_label # если $at != 0 — переход будет
jal my_func # ... my_func: ; ... jr $ra
sll $t1, $a0, 2 la $t2, jumptable addu $t1, $t1, $t2 lw $t3, 0($t1) jr $t3
| Архитектура | Флаги/Механизм | Условные переходы | Рабочий пример |
|---|---|---|---|
| ————- | ——————————- | —————————————– | ——————– |
| x86 | EFLAGS (ZF, SF, OF, CF, PF) | JE, JNE, JL, JG, JA, JB… | cmp ax, bx; jl label |
| ARM | CPSR (N, Z, C, V) | B<cond> (BEQ, BNE, BLT и др.) | cmp r0, r1; blt label |
| MIPS | Нет флагов, сравн. в регистре | beq, bne, slt+bnez | slt $at, $a0, $a1; bnez $at, l |
mov eax, -3 ; FFFFFFFD mov ebx, 1 ; 00000001 cmp eax, ebx ; -3 - 1 = -4 (FFFFFFFC) jg more_label ; больше? (signed) jl less_label ; меньше? (signed) je equal_label ; равно?
Флаги:
'JG' требует ZF=0 и SF=OF: 1 ≠ 0 — перехода НЕ будет.
'JL' требует SF ≠ OF: 1 ≠ 0 — переход БУДЕТ!
—
mov eax, 7 mov ebx, 7 cmp eax, ebx je eq_label ; ZF=1 — переход БУДЕТ! jne neq_label ; ZF=0 — перехода не будет
—
MOV R0, #5 MOV R1, #8 CMP R0, R1 ; результат -3, N=1, Z=0, V=0 BEQ label_eq ; Z=1? BNE label_ne ; Z=0? BLT label_lt ; N ≠ V?
—
MOV R2, #0x7FFFFFFF ADD R2, R2, #1 ; R2 = 0x80000000 (отрицательное по знаку) CMP R2, #0 BEQ label_zero ; Z=0 BMI label_negative ; N=1 — БУДЕТ переход! BVS label_o ; V=1, было переполнение — БУДЕТ переход!
—
li $t0, -2 li $t1, 1 slt $at, $t0, $t1 # if $t0 < $t1, $at = 1 bnez $at, less_label # если 1 — переход БУДЕТ!
—