SETcc, CMOV и ветвления без прыжков
Контекст: x86/x86-64, синтаксис Intel (NASM). См. управляющие конструкции про флаги и
Jcc.
Два способа реагировать на флаги
После CMP или арифметики процессор выставляет ZF, CF, SF, OF и др. Классический путь — условный переход (JE, JG, JB …): меняется RIP, конвейер может «промахнуться» по предсказанию ветвления.
Альтернативы на x86:
SETcc— записать в 8-битный регистр или байт памяти 0 или 1 по условию.CMOVcc— условно скопировать значение между регистрами, не меняя поток команд.
Обе группы читают те же условия, что и Jcc (E = equal/ZF, NE, L/G знаковые, B/A беззнаковые).
SETcc — флаг в байт
cmp rax, rbx
sete al ; al = 1, если rax == rbx, иначе 0
movzx rax, al ; расширить до 64 бит, если нужно целое 0/1
Частые мнемоники:
| Мнемоника | Условие (после cmp A,B) |
|---|---|
SETE / SETZ | равны |
SETNE / SETNZ | не равны |
SETL / SETNGE | A < B (знаковое) |
SETG / SETNLE | A > B (знаковое) |
SETB / SETNAE | A < B (беззнаковое) |
SETA / SETNBE | A > B (беззнаковое) |
Ограничение: операнд назначения — только 8 бит (AL, BL, байт в памяти). Для 32/64-битного 0/1 делают SETcc + MOVZX/MOVSX.
Применение: упаковка логического результата в структуру, подготовка маски, минимизация ветвлений при серии мелких сравнений.
CMOVcc — условный MOV между регистрами
cmp rdi, rsi
mov rax, rdi
cmovl rax, rsi ; если rdi < rsi (знаковое), rax := rsi
CMOV не допускает память в обоих операндах (только регистр ↔ регистр в типичных формах). Условие ложно — значение в приёмнике не меняется, поэтому перед CMOV часто копируют «значение по умолчанию»:
mov rax, rdi ; предположим «максимум = rdi»
cmp rdi, rsi
cmovl rax, rsi ; если rsi больше — взять rsi
Так реализуют max(a,b) и min(a,b) без двух меток и прыжков.
Когда что выбирать
| Ситуация | Подход |
|---|---|
| Большие разные ветки кода | Jcc — меньше инструкций |
| Короткая выборка между двумя значениями | CMOV |
| Нужен 0/1 в памяти или массив флагов | SETcc |
| Глубокий конвейер, непредсказуемые ветви | иногда выгоднее CMOV (зависит от CPU и профиля) |
| Код, критичный к побочным эффектам чтения | осторожно: CMOV всё равно выполняет оба операнда с точки зрения микроархитектуры в старых моделях; для памяти с побочными эффектами используйте ветвление |
Современные компиляторы генерируют CMOV для тернарного оператора a < b ? b : a на простых типах.
Связь с длинной арифметикой
В длинном сложении финальный перенос за старший разряд удобно сохранить так:
setc al
movzx rax, al ; 1 = было переполнение беззнакового сложения
Вместо перехода на метку «overflow».
Чего избегать
SETccв 32-битный регистр напрямую — такой формы нет; только 8 бит.- Путаница знакового и беззнакового условия — те же правила, что для
JLиJBв типах данных. - CMOV вместо Jcc при вызове функций — если ветка вызывает разный код, нужен переход, а не условное копирование.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Полный отказ от высокоуровневых языков нецелесообразен. Поэтому большинство компиляторов поддерживают встроенный ассемблер — механизм вставки ассемблерных инструкций непосредственно в код на C/C++. %macro, %define и %if в NASM — шаблоны инструкций без дублирования исходного текста. Разделение программы на .asm-файлы, global и extern, сборка объектников и линковка в ELF. Вызов ассемблерных функций из C и наоборот — System V AMD64 ABI, выравнивание стека, сборка. Секции ELF, символы, objdump и сопоставление дизассемблирования с исходным NASM-кодом. REP, MOVS, SCAS, STOS, флаг DF и доступ к данным по индексу через таблицу. SSE2 для float и double, регистры XMM, выравнивание; кратко про стек x87 и AVX. Microsoft x64 calling convention, shadow space, вывод в консоль и файлы через API вместо syscall. Основы ассемблера - синтаксис Intel/AT&T, базовые инструкции и принципы низкоуровневого программирования. Архитектура ассемблерных программ - взаимодействие с ОС, вызовы библиотек и организация низкоуровневого кода. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и команды процессора в ассемблере - регистр команд, переходы и управление потоком исполнения.История ассемблерных языков
Макросы и условная сборка
Несколько модулей и линковка
Взаимодействие с C и C++
Чтение исполняемого файла и листинга
Строковые инструкции и таблицы поиска
Числа с плавающей точкой и SIMD
Windows x64, WinAPI и отличия от Linux
Основы ассемблера
Архитектура ассемблерных программ
Типы данных и регистры
Управляющие конструкции и команды процессора