2013-04-02 164 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

但是內在函數也有缺點:編譯器負責寄存器分配和重新排序指令等。編譯器通常不會這麼做。 –