内联汇编中的子数组。 C++

Subarrays in inline-assembly. C++

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 中,大多数指令的内存操作数必须对齐)。

汇编程序块还有其他低效和问题。例如,如评论中所述,您遗漏了 clobber,它表示 CPU 的片段和 asm 块可能修改的内存状态。在您的情况下,您缺少 "memory"、"xmm0" 和 "xmm1" 破坏者。没有这些,编译器将假设 asm 块不影响内存内容(特别是 aResult 数组)或 xmm 寄存器(并且,例如,将这些寄存器用于冲突中的目的用你的 asm 块)。

此外,您似乎在 addpsdivps 指令中弄乱了输入和输出寄存器,因为您在某些情况下覆盖或不使用先前指令的结果。在 gcc 使用的 AT&T x86 asm 语法中,最后一个操作数是输出操作数。在使用任何 AVX 指令时,您通常应该使用每条指令的 AVX 版本,尽管如果 YMM 寄存器的上半部分已经干净(例如 vzeroupper ).使用 vaddps / vdivps 是可选的,但建议使用。

此外,您正在低效地传递对输入和输出数组的引用。与其将指针传递给数组的特定元素,不如将内存引用传递给这些元素更有效,这允许编译器使用比普通指针更复杂的内存引用参数。这消除了在 asm 块之前的单独指令中计算指针的需要。此外,在 xmm 寄存器而不是内存中传递 tree 常量效率更高。理想情况下,您希望将 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 块更安全,还会使其更高效,因为它会启用额外的编译器优化。当然,这只有在您的编译器支持内部函数时才有可能。