内置pcmpistri不在gcc工作

时间:2017-05-16 18:02:59

标签: c gcc intrinsics sse4

我正在尝试编写一个 strcmp 版本,该版本利用了利用GCC内在函数的SSE4.2新指令。

这是我到目前为止的代码:

#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

根据Wikibooks,如果输出索引的指令(就像我试图使用的pcmpistri那样),结果会保存在ECX寄存器中,但是,如果我记得的话正确地,紧跟在pcmpistri之后的指令覆盖了对EAX注册的指令!

我认为这可能是让我疯狂的错误,但我没有集会经验,我可能错了。

是否有人遇到此问题?有谁知道如何解决这个问题?

我尝试使用GCC 5.4和6.2在Ubuntu 16.04(实际上是Windows上的bash)上使用-O0-O1-O2(显然是-msse4.2

是什么让我觉得这是一个GCC错误,从Visual Studio 2017编译的undex MSVC的类似代码可以正常工作:

#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;
}

1 个答案:

答案 0 :(得分:0)

Yo可能会惊讶地发现,实际上反汇编代码以相反的顺序(即从左到右)呈现每个指令的参数列表。因此,“ move%ecx,%eax”实际上是“ MOV eax,ecx”!只需在“指令级别”中以调试模式逐步运行代码并跟踪寄存器更改。