10

在 gcc 中,我想通过 asm 代码对 2 个 C 变量进行 128 位 xor:如何?

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)
4

3 回答 3

19

如果变量不是 16 字节对齐的,您会看到段错误问题。CPU 无法在未对齐的内存地址之间进行 MOVDQA,并且会生成处理器级别的“GP 异常”,提示操作系统对您的应用程序进行段错误。

您在堆上声明(堆栈、全局)或分配的 C 变量通常不会与 16 字节边界对齐,尽管偶尔您可能会偶然得到一个对齐的边界。您可以使用 __m128 或 __m128i 数据类型指示编译器确保正确对齐。每一个都声明了一个正确对齐的 128 位值。

此外,阅读 objdump,看起来编译器用代码包装了 asm 序列,以使用 MOVQ 指令将操作数从堆栈复制到 xmm2 和 xmm3 寄存器,然后让您的 asm 代码将值复制到 xmm0 和 xmm1。在对 xmm0 进行异或运算后,包装器仅将结果复制到 xmm2,然后再将其复制回堆栈。总的来说,效率不是很高。MOVQ 一次复制 8 个字节,并期望(在某些情况下)一个 8 字节对齐的地址。获取未对齐的地址,它可能会像 MOVDQA 一样失败。然而,包装器代码将对齐的偏移量(-0x80、-0x88 和后来的 -0x78)添加到 BP 寄存器,它可能包含也可能不包含对齐值。总的来说,生成的代码中没有对齐的保证。

以下确保参数和结果存储在正确对齐的内存位置,并且似乎工作正常:

#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 位) 编译

gcc -msse2 -o app app.c

输出:

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

在上面的代码中,_mm_setr_epi32 用于用 128 位值初始化ab,因为编译器可能不支持 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

输出:

10ff00ff00ff00ff 00ff00ff00ff00ff

或者,如果您想避免使用内联程序集,您可以改用SSE 内在函数(PDF)。这些是内联函数/宏,它们使用类似 C 的语法封装 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
于 2010-01-02T06:56:00.837 回答
2

嗯,为什么不使用__builtin_ia32_pxor内在函数?

于 2010-01-02T06:41:30.553 回答
1

在后期模型 gcc(我的是 4.5.5)下,选项 -O2 或更高版本意味着-fstrict-aliasing导致上面给出的代码抱怨:

supersuds.cpp:31: warning: dereferencing pointer ‘v64’ does break strict-aliasing rules
supersuds.cpp:30: note: initialized from here

这可以通过提供额外的类型属性来解决,如下所示:

typedef int64_t __attribute__((__may_alias__)) alias_int64_t; 
void print128(__m128i value) {
    alias_int64_t *v64 = (int64_t*)  &value;
    printf("%.16lx %.16lx\n", v64[1], v64[0]); 
}

我首先直接尝试了没有typedef的属性。它被接受了,但我仍然收到警告。typedef 似乎是魔法的必要组成部分。

顺便说一句,这是我在这里的第二个答案,我仍然讨厌我还不能告诉我被允许在哪里编辑的事实,所以我无法将它发布到它所属的地方。

还有一件事,在 AMD64 下,需要将 %llx 格式说明符更改为 %lx。

于 2010-12-09T00:44:25.013 回答