Я пытаюсь написать версию 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;
}
return -__LINE__
— очень плохая идея! POSIX гарантирует только диапазонsigned char
для результатаmain
. - person too honest for this site   schedule 16.05.2017__builtin_xxx
с gcc? Почему вы не используете стандартные встроенные функции, т.е. те же самые, которые вы используете в версии кода MSVC? - person Paul R   schedule 16.05.2017_mm_cmpistri
? На самом деле я не смог найти стандартную версию для__builtin_ia32_loaddqu
, и разные встроенные функции плохо сочетаются друг с другом. - person Samuele Pilleri   schedule 16.05.2017_mm_lddqu_si128
. В целом встроенные функции, приведенные в руководстве по встроенным функциям Intel, работают для любого компилятора, поддерживающего SSE/ AVX/и т. д. (gcc, clang, ICC, MSVC и т. д.). - person Paul R   schedule 17.05.2017