Нередко сравнение двух значений произодится, чтобы в зависимости от результатов сравнения загрузить в регистр некоторое значение или, наоборот,
не загружать это значение, если проверка или сравнение не пройдены. Для упрощения подобных действий процессоры 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.