Каждая инструкция asm в вашем источнике соответствует 1 машинной инструкции 1 . Ваш ассемблер не является компилятором.
То, что вы можете сделать в одной инструкции, полностью зависит от того, что может делать машинный код; Синтаксис исходного кода asm может выражать все. Инструкция x86 cmp
может считывать только 2 входных операнда и записывать флаги, поэтому, разумеется, она не может устанавливать флаги на основе 6 входов.
Иногда вы можете эффективно превратить логическое выражение источника C в одну ветвь asm, например, логический ||
может использовать побитовое ИЛИ, которое устанавливает ZF на основе результата, отличного от нуля. Так что if(a || b || c)
может скомпилироваться в
or eax, ebx
or eax, ecx
jnz .not_if
; if body
.not_if:
Но, как правило, лучше сравнивать и ветвить каждую часть условия в отдельности. например if (a && b)
может компилироваться в:
test eax,eax
jz .not_if
test ebx,ebx
jz .not_if
; if body
.not_if:
Вы не можете test eax,ebx
, потому что побитовое И может быть 0, например, с EAX = 1 и EBX = 2.
Другой случай, когда вы можете объединить вещи в один cmp / jcc - это проверки диапазона: двойная проверка состояния в сборке sub
, чтобы довести один конец диапазона до 0
, затем cmp
/ ja
(сравнение без знака). Переходит, если sub
перенесено в большое значение без знака, или если значение было вне диапазона.
В более общем плане, посмотрите, что делают компиляторы при компиляции C. Назначение переменной volatile
удобно помещать в тело if()
, если вы не хотите, чтобы компилятор мог поворачивать * 1034. * в CMOV без ответвлений или что-то. См. Как удалить "шум" из вывода сборки GCC / clang? для получения дополнительной информации о просмотре вывода компилятора и создании полезного ввода компилятора. Особенно выступление Мэтта Годболта на CppCon .
На самом деле создание логических значений в целочисленных регистрах и ANDing / ORing их вместе работает на MIPS (который не имеет регистра FLAGS, поэтому вы либо переходите, либо сравниваете в целочисленный регистр).
Или на POWER / PowerPC, где имеется несколько полей кода условия, и вы можете сравнить их с CR0, сравнить с CR1, а затем использовать команду регистра условий для объединения условий.
IDK, если вы забыли пометить x86 или пытались задать общий вопрос о разных языках ассемблера.
Сноска 1: Многие архитектуры RISC имеют "псевдоинструкции", которые расширяются до 2 или 3 реальных машинных инструкций, например li $t0, 0x1234567
в MIPS -> lui
для установки верхней половины 32-битной константы, и ori $t0, $t0, 0x4567
для установки нижней половины. Архитектуры с фиксированной шириной инструкции не могут вписать произвольную 32-битную константу в одну инструкцию, как это может сделать x86.
x86 не использует «псевдоинструкции». Они уже достаточно сложны. : P