Для сравнения чисел с плавающей точкой расширения SSE/AVX предоставляют инструкции (v)cmpps
(сравнивает числа одинарной точности) и (v)cmppd
(для чисел двойной точности):
cmpps xmmdest, xmmsrc/mem128, imm8 vcmpps xmmdest, xmmsrc1, xmmsrc2/mem128, imm8 vcmpps ymmdest, ymmsrc1, ymmsrc2/mem256, imm8 cmppd xmmdest, xmmsrc/mem128, imm8 vcmppd xmmdest, xmmsrc1, xmmsrc2/mem128, imm8 vcmppd ymmdest, ymmsrc1, ymmsrc2/mem256, imm8
Стоит отметить, что сравнения могут быть упорядоченными (ordered) и неупорядоченными (unordered). При неупорядоченном сравнении как минимум один из операндов представляет значение NaN. Упорядоченное сравнение, наоборот, - это такое сравнение, где ни один из операндов не является NaN
.
Кроме того, сравнения могут быть сигнальные (signaling) и тихие (quiet). Сигнальные сравнения генерируют исключение недопустимой арифметической операции (IA), если результатом операцим является NaN. Тихие сравнения не вызывают исключения и отражают только состояние в регистре MXCSR.
Последний операнд инструкций - imm8
указывает на тип сравнения.
00h
: проверяет операнды на равенство, если они равны, устанавливает для всех битов дорожки 1, иначе устанавливает 0, (сравнение упорядоченное, тихое)
01h
: если первый операнд меньше второго, устанавливает 1 (сравнение упорядоченное, сигнальное)
02h
: если первый операнд меньше или равен второму, устанавливает 1 (сравнение упорядоченное, сигнальное)
03h
: устанавливает 1, если сравенение неупорядоченное (сравнение неупорядоченное, тихое)
04h
: если операнды не равны, устанавливает 1 (сравнение неупорядоченное, тихое)
05h
: если первый операнд НЕ меньше второго, устанавливает 1 (сравнение неупорядоченное, сигнальное)
06h
: если первый операнд НЕ меньше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, сигнальное)
07h
: устанавливает 1, если сравенение упорядоченное (сравнение упорядоченное, тихое)
08h
: если операнды равны, устанавливает 1 (сравнение неупорядоченное, тихое)
09h
: если первый операнд НЕ больше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, сигнальное)
0Ah
: если первый операнд НЕ больше второго, устанавливает 1 (сравнение неупорядоченное, сигнальное)
0Bh
: возвращает 0, если сравнение упорядоченное (сравнение упорядоченное, тихое)
0Ch
: если операнды не равны, устанавливает 1 (сравнение упорядоченное, тихое)
0Dh
: если первый операнд больше или равен второму, устанавливает 1 (сравнение упорядоченное, сигнальное)
0Eh
: если первый операнд больше второго, устанавливает 1 (сравнение упорядоченное, сигнальное)
0Fh
: устанавливает 1, если сравнение неупорядоченное (сравнение неупорядоченное, тихое)
10h
: если операнды равны, устанавливает 1 (сравнение упорядоченное, сигнальное)
11h
: если первый операнд меньше второго, устанавливает 1 (сравнение упорядоченное, тихое)
12h
: если первый операнд меньше или равен второму, устанавливает 1 (сравнение упорядоченное, тихое)
13h
: устанавливает 1, если сравнение неупорядоченное (сравнение неупорядоченное, сигнальное)
14h
: если операнды не равны, устанавливает 1 (сравнение неупорядоченное, сигнальное)
15h
: если первый операнд НЕ меньше второго, устанавливает 1 (сравнение неупорядоченное, тихое)
16h
: если первый операнд НЕ меньше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, тихое)
17h
: устанавливает 1, если сравнение упорядоченное (сравнение упорядоченное, сигнальное)
18h
: если операнды равны, устанавливает 1 (сравнение неупорядоченное, сигнальное)
19h
: если первый операнд НЕ больше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, тихое)
1Ah
: если первый операнд НЕ больше второго, устанавливает 1 (сравнение неупорядоченное, тихое)
1Bh
: возвращает 0, если сравнение упорядоченное (сравнение упорядоченное, сигнальное)
1Ch
: если операнды не равны, устанавливает 1 (сравнение упорядоченное, сигнальное)
1Dh
: если первый операнд больше или равен второму, устанавливает 1 (сравнение упорядоченное, тихое)
1Eh
: если первый операнд больше второго, устанавливает 1 (сравнение упорядоченное, тихое)
1Fh
: устанавливает 1, если сравнение неупорядоченное, (сравнение неупорядоченное, сигнальное)
Если условие верно, то в соответствующей 32- или 64-битной дорожке XMM0 все биты устанавливаются в 1, если условие не верно, то все биты равны 0.
Например, сравнивним два вектора чисел real4:
.data nums0 real4 0.1, 1.2, 2.1, 3.2 nums1 real4 0.1, 1.1, 2.1, 3.1 .code main proc movaps xmm0, nums0 movaps xmm1, nums1 cmpps xmm0, xmm1, 0 ;XMM0 = -1, 0, -1, 0 ret main endp end
Здесь сравниваем векторы real4 из регистров XMM0 и XMM1. Поскольку инструкции cmpps
в качестве третьего операнда передается число 0 (или 00h
), то инструкция
проверяет числа в сооветствующих дорожках регистров на равенство. То есть единовременно сравниваем 4 числа из XMM0 с 4 числами из XMM1. Результат сравнения помещается в XMM0 и он будет представлять
вектор чисел [-1, 0, -1, 0]
. То есть первые и третьи 32-битные дорожки регистров равны, а вторые и четвертые дорожки не равны.
Для ряда случаев есть синонимичные инструкции, которые упрощают понимание кода:
cmpeqps x1, x2
: синоним для cmpps x1, x2, 0
cmpeqpd x1, x2
: синоним для cmppd x1, x2, 0
cmpltps x1, x2
: синоним для cmpps x1, x2, 1
cmpltpd x1, x2
: синоним для cmppd x1, x2, 1
cmpleps x1, x2
: синоним для cmpps x1, x2, 2
cmplepd x1, x2
: синоним для cmppd x1, x2, 2
cmpunordps x1, x2
: синоним для cmpps x1, x2, 3
cmpunordpd x1, x2
: синоним для cmppd x1, x2, 3
cmpneqps x1, x2
: синоним для cmpps x1, x2, 4
cmpneqpd x1, x2
: синоним для cmppd x1, x2, 4
cmpnltps x1, x2
: синоним для cmpps x1, x2, 5
cmpnltpd x1, x2
: синоним для cmppd x1, x2, 5
cmpnleps x1, x2
: синоним для cmpps x1, x2, 6
cmpnlepd x1, x2
: синоним для cmppd x1, x2, 6
cmpordps x1, x2
: синоним для cmpps x1, x2, 7
cmpordpd x1, x2
: синоним для cmppd x1, x2, 7
Так, примеры выше мы могли бы переписать так:
.data nums0 real4 0.1, 1.2, 2.1, 3.2 nums1 real4 0.1, 1.1, 2.1, 3.1 .code main proc movaps xmm0, nums0 movaps xmm1, nums1 cmpeqps xmm0, xmm1 ;XMM0 = -1, 0, -1, 0 ret main endp end
Версии AVX этих инструкций принимают 4 операнда: целевой регистр XMM или YMM для хранения результатов сравнения, два сравниваемых операнда и операнд imm8, который указывает на тип сравнения:
vcmpps xmmdest, xmmsrc1, xmmsrc2/mem128, imm8 vcmpps ymmdest, ymmsrc1, ymmsrc2/mem256, imm8 vcmppd xmmdest, xmmsrc1, xmmsrc2/mem128, imm8 vcmppd ymmdest, ymmsrc1, ymmsrc2/mem256, imm8
В зависимости от значения четверного операнда - imm8 для этих инструкций есть синонимичные инструкций:
imm8 | Инструкция |
00h |
|
01h |
|
02h |
|
03h |
|
04h |
|
05h |
|
06h |
|
07h |
|
08h |
|
09h |
|
0Ah |
|
0Bh |
|
0Ch |
|
0Dh |
|
0Eh |
|
0Fh |
|
10h |
|
11h |
|
12h |
|
13h |
|
14h |
|
15h |
|
16h |
|
17h |
|
18h |
|
19h |
|
1Ah |
|
1Bh |
|
1Ch |
|
1Dh |
|
1Eh |
|
1Fh |
|
Инструкции movmskps и movmskpd извлекают знаковые биты 32- и 64-битных дорожек регистров XMM и YMM соответственно и сохраняют эти биты в младших 4 или 8 битах регистра общего назначения.
movmskps reg, xmmsrc movmskpd reg, xmmsrc vmovmskps reg, ymmsrc vmovmskpd reg, ymmsrc
Первый операнд представляет 32- или 64-битный регистр общего назначения. Второй операнд представляет регистр XMM или YMM с результатами сравнения.
Инструкция movmskps
копирует знаковые биты из четырех дорожек регистра XMM в младшие 4 бита регистра общего назначения
Инструкция movmskpd
копирует знаковые биты из четырех дорожек регистра XMM в биты 0 и 1 регистра общего назначения.
Инструкция vmovmskps
копирует знаковые биты из четырех дорожек регистра XMM или из восьми дорожек регистра YMM в малдшие 4 и 8 битов
регистра назначения соответственно.
Инструкция vmovmskpd
копирует знаковые биты из четырех дорожек регистра YMM в младшие 4 биты регистра общего назначения.
Стоит отметить, что хотя эти инструкции ориентированы прежде всего на сравнение чисел с плавающей точкой - real4 и real8, но их также можно использовать для получения результатов сравнения целых чисел (movmskps и vmovmskps) и 64-битных целых чисел (movmskpd и vmovmskpd). Например:
.data nums0 qword 23, 55 nums1 qword 22, 55 .code main proc movapd xmm0, nums0 movapd xmm1, nums1 cmpeqpd xmm0, xmm1 ; XMM0 = XMM0 == XMM1 (XMM0 = -1, 0) movmskpd rax, xmm0 ; RAX = 2 (010b - в двоичной системе) ret main endp end
В данном случае проверяются на равенство элементы двух векторов четверных слов, и с помощью инструкции movmskpd
результаты сравнения помещаются в регистр RAX.
Поскольку сравнивают два значения, то в регистре RAX результаты будут помещаться в два первых бита. В итоге в RAX будет число 2 или 10b в бинарной системе. Первый бит равен 0,
так как числа первых дорожек векторов не равны. Второй бит равен 1, так как вторые дорожки векторов равны.
Получив результаты сравнения в регистр общего назначения, очень удобно использовать данный регистр для условной обработки:
.data nums0 qword 23, 55 nums1 qword 22, 55 .code main proc movapd xmm0, nums0 movapd xmm1, nums1 cmpeqpd xmm0, xmm1 ; XMM0 = XMM0 == XMM1 movmskpd rax, xmm0 ; RAX = 2 (010b - в двоичной системе) lea rcx, jmpTable ; загружаем адрес таблицы jmpTable jmp qword ptr [rcx][rax*8] ; в зависимости от результата переходим к определенной метке jmpTable qword nene ; обе пары чисел не равны qword neeq ; в первой паре числа равны, во второй не равны qword eqne ; во второй паре числа равны, первой не равны qword eqeq ; обе пары чисел равны nene: mov rax, 4 jmp exit neeq: mov rax, 8 jmp exit eqne: mov rax, 16 jmp exit eqeq: mov rax, 32 jmp exit exit: ret main endp end
Так как инструкция movmskpd
копирует 2 бита из XMM0 в RAX, можно использовать RAX в качестве индекса в таблице переходов для выбора одной из четырех разных меток.
Код с меткой nene выполняется, если в обоих парах числа не равны. Метка neeq определяет код, если в первой паре числа равны, во второй не равны.
Метка eqne определяет код для ситуации, если во второй паре числа равны, а в первой не равны. И метка eqeq определяет код для ситуации, когда оба набора дорожек содержат одинаковые значения.