在gcc中,我想做一个带有2个C变量的128位xor,通过asm代码:怎么做?
asm (
"movdqa %1, %%xmm1;"
"movdqa %0, %%xmm0;"
"pxor %%xmm1,%%xmm0;"
"movdqa %%xmm0, %0;"
:"=x"(buff) /* output operand */
:"x"(bu), "x"(buff)
:"%xmm0","%xmm1"
);
但我有一个Segmentation fault错误; 这是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)
答案 0 :(得分:19)
如果变量不是16字节对齐的话,您会看到段错误。 The CPU can't MOVDQA to/from unaligned memory addresses,并会生成处理器级别的“GP异常”,提示操作系统对您的应用进行分段。
您在堆上声明(堆栈,全局)或分配的C变量通常不会与16字节边界对齐,但偶尔您可能偶然得到一个对齐的变量。您可以使用__m128或__m128i数据类型指示编译器确保正确对齐。每个都声明一个正确对齐的128位值。
此外,读取objdump,看起来编译器用asm序列包装asm序列,使用MOVQ指令将操作数从堆栈复制到xmm2和xmm3寄存器,只是让你的asm代码然后将值复制到xmm0和xmm1。 xor-x到xmm0之后,包装器将结果复制到xmm2,然后将其复制回堆栈。总的来说,效率不是很高。 MOVQ一次复制8个字节and expects (under some circumstances), an 8-byte aligned address。得到一个未对齐的地址,它可能会像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位值的 a 和 b ,因为编译器可能不支持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
答案 1 :(得分:1)
__builtin_ia32_pxor
内在的?
答案 2 :(得分: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。