как работать с 128 битами C переменная и xmm 128 битами asm?

в gcc я хочу сделать 128 битов xor с 2 переменными C, с помощью кода asm: как?

asm (
    "movdqa %1, %%xmm1;"
    "movdqa %0, %%xmm0;"
     "pxor %%xmm1,%%xmm0;"
     "movdqa %%xmm0, %0;"

    :"=x"(buff) /* output operand */
    :"x"(bu), "x"(buff)
    :"%xmm0","%xmm1"
    );

но у меня есть ошибка отказа Сегментации; это - вывод objdump:

movq   -0x80(%rbp),%xmm2

movq   -0x88(%rbp),%xmm3

movdqa %xmm2,%xmm1

movdqa %xmm2,%xmm0

pxor   %xmm1,%xmm0

movdqa %xmm0,%xmm2

movq   %xmm2,-0x78(%rbp)
10
задан hivert 13 March 2014 в 23:30
поделиться

2 ответа

Ммм, почему бы не использовать __builtin_ia32_pxor intrinsic?

1
ответ дан 3 December 2019 в 18:33
поделиться

Если переменные не выровнены на 16 байт, то могут возникнуть проблемы с выравниванием. ЦПУ не может MOVDQA выровнять/от выровненных адресов памяти и сгенерирует "GP-исключение" на уровне процессора, что подтолкнет операционную систему к сегрегации вашего приложения.

C переменные, которые вы объявляете (стековые, глобальные) или выделяете на куче, обычно не выровнены по границе 16 байт, хотя иногда вы можете получить выровненную по случайности. Вы можете направить компилятор на обеспечение правильного выравнивания, используя типы данных __m128 или __m128i. Каждый из них объявляет правильно выровненное 128-битное значение.

Далее, читая объектный дамп, кажется, что компилятор обернул последовательность asm кодом для копирования операндов из стека в регистры xmm2 и xmm3, используя инструкцию MOVQ, только для того, чтобы ваш asm код затем скопировал значения в регистры xmm0 и xmm1. После xor-ing в xmm0, обёртка копирует результат в xmm2 только для того, чтобы затем скопировать его обратно в стек. В целом, не очень эффективно. MOVQ копирует 8 байт за раз и ожидает (при некоторых обстоятельствах) 8-байтового выровненного адреса . Получив выровненный адрес, он может выйти из строя так же, как и MOVDQA. Однако код обертки добавляет в регистр BP выровненное смещение (-0x80, -0x88, и позднее -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 bit)

gcc -msse2 -o app app.c

output:

10ffff0000ffff00 00ffff0000ffff00
0000ffff0000ffff 0000ffff0000ffff
10ff00ff00ff00ff 00ff00ff00ff00ff

В приведенном выше коде для инициализации a и b 128-битными значениями используется _mm_setr_epi32, так как компилятор может не поддерживать 128 целочисленных литералов.

print128 записывает шестнадцатеричное представление 128-битного целого числа, так как printf может оказаться не в состоянии это сделать.


Ниже приведена более короткая информация, которая позволяет избежать дублирования при копировании. Компилятор добавляет свои movdqa со скрытой обёрткой, чтобы заставить pxor %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

output:

10ff00ff00ff00ff 00ff00ff00ff00ff

Альтернативно, если вы хотите избежать встроенной сборки, вы можете использовать SSE intrinsics вместо (PDF). Это встроенные функции/макросы, которые инкапсулируют MMX/SSE инструкции с С-подобным синтаксисом. _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
19
ответ дан 3 December 2019 в 18:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: