Ответ 1
Вы увидите проблемы segfault, если переменные не выровнены по 16 байт. ЦП не может MOVDQA в/из неаудированных адресов памяти и будет генерировать "исключение GP" на уровне процессора, предлагая ОС segfault your приложение.
C переменные, которые вы объявляете (стек, глобальные) или выделяете в куче, обычно не привязаны к границе 16 байтов, хотя иногда вы можете получить выровненный по одному. Вы можете направить компилятор для обеспечения правильного выравнивания с использованием типов данных __m128 или __m128i. Каждый из них объявляет правильно выровненное 128-битное значение.
Далее, прочитав objdump, похоже, что компилятор завернул последовательность asm с кодом, чтобы скопировать операнды из стека в регистры xmm2 и xmm3, используя инструкцию MOVQ, только чтобы ваш код asm затем скопировал значения в xmm0 и xmm1. После xor-ing в xmm0 оболочка копирует результат в xmm2, а затем копирует его обратно в стек. В целом, не очень эффективно. MOVQ копирует 8 байтов за раз, и ожидает (при некоторых обстоятельствах) 8-байтового выровненного адреса. Получив неравномерный адрес, он может потерпеть неудачу, как MOVDQA. Однако код-оболочка добавляет выровненное смещение (-0x80, -0x88 и later -0x78) в регистр ВР, который может содержать или не содержать выровненное значение. В целом, нет никакой гарантии выравнивания в сгенерированном коде.
Следующее гарантирует, что аргументы и результат сохраняются в правильно выровненных ячейках памяти и, кажется, работают нормально:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *v64 = (int64_t*) &value;
printf("%.16llx %.16llx\n", v64[1], v64[0]);
}
void main() {
__m128i a = _mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00), /* low dword first! */
b = _mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff),
x;
asm (
"movdqa %1, %%xmm0;" /* xmm0 <- a */
"movdqa %2, %%xmm1;" /* xmm1 <- b */
"pxor %%xmm1, %%xmm0;" /* xmm0 <- xmm0 xor xmm1 */
"movdqa %%xmm0, %0;" /* x <- xmm0 */
:"=x"(x) /* output operand, %0 */
:"x"(a), "x"(b) /* input operands, %1, %2 */
:"%xmm0","%xmm1" /* clobbered registers */
);
/* printf the arguments and result as 2 64-bit hex values */
print128(a);
print128(b);
print128(x);
}
скомпилировать с (gcc, ubuntu 32 бит)
gcc -msse2 -o app app.c
выход:
10ffff0000ffff00 00ffff0000ffff00
0000ffff0000ffff 0000ffff0000ffff
10ff00ff00ff00ff 00ff00ff00ff00ff
В приведенном выше коде _mm_setr_epi32 используется для инициализации a и b со 128-битными значениями, поскольку компилятор может не поддерживать 128 целых литералов.
print128 записывает шестнадцатеричное представление целочисленного 128-битного числа, поскольку printf не может этого сделать.
Ниже приведено краткое и позволяет избежать дублирования копирования. Компилятор добавляет скрытую оболочку movdqa, чтобы сделать magor% 2,% 0 волшебным образом работать без необходимости загружать регистры самостоятельно:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *px = (int64_t*) &value;
printf("%.16llx %.16llx\n", px[1], px[0]);
}
void main() {
__m128i a = _mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00),
b = _mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff);
asm (
"pxor %2, %0;" /* a <- b xor a */
:"=x"(a) /* output operand, %0 */
:"x"(a), "x"(b) /* input operands, %1, %2 */
);
print128(a);
}
скомпилировать по-прежнему:
gcc -msse2 -o app app.c
выход:
10ff00ff00ff00ff 00ff00ff00ff00ff
В качестве альтернативы, если вы хотите избежать встроенной сборки, вы можете использовать SSE intrinsics вместо (PDF). Это встроенные функции/макросы, которые инкапсулируют инструкции MMX/SSE синтаксисом типа C. _mm_xor_si128 уменьшает вашу задачу до одного вызова:
#include <stdio.h>
#include <emmintrin.h>
void print128(__m128i value) {
int64_t *v64 = (int64_t*) &value;
printf("%.16llx %.16llx\n", v64[1], v64[0]);
}
void main()
{
__m128i x = _mm_xor_si128(
_mm_setr_epi32(0x00ffff00, 0x00ffff00, 0x00ffff00, 0x10ffff00), /* low dword first !*/
_mm_setr_epi32(0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff));
print128(x);
}
компиляции:
gcc -msse2 -o app app.c
выход:
10ff00ff00ff00ff 00ff00ff00ff00ff