用于乘法复数的汇编代码/ AVX指令。 (GCC内联装配)

时间:2013-04-02 14:35:37

标签: c gcc assembly complex-numbers avx

我们正在运行科学计划,我们希望实施AVX功能。整个程序(用Fortran + C编写)将被矢量化,目前我正在尝试在GCC内联汇编中实现复数乘法。

汇编代码需要4个复数并一次执行两次复数乘法:

v2complex cmult(v2complex *a, v2complex *b) {
    v2complex ret;
    asm (
        "vmovupd %2,%%ymm1;"
        "vmovupd %2, %%ymm2;"
        "vmovddup %%ymm2, %%ymm2;"
        "vshufpd $15,%%ymm1,%%ymm1,%%ymm1;"
        "vmulpd %1, %%ymm2, %%ymm2;"
        "vmulpd %1, %%ymm1, %%ymm1;"
        "vshufpd $5,%%ymm1,%%ymm1, %%ymm1;"
        "vaddsubpd %%ymm1, %%ymm2,%%ymm1;"
        "vmovupd %%ymm1, %0;"
        :
        "=m"(ret)
        :
        "m" (*a),
        "m" (*b)
        );
    return ret;
}

其中a和b是256位双精度:

typedef union v2complex {
    __m256d v;
    complex c[2];
} v2complex;

问题在于代码主要产生正确的结果,但有时会失败。

我对集会很新,但我试图自己解决这个问题。似乎C程序(优化的-O3)与汇编代码中使用的寄存器ymm交互。例如,我可以在执行乘法之前printf其中一个值(例如a),并且程序永远不会给出错误的结果。

我的问题是如何告诉GCC不要与ymm互动。我没有成功 将ymm放入破坏的寄存器列表。

2 个答案:

答案 0 :(得分:7)

正如你猜测的那样,问题是你没有告诉海湾合作委员会哪些登记你正在破坏。如果他们还不支持将YMM寄存器放入clobber列表中,我会感到惊讶;您使用的是什么版本的GCC?

无论如何,将相应的XMM寄存器放入clobber列表中几乎肯定就足够了:

: "=m" (ret) : "m" (*a), "m" (*b) : "%xmm1", "%xmm2");

其他一些说明:

  • 您正在加载两次输入,效率很低。没有理由这样做。
  • 我会使用"r" (a), "r" (b)作为约束,并像vmovupd (%2), %%ymm1一样编写我的负载。生成的代码可能没什么区别,但似乎更具惯用性。
  • 在执行任何SSE代码之前,不要忘记在vzeroupper之后放置AVX代码,以避免(大)档位。

答案 1 :(得分:3)

我添加了两条评论,而不是直接回答您的问题:

  • 我强烈建议使用编译器内在函数而不是直接汇编。这样编译器就可以处理寄存器分配,并且可以更好地优化代码(内联方法,重新排序指令等)。
  • Agner Fog具有C++ vector class library优化的矢量化操作,包括对复数的操作。即使您可能无法在C代码中直接使用他的库,他的优化代码也可能是一个很好的起点;请参阅the zipped source code中的src/special/complexvec.h