Я немного запутался в том, что "cmovb" делает в этом ассемблерном коде.

leal   (%rsi, %rsi), %eax  // %eax <- %rsi + %rsi
cmpl   %esi, %edi          // compare %edi and %esi
cmovb  %edi, %eax
ret

И код C для этого:

int foo(unsigned int a, unsigned int b)
{
    if(a < b) 
        return a;
    else
        return 2*b;
}

Может ли кто-нибудь помочь мне понять, как здесь работает cmovb?

1
boomken 8 Ноя 2018 в 02:15

2 ответа

Лучший ответ

Как и Джестер прокомментировал вопрос, семейство инструкций cmov* - это условные перемещения, соединенные через регистр flags с предыдущей (сравнительной) операцией.

Вы можете использовать, например, документация Intel в качестве справочника для набора инструкций x86-64 / AMD64. Инструкции по условному перемещению показаны на странице 172 объединенного тома.

cmovb, cmovnae и cmovc работают одинаково: если установлен флаг переноса, они перемещают исходный операнд в целевой операнд. В противном случае они ничего не делают.

Если затем мы посмотрим на предыдущие инструкции, которые влияют на флаги, мы увидим, что инструкция cmp (суффикс l является частью синтаксиса AT&T и означает, что аргументы являются "длинными") изменяет набор флагов в зависимости от разницы между двумя аргументами. В частности, если второй меньше первого (в синтаксисе AT&T), устанавливается флаг переноса, в противном случае флаг переноса сбрасывается; так же, как если бы вычитание было выполнено без сохранения результата. (Инструкция cmp влияет и на другие флаги, но они игнорируются кодом.)

0
Nominal Animal 7 Ноя 2018 в 23:45

C MOV B = Условное перемещение, если ниже (установлен флаг переноса). Он буквально делает то, что говорит, если условие выполнено, двигайтесь. Условие - a<b, а перемещаемое значение - 2*b.

ABI хранит возвращаемое значение в %edi, поэтому сначала сохраняет a, а затем условно перезаписывает его на 2*b.

0
MSalters 7 Ноя 2018 в 23:45