Расширения SSE/AVX предоставляют инструкции для паралелльных операций с плавающей точкой:
addps
: складывает 4 дорожки операндов с 32-разрядными числами с плавающей точкой
addpd
: складывает 2 дорожки операндов с 64-разрядными числами с плавающей точкой
vaddps
: складывает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой
vaddpd
: складывает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой
subps
: вычитает 4 дорожки операндов с 32-разрядными числами с плавающей точкой
subpd
: вычитает 2 дорожки операндов с 64-разрядными числами с плавающей точкой
vsubps
: вычитает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой
vsubpd
: вычитает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой
mulps
: перемножает 4 дорожки операндов с 32-разрядными числами с плавающей точкой
mulpd
: перемножает 2 дорожки операндов с 64-разрядными числами с плавающей точкой
vmulps
: перемножает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой
vmulpd
: перемножает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой
divps
: делит 4 дорожки операндов с 32-разрядными числами с плавающей точкой
divpd
: делит 2 дорожки операндов с 64-разрядными числами с плавающей точкой
vdivps
: делит 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой
vdivpd
: делит 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой
maxps
: вычисляет максимальное в каждой из 4 пар чисел с плавающей точкой одинарной точности
maxpd
: вычисляет максимальное в каждой из 2 пар чисел с плавающей точкой двойной точности
vmaxps
: вычисляет максимальное в каждой из 4 (для 128-битной версии) или 8 (для 256-битной версии) пар чисел с плавающей точкой одинарной точности
vmaxpd
: вычисляет максимальное в каждой из 2 (для 128-битной версии) или 4 (для 256-битной версии) пар чисел с плавающей точкой двойной точности
minps
: вычисляет минимальное в каждой из 4 пар чисел с плавающей точкой одинарной точности
minpd
: вычисляет минимальное в каждой из 2 пар чисел с плавающей точкой двойной точности
vminps
: вычисляет минимальное в каждой из 4 (для 128-битной версии) или 8 (для 256-битной версии) пар чисел с плавающей точкой одинарной точности
vminpd
: вычисляет минимальное в каждой из 2 (для 128-битной версии) или 4 (для 256-битной версии) пар чисел с плавающей точкой двойной точности
sqrtps
: вычисляет квадратный корень для каждого из 4 чисел с плавающей точкой одинарной точности
sqrtpd
: вычисляет квадратный корень для 2 чисел с плавающей точкой двойной точности
vsqrtps
: вычисляет квадратный корень для каждого из 4 (для 128-битной версии) или 8 (для 256-битной версии) чисел с плавающей точкой одинарной точности
vsqrtpd
: вычисляет квадратный корень для каждого из 2 (для 128-битной версии) или 4 (для 256-битной версии) чисел с плавающей точкой двойной точности
rsqrtps
: вычисляет приблизительно обратный квадратный корень для каждого из 4 чисел с плавающей точкой одинарной точности
vrsqrtps
: вычисляет приблизительно обратный квадратный корень для каждого из 4 (для 128-битной версии) или 8 (для 256-битной версии) чисел с плавающей точкой одинарной точности
Общий синтаксис инструкций на примере v(addps)
:
addps xmmdest, xmmsrc/mem128 vaddps xmmdest, xmmsrc1, xmmsrc2/mem128 vaddps ymmdest, ymmsrc1, ymmsrc2/mem256
Инструкции вычисления квадратного корня принимают один операнд:
sqrtps xmmdest, xmmsrc/mem128 vsqrtps xmmdest, xmmsrc/mem128 vsqrtps ymmdest, ymmsrc/mem256
Применение инструкций на примере сложения на Linux:
global main extern printf section .data nums0 dd 1.1, 2.2, 3.3, 4.4 nums1 dd 1.2, 2.3, 3.4, 4.5 format_str db "%.1f, %.1f, %.1f, %.1f", 10, 0 section .text main: sub rsp, 8 ; выравниваение по 16 байтам для функции printf movaps xmm0, [nums0] movaps xmm1, [nums1] vaddps xmm5, xmm0, xmm1 ; xmm5 = xmm0+xmm1 ; выводим данные на консоль movss xmm0, xmm5 ; помещаем число dword в регистр для передачи в функцию printf cvtss2sd xmm0, xmm0 ; преобразуем dword в qword (double) psrldq xmm5, 4 ; сдвиг вправо на 4 байта для получения следующего числа movss xmm1, xmm5 cvtss2sd xmm1, xmm1 psrldq xmm5, 4 movss xmm2, xmm5 cvtss2sd xmm2, xmm2 psrldq xmm5, 4 movss xmm3, xmm5 cvtss2sd xmm3, xmm3 mov rdi, format_str call printf add rsp, 8 ret
Здесь складываются два вектора 32-разрядных чисел nums0 и nums1. Для упрощения тестирования для вывода результата на консоль здесь используется функция printf
. Компиляция и консольный вывод программы:
root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o root@Eugene:~/asm# gcc -static hello.o -o hello root@Eugene:~/asm# ./hello 2.3, 4.5, 6.7, 8.9 root@Eugene:~/asm#
Аналогичный пример на Windows:
global main extern printf section .data nums0 dd 1.1, 2.2, 3.3, 4.4 nums1 dd 1.2, 2.3, 3.4, 4.5 format_str db "%.1f, %.1f, %.1f, %.1f", 10, 0 section .text main: sub rsp, 40 ; выравниваение по 16 байтам для функции printf movaps xmm0, [rel nums0] movaps xmm1, [rel nums1] vaddps xmm5, xmm0, xmm1 ; xmm5 = xmm0+xmm1 ; выводим данные на консоль movss xmm1, xmm5 cvtss2sd xmm1, xmm1 movq rdx, xmm1 psrldq xmm5, 4 movss xmm2, xmm5 cvtss2sd xmm2, xmm2 movq r8, xmm2 psrldq xmm5, 4 movss xmm3, xmm5 cvtss2sd xmm3, xmm3 movq r9, xmm2 psrldq xmm5, 4 movss xmm4, xmm5 cvtss2sd xmm4, xmm4 movq [rsp+32], xmm4 mov rcx, format_str call printf add rsp, 40 ret