Как эффективно проверять несколько значений в сборке - PullRequest
0 голосов
/ 01 июля 2018

Хотите знать, как вы выполняете такие вещи в сборке (NASM x86 или что-нибудь).

if (a && (b || !c) && (d || e)) {
  something()
}

Я только начинаю узнавать о сборке, поэтому хотел посмотреть, как вы справитесь с этой ситуацией с несколькими переменными. Вы не можете сделать (получить jnz от здесь ):

cmp eax, a && (b || !c) && (d || e)
jnz something

Так что, похоже, вам придется как-то накапливать значение.

1 Ответ

0 голосов
/ 01 июля 2018

Каждая инструкция 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...