встроенный pcmpistri не работает в gcc

Я пытаюсь написать версию strcmp, в которой используются новые инструкции SSE4.2, использующие встроенные функции GCC.

Это код, который у меня есть до сих пор:

#include <stdio.h>
#include <smmintrin.h>

int main(int argc, char const *argv[])
{
    int n;
    const char str1[16] = "foo bar";
    const char str2[16] = "foo quxx";

    /* Safety check for SSE4.2 support */
    __builtin_cpu_init();
    if(__builtin_cpu_supports("sse4.2"))
        puts("Ok SSE4.2");
    else
    {
        puts("Nok SSE4.2");
        return -__LINE__;
    }

    /* Load strings into registers */
    __v16qi xmm1 = __builtin_ia32_loaddqu(str1);
    __v16qi xmm2 = __builtin_ia32_loaddqu(str2);

    /* Print to check registers were loaded correctly */
    printf("xmm1: %s\nxmm2: %s\n", (const char *) &xmm1, (const char *) &xmm2);

    /*  Perform compare */
    n = __builtin_ia32_pcmpistri128(xmm1, xmm2, (_SIDD_CMP_EQUAL_EACH | _SIDD_LEAST_SIGNIFICANT));

    /* Print result */
    printf("n: %d\n", n);

    return 0;
}

Он должен печатать индекс первого другого байта, но вместо этого всегда печатает 0.

Я пытался отладить его часами, пока не увидел это в сгенерированной сборке:

call    printf
movdqa  -64(%rbp), %xmm1
movdqa  -80(%rbp), %xmm0
pcmpistri   $8, %xmm1, %xmm0
movl    %ecx, %eax
pcmpistrm   $8, %xmm1, %xmm0
movl    %eax, -84(%rbp)
movl    -84(%rbp), %eax

Согласно Викиучебникам в случае инструкций, выводящих индекс (так же, как pcmpistri I пытаюсь использовать) результат сохраняется в регистре ECX, но, если я правильно помню, инструкция сразу после pcmpistri переопределяет этот регистр с EAX!

Я думаю, что это может быть ошибка, которая сводит меня с ума, но у меня нет опыта сборки, и я, вероятно, ошибаюсь.

Кто-нибудь испытывает эту проблему? Кто-нибудь знает, как это решить?

Я пробовал с GCC 5.4 и 6.2 под Ubuntu 16.04 (на самом деле, bash в Windows) с -O0, -O1 и -O2 (и, очевидно, -msse4.2).

Что заставляет меня думать, что это ошибка GCC, так это то, что аналогичный код, скомпилированный undex MSVC из Visual Studio 2017, работает правильно:

#include <stdio.h>
#include <nmmintrin.h>


int main()
{
    __m128i a, b;

    const int mode = _SIDD_CMP_EQUAL_EACH | _SIDD_LEAST_SIGNIFICANT;

    a.m128i_u16[7] = 0xFFFF;
    a.m128i_u16[6] = 0xFFFF;
    a.m128i_u16[5] = 0xFFFF;
    a.m128i_u16[4] = 0xFFFF;
    a.m128i_u16[3] = 0xFFFF;
    a.m128i_u16[2] = 0xFFFF;
    a.m128i_u16[1] = 0x0001;
    a.m128i_u16[0] = 0xFFFF;

    b.m128i_u16[7] = 0x0001;
    b.m128i_u16[6] = 0x0001;
    b.m128i_u16[5] = 0x0001;
    b.m128i_u16[4] = 0x0001;
    b.m128i_u16[3] = 0x0001;
    b.m128i_u16[2] = 0x0001;
    b.m128i_u16[1] = 0x0001;
    b.m128i_u16[0] = 0x0001;

    int returnValue = _mm_cmpistri(a, b, mode);
    printf_s("%i\n", returnValue);

    return 0;
}

person Samuele Pilleri    schedule 16.05.2017    source источник
comment
Примечание: return -__LINE__ — очень плохая идея! POSIX гарантирует только диапазон signed char для результата main.   -  person too honest for this site    schedule 16.05.2017
comment
Почему вы используете встроенные функции __builtin_xxx с gcc? Почему вы не используете стандартные встроенные функции, т.е. те же самые, которые вы используете в версии кода MSVC?   -  person Paul R    schedule 16.05.2017
comment
Подсистемы @Olaf NT представляют собой интерфейс между ядром и пользовательским пространством. Я понимаю, что это не ядро ​​Linux, но оно спроектировано так, чтобы быть как можно более похожим, и оно хорошо работает для целей этого проекта (который не включает какое-либо серьезное взаимодействие с пространством ядра). @PaulR Я действительно нашел их на Официальная страница GCC, вы имеете в виду _mm_cmpistri? На самом деле я не смог найти стандартную версию для __builtin_ia32_loaddqu, и разные встроенные функции плохо сочетаются друг с другом.   -  person Samuele Pilleri    schedule 16.05.2017
comment
@SamuelePilleri: см. _mm_lddqu_si128. В целом встроенные функции, приведенные в руководстве по встроенным функциям Intel, работают для любого компилятора, поддерживающего SSE/ AVX/и т. д. (gcc, clang, ICC, MSVC и т. д.).   -  person Paul R    schedule 17.05.2017


Ответы (1)


Вы можете быть удивлены, обнаружив, что на самом деле код дизассемблирования представляет список аргументов каждой инструкции в обратном порядке, то есть слева направо. Итак, «movl %ecx, %eax» на самом деле «MOV eax, ecx»! Просто запустите свой код в режиме отладки шаг за шагом на уровне инструкций и отследите изменения регистра.

person user12987330    schedule 01.03.2020