Инструкция CCMP. Объединение условий

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

Инструкция CCMP позволяет выполнить сравнение операндов исходя из уже ранее выполненного сравнения. Благодаря этому мы можем объединить несколько условий и проверить некоторое условие, если только другое условие истинно.

Данная инструкция имеет следующий синтаксис:

CCMP Xn, Xm/#imm, nzcv, cond

Инструкция принимает 4 операнда:

  • Xn: 64-разрядный или 32-разрядный регистр

  • Xm/#imm: регистр той же разрядности или непосредственный операнд

  • nzcv: значение для флагов NZCV, если условие cond не выполняется

  • cond: условие, при котором выполняется сравнение в CCMP (одно из условий, перечисленых в https://metanit.com/assembler/arm64/2.7.php)

Эта инструкция проверяет условие cond. Если оно истинно, то сравнивает операнды Xn и Xm/#imm и в результате сравнения соответствующим образом устанавливает флаги NZCV. Если условие cond ложно, то устанавливает для флагов NZCV значение nzcv (третий операнд инструкции). С помощью псевдокода можно описать механику инструкции следующим образом:

if(cond) 
{
  NZCV = CMP(Xn, Xm/#imm);
} 
else 
{
  NZCV = nzcv;
}

Поскольку эта инструкция применяется для проверки комлексных, комбинированных условий, а комбинировать условия можно по разному, то рассмотрим несколько ситуаций, как можно построить и проверить подобные условия.

Объединение условие с помощью AND

Объединение условие с помощью AND предполагает, что некоторое действие выполняется, если истинны оба условия:

cond1 && cond2

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

 X0 == X1 && X2 < X3

То есть значение регистров X0 и X1 должны быть равны, и одновременно значение регистра X2 должно быть меньше числа в X3. Посмотрим, как нам проверить такое сложно условие в ассемблере ARM64.

.global _start

_start:
    mov x0, #4         
    mov x1, #4 
    mov x2, #5         
    mov x3, #6 
    cmp x0, x1          // сравниваем x0 и x1 и устанавливаем флаги
    ccmp x2, x3, 0, EQ  // если x0 == x1, то сравниваем LT x2 и x3
    b.lt true       // если  x2 < x3, переход к метке true

    mov x0, #1          // если x0 != x1 или x2 >= x3, то x0 = 1
    b exit
true:
    mov x0, #2         // если x0 == x1 И x2 < x3, то x0 = 2
exit:
    mov x8, #93         // номер функции Linux для выхода из программы - 93
    svc 0               // вызываем функцию и выходим из программы

Рассмотрим поэтапно. Вначале проверяем первое условие инструкций CMP:

cmp x0, x1

В результате проверки устанавливаются флаги.

Далее проверяем второе условие инструкцией CCMP:

ccmp x2, x3, 0, EQ

Эта инструкция в качестве условия принимает EQ. То есть она выполняет проверку второго условия, если только флаг нуля Z установлен, что значит, что при предыдущим сравнении значения регистров Х0 и Х1 оказались равны. То есть если первое условие истинно.

Допустим, первое условие оказалось истинно, выполняется сравение значений регистров Х2 и Х3, и опять устанавливаются флаги. Далее мы можем проверить результат второй проверки. И если первое число (Х2) оказалось меньше второго (Х3), выполнить переход к метке true

b.lt true       // если  x2 < x3, переход к метке true

Таким образом, второе условие проверяется, если только первое истинно (x0 == x1). Соответственно, если после второй проверки второе условие оказалось истинно, это подразумевает, что и первое условие было истинно.

Но что, если первое условие НЕ истинно (Х0 != Х1)? В этом случае нам надо третьему операнду инструкции CCMP передать некоторое значение. Этот операнд представляет значение, которое передается флагам NZCV. То есть нам надо передать четырехбитное число, где каждый бит будет определять значение для каждого флага состояния NZCV. Какое же значение нам надо передать?

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

b.lt true       // если  x2 < x3, переход к метке true

Поэтому в инструкции CCMP нам надо передать флагам такие значения, при которых второе условие будет ложным. Что представляет собой второе условие - LT? А оно предполагает, что N-флаг и V-флаг не равны (N != V). Например, один флаг равен 1, а второй - 0 или наоборот. Следовательно, чтобы условие LT было ложным, надо сделать так, чтобы эти флаги были равны, то есть передать им одно и то же значение:

ccmp x2, x3, 0, EQ

В примере выше флагам передается число 0 или фактически 0000, то есть все флаги будут равны 0 (N=Z=C=V=0). Причем в данном случае нас не интересуют флаги C и Z. Нам главное, чтобы флаги N и Z были равны.

Таким образом, если первое условие (X0 == X1) ложно, то инструкция CCMP не выполняет сравение Х2 и Х3, а устанавливает флаги таким образом, чтобы условие LT не соблюдалось. В итоге, при выполнении инструкции

b.lt true       // если  x2 < x3, переход к метке true

условие LT ложно, и переход к метке true не выполняется.

Таким образом, мы можем объединить два условия и выполнять действия, если только оба этих условия истинны.

Объединение условие с помощью OR

Объединение условие с помощью OR предполагает, что некоторое действие выполняется, если истинно хотя бы одно из двух условий:

cond1 || cond2

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

 X0 == X1 || X2 < X3

То есть или значения регистров X0 и X1 должны быть равны, или значение регистра X2 должно быть меньше числа в X3. Достаточно, соблюдения только одного из этих условий. Посмотрим, как это сделать:

.global _start

_start:
    mov x0, #4         
    mov x1, #4 
    mov x2, #6         
    mov x3, #6 
    cmp x0, x1          // сравниваем x0 и x1 и устанавливаем флаги
    ccmp x2, x3, 8, NE  // если x0 == x1, то сравнение x2 и x3 не имеет смысла
    b.lt true       // если  x2 < x3, переход к метке true

    mov x0, #1          // если x0 != x1 И одновременно x2 >= x3, то x0 = 1
    b exit
true:
    mov x0, #2         // если x0 == x1 ИЛИ x2 < x3, то x0 = 2
exit:
    mov x8, #93         // номер функции Linux для выхода из программы - 93
    svc 0               // вызываем функцию и выходим из программы

Здесь опять же начинаем с того, что сравниваем Х0 и Х1 - выполняем установку флагов для проверки первого условия:

cmp x0, x1

Но теперь, если Х0 == Х1, то сравнивать Х2 и Х3 не имеет смысла. Поэтому выполняем следующую инструкцию CCMP:

ccmp x2, x3, 8, NE

В качестве условия инструкции передается NE - то есть первое условие должно быть ложно (Х0 != Х1). И если только первое условие ложно, инструкция CCMP сравнивает значения регистров Х2 и Х3.

Если же первое условие истинно, то есть условие NE не выполняется, то нам надо опять же определенным образом установить флаги. Далее у нас идет проверка условия LT (второго условия) и переход к метке true, если это условие истинно:

b.lt true

И если первое условие истинно (X0 == X1), и соответственно CCMP регистры Х2 и Х3 не проверяет и флаги для второго условия не устанавливает, то нам надо передать флагам такие значения, при которых второе условие (LT) также будет истинно. А это опять же неравенство флагов N и V. Поэтому третьему операнду инструкции CCMP передаем число 8:

ccmp x2, x3, 8, NE

Число 8 в двоичном виде равно 1000, поэтому флаги будут установлены следующим образом: N=1, Z=0, C=0, V=0. То есть в данном случае мы видим, что флаги N и V не равны, поэтому условие LT (X2 < X3) будет истинным, и будет происходить переход к метке true

CCMN

Аналогичным образом можно применять инструкцию CCMN за тем исключением, что для проверки первого условия применяется не CMP, а инструкция CMN

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