Инструкции условного копирования

Последнее обновление: 23.09.2023

Нередко сравнение двух значений произодится, чтобы в зависимости от результатов сравнения загрузить в регистр некоторое значение или, наоборот, не загружать это значение, если проверка или сравнение не пройдены. Для упрощения подобных действий процессоры x86-64 поддерживают набор инструкций условного копирования cmov. Инструкции перемещения с проверкой флагов:

  • cmovc / cmovb / cmovnae: перемещает, если флаг переноса CF = 1

  • cmovnc / cmovnb / cmovae: перемещает, если флаг переноса CF = 0

  • cmovz / cmove: перемещает, если флаг нуля ZF = 1

  • cmovnz / cmovne: перемещает, если флаг нуля ZF = 0

  • cmovs: перемещает, если флаг знака SF = 1

  • cmovns: перемещает, если флаг знака SF = 0

  • cmovo: перемещает, если флаг переполнения OF = 1

  • cmovno: перемещает, если флаг переполнения OF = 0

Отдельная группа инструкций предназначена для сравнения с копированием. Здесь есть инструкции для сравнения беззнаковых чисел:

  • cmova: перемещает, если первый операнд больше второго (CF=0, ZF=0)

  • cmovnbe: перемещает, если первый операнд не меньше и не равен второму (CF=0, ZF=0)

  • cmovae / cmovnc / cmovnb: перемещает, если первый операнд больше или равен второму (CF=0)

  • cmovnb / cmovnc / cmovae: перемещает, если первый операнд не меньше второго (CF=0)

  • cmovb / cmovc / cmovnae: перемещает, если первый операнд меньше второго (CF=1)

  • cmovnae / cmovc / cmovb: перемещает, если первый операнд не больше и не равен второму (CF=1)

  • cmovbe: перемещает, если первый операнд меньше или равен второму (CF=1 или ZF=1)

  • cmovna: перемещает, если первый операнд не больше второго (CF=1 или ZF=1)

И инструкции для сравнения чисел со знаком:

  • cmovg: перемещает, если первый операнд больше второго (SF=OF или ZF=0)

  • cmovnle: перемещает, если первый операнд не меньше и не равен второму (SF=OF или ZF=0)

  • cmovge: перемещает, если первый операнд больше или равен второму (SF=OF)

  • cmovnl: перемещает, если первый операнд не меньше второго (SF=OF)

  • cmovl: перемещает, если первый операнд меньше второго (SF != OF)

  • cmovnge: перемещает, если первый операнд не больше и не равен второму (SF != OF)

  • cmovle: перемещает, если первый операнд меньше или равен второму (SF != OF или ZF=1)

  • cmovng: перемещает, если первый операнд не больше второго (SF != OF или ZF=1)

И две общие инструкции как для чисел со знаком, так и для беззнаковых чисел:

  • cmove: перемещает, если первый операнд равен второму (ZF=1). Аналогичен инструкции cmovz

  • cmovne: перемещает, если первый операнд не равен второму (ZF=0). Аналогичен инструкции cmovnz

Первый параметр этих инструкций (что копируем) представляет регистр общего назначения (16, 32 или 64-битный). Второй параметр (куда копируем) - либо регистр, либо переменная (также 16, 32 или 64-битные). Также к этим инструкциям прибавляется суффикс, который указывает на разрядность используемых данных - q (для 64-разрядных целых чисел), l (для 32-разрядных чисел), w (для 16-разрядных чисел) и b (для однобайтовых чисел). Например, варианты инструкции cmove для чисел разных разрядностей

  • cmoveq копирование в 64-разрядный регистр

  • cmovel копирование в 32-разрядный регистр

  • cmovew копирование в 16-разрядный регистр

  • cmoveb копирование в 8-разрядный регистр

Например, возьмем следующую программу:

.globl _start
.text
_start:
    movq $0xffffffffffffffff, %rcx
    movq $1, %rdx
    addq %rcx, %rdx         # складываем RCX и RDX
    jc carry_set            # если флаг переноса установлен, переходим к метке carry_set
    movq $2, %rdi           # если флаг переноса НЕ установлен
    jmp exit
carry_set:
    movq $4, %rdi       # если флаг переноса установлен
exit:
    movq $60, %rax
    syscall

Здесь складываем 2 числа (регистры RCX и RDX). И если при сложении устанавливается флаг переноса, то осуществляется переход к метке carry_set, где в регистр RDI помещается 4. Если флаг переноса не установлен, то в регистр RDI помещается 2, и выполняется завершение программы. С помощью инструкций условного перемещения эту программу можно упростить так:

.globl _start
.text
_start:
    movq $0xffffffffffffffff, %rcx
    movq $1, %rdx
    addq %rcx, %rdx         # складываем RCX и RDX, устанавливается флаг переноса CF

    movq $2, %rcx           # вариант, если флаг переноса сброшен (CF = 0)
    movq $4, %rdx           # вариант, если флаг переноса установлен (CF = 1)

    cmovncq %rcx, %rdi        # Если CF = 0
    cmovcq %rdx, %rdi         # Если CF = 1

    movq $60, %rax
    syscall

Другой пример - поместим в регистр RDI определенное значение в зависимости от результата сравнения:

.globl _start
.text
_start:
    movq $2, %rcx
    movq $2, %rdx
    cmpq %rcx, %rdx         # сравниваем RCX и RDX, устанавливается флаг нуля ZF

    movq $8, %rcx           # вариант, если флаг нуля сброшен (ZF = 0)
    movq $16, %rdx           # вариант, если флаг нуля установлен (ZF = 1)

    cmovneq %rcx, %rdi        # Если ZF = 0
    cmoveq %rdx, %rdi         # Если ZF = 1

    movq $60, %rax
    syscall

В данном случае если числа в регистрах RCX и RDX равны, в регистр RDI помещается число 16, иначе помещается число 8.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850