void new2d(int* aInit, int* aResult)
{
int cyclic[34] = {0};
for (int i = 0; i < 32; i++)
{
cyclic[i] = aInit[i];
}
cyclic[32] = aInit[0];
cyclic[33] = aInit[1];
float three = 3.0;
for (int i = 0; i < 32; i += 4)
{
int j = i + 1;
int k = j + 1;
__asm__ __volatile__
(
"vmovdqa (%0), %%xmm0;"
"vmovdqa (%1), %%xmm1;"
"vcvtdq2ps %%xmm0, %%xmm0;"
"vcvtdq2ps %%xmm1, %%xmm1;"
"addps %%xmm0, %%xmm1;"
"vmovdqa (%2), %%xmm1;"
"vcvtdq2ps %%xmm1, %%xmm1;"
"addps %%xmm0, %%xmm1;"
"vbroadcastss (%3), %%xmm1;"
"divps %%xmm0, %%xmm1;"
"vcvtps2dq %%xmm0, %%xmm0;"
"vmovdqa %%xmm0, (%4);"
:
: "a"(&(cyclic[i])), "b"(&(cyclic[j])), "c"(&(cyclic[k])), "d"(&three), "S"(&aResult[i])
);
}
}
我想把一个初始数组的三个子数组相加,找出它们的平均值,然后保存在结果数组中。但启动.exe文件后,显示Segmentation Fault,如何解决?使用GNU 2.9.3, Ubuntu
您正在使用 vmovdqa
指令,该指令要求在数组的未对齐元素上使用一个对齐的内存操作数。在数组的未对齐元素上使用 vmovdqu
来代替,用于加载和存储。或者更好的是,在实际的计算指令中使用内存操作数(不过这只在AVX中有效;在遗留的SSE中,大多数指令的内存操作数必须是对齐的)。
汇编器块还有其他的低效率和问题。例如,正如评论中提到的,你缺少了clobbers,它表明了可能被asm块修改的CPU和内存状态的片段。在你的例子中,你缺少了 "memory"、"xmm0 "和 "xmm1 "clobbers。如果没有这些,编译器就会认为asm块不会影响内存内容(the aResult
特别是数组)或 xmm
寄存器(例如,将这些寄存器用于自己的目的,与你的asm块发生冲突)。
另外,你似乎弄乱了输入和输出寄存器在 addps
和 divps
指令,因为在少数情况下,你会覆盖或不使用前面指令的结果。在gcc使用的AT&T x86 asm语法中,最后一个操作数是输出操作数。 在使用任何AVX指令时,通常应该使用每条指令的AVX版本,尽管将128位AVX指令与传统SSE混合使用,如果YMM寄存器的上半部分已经干净了(例如vzeroupper),就不会造成SSEAVX过渡停顿。 使用 vaddps
vdivps
是可选的,但建议使用。
另外,你传递对输入和输出数组的引用效率很低。与其将指针传递给数组的特定元素,不如将内存引用传递给这些元素更有效率,这使得编译器可以使用比普通指针更复杂的内存引用参数。这消除了在你的asm块之前,在一个单独的指令中计算指针的需要。另外,将 tree
常数 xmm
寄存器而不是内存。理想的情况下,你会希望将 vbroadcastss
的循环,但这只有在支持内在的情况下才有可能。 或者把循环写在一个asm语句里面)。
修正和改进后的asm语句应该是这样的。
__asm__ __volatile__
(
"vcvtdq2ps %1, %%xmm0;"
"vcvtdq2ps %2, %%xmm1;"
"vaddps %%xmm1, %%xmm0;"
"vcvtdq2ps %3, %%xmm1;"
"vaddps %%xmm1, %%xmm0;"
"vbroadcastss %4, %%xmm1;"
"vdivps %%xmm0, %%xmm1;"
"vcvtps2dq %%xmm1, %0;"
: "=m"(aResult[i])
: "m"(cyclic[i]), "m"(cyclic[j]), "m"(cyclic[k]), "x"(three)
: "memory", "xmm0", "xmm1"
);
(其实不一定非得是这样的) volatile
现在内存输出是显式操作数了)。)
但是一个更好的解决方案是使用内在技术来实现这个asm块。这不仅会使asm块更安全,还会使它更有效率,因为它可以实现额外的编译器优化。当然,这只有在你的编译器支持intrinsics的情况下才有可能。