2013-04-02 166 views
7

我们正在运行一个科学计划,我们想要实现AVX功能。整个程序(用Fortran + C编写)将被矢量化,此刻我正试图在GCC内联汇编中实现复数乘法。用于复数乘法的汇编代码/ AVX指令。 (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交互。例如,我可以在执行乘法之前打印出其中一个值(例如a),并且程序不会给出错误的结果。

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

回答

7

正如你猜测的那样,问题在于你没有告诉GCC哪个寄存器是你正在破解的。如果他们还不支持将YMM寄存器放在clobber列表中,我感到很惊讶;你使用的是什么版本的GCC?

在任何情况下,它几乎肯定会足以把相应的XMM寄存器在撞列表,而不是:

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

其他一些注意事项:

  • 你同时装入输入两次,这是低效的。没有理由这样做。
  • 我会使用"r" (a), "r" (b)作为约束,并写我的负载像vmovupd (%2), %%ymm1。生成的代码可能没有区别,但似乎更习惯于正确。
  • 执行任何SSE代码之前,以避免(大)摊位不要忘了把vzeroupper以下AVX代码。
+0

非常感谢你,这解决了这个问题=)。我使用gcc 4.7.2和thx来提供建议。 –

+1

不要使用'“r”(a),“r”(b)'用'vmovupd(%2),%% ymm1'等,GCC会假定* a和* b不被访问(除非你添加一个“内存”clobber)。 –

3

我想补充两点意见,没有直接回答你的问题:

  • 我强烈建议使用编译器内在,而不是直接组装。通过这种方式,编译器负责寄存器分配,并且可以在优化代码方面做得更好(内联方法,重新排序指令等)。
  • Agner Fog有一个优化向量化操作的C++ vector class library,包括对复数的操作。即使你可能不能够直接在C代码中使用自己的图书馆,他的优化代码可能是一个很好的起点;请参阅src/special/complexvec.hthe zipped source code
+0

Thx,即使我无法使用它,我也会查看它。实际上,我已经编译了两个版本,即内部代码和程序集,我想知道为什么程序集运行不好,因为都编译为相同的优化程序集代码(objdump -S ..)。 –

+0

但是内在函数也有缺点:编译器负责寄存器分配和重新排序指令等。编译器通常不会这么做。 –