gpt4 book ai didi

c - 如何使用 128 位 C 变量和 xmm 128 位 asm?

转载 作者:太空狗 更新时间:2023-10-29 16:46:31 26 4
gpt4 key购买 nike

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

最佳答案

如果变量不是 16 字节对齐的,您会看到段错误问题。 The CPU can't MOVDQA to/from unaligned memory addresses ,并会生成处理器级“GP 异常”,提示操作系统对您的应用进行段错误。

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

此外,阅读 objdump,看起来编译器用代码包装了 asm 序列,使用 MOVQ 指令将操作数从堆栈复制到 xmm2 和 xmm3 寄存器,只是让你的 asm 代码然后将值复制到 xmm0和 xmm1。异或到 xmm0 后,包装器仅将结果复制到 xmm2,然后再将其复制回堆栈。总的来说,效率不是很高。 MOVQ 一次复制 8 个字节,and expects (under some circumstances), an 8-byte aligned address .获取未对齐的地址,它可能会像 MOVDQA 一样失败。然而,包装器代码向 BP 寄存器添加对齐偏移量(-0x80、-0x88 和后来的 -0x78),它可能包含也可能不包含对齐值。总的来说,生成的代码不保证对齐。

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

#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 intrinsics instead (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

关于c - 如何使用 128 位 C 变量和 xmm 128 位 asm?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1990266/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com