【问题标题】:builtin pcmpistri not working in gcc内置 pcmpistri 在 gcc 中不起作用
【发布时间】:2017-10-15 22:54:34
【问题描述】:

我正在尝试编写一个 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 注册的!

我认为这可能是让我发疯的错误,但我没有组装经验,我可能错了。

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

我已经在 Ubuntu 16.04(实际上是 Windows 上的 bash)下使用 -O0-O1-O2(显然是 -msse4.2)尝试使用 GCC 5.4 和 6.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;
}

【问题讨论】:

  • 注意:return -__LINE__ 是一个非常糟糕的主意! POSIX 仅保证signed char 的结果范围为main
  • 为什么要使用 __builtin_xxx 内部函数和 gcc ?你为什么不使用标准的内在函数,即你在 MSVC 版本的代码中使用的那些?
  • @Olaf NT 子系统是内核和用户空间之间的接口。我知道它不是 Linux 内核,但它被设计为尽可能地相似,并且对于这个“项目”(不包括任何主要的内核空间交互)来说效果很好。 @PaulR 我实际上在GCC official page 上找到了那些,你是说_mm_cmpistri?我实际上找不到__builtin_ia32_loaddqu 的“标准”版本,而且不同的内在函数不能很好地相互配合。
  • @SamuelePilleri:见_mm_lddqu_si128。一般来说,Intel intrinsics guide 中给出的内在函数适用于任何支持 SSE/AVX/etc(gcc、clang、ICC、MSVC 等)的编译器。

标签: c gcc intrinsics att sse4


【解决方案1】:

你可能会惊讶地发现实际上反汇编代码以相反的顺序呈现每条指令的参数列表,即从左到右。所以 "movl %ecx, %eax" 实际上是 "MOV eax, ecx" !只需在指令级逐步在调试模式下运行您的代码并跟踪寄存器更改。

【讨论】:

    猜你喜欢
    • 2015-04-03
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    • 2015-03-16
    相关资源
    最近更新 更多