2012-11-12 43 views
0

我正在研究一個需要與QNX-Momentics(基於eclipse,g ++ 4.6.1工具鏈)和Visual Studio 2010編譯的項目。對於一些例程,我決定去實現手動裝配,因爲即使內在函數也沒有很好的優化。第一個編譯器具有ATt & T語法,可以使用-masm = intel標誌進行「智能化」,第二種是intel方言。不是很好,但工作 - -雙重asm方言項目的問題intel/AT&T

使用Intel標誌,我可以用超越記法方面定義絕招:現在

#ifdef _WIN32 
    #define _cmd(...) __VA_ARGS__ 
    __asm { 
#else 
    #define _cmd(...) #__VA_ARGS__ 
    asm volatile (
#endif 
    // constants 
    // set loop counter 
    _cmd(xor  eax, eax;) 
     : 
     : 
#ifdef _WIN32 
    } 
#else 
    ); 
#endif 

,一個問題是,我不能訪問本地變量或參數按名稱功能使用內聯AT & T.一個提示我在另一個線程獲得,使用像

register __m128i x asm("xmm6"); 

東西沒有工作,工作的局部變量,它被分配到XMM0。沒有內部函數定義的局部變量或參數,導致AT &牛逼未定義的引用,所以我決定用裸棧處理,如

_cmd(movupd xmmword ptr [eax], xmm3;) 

,並遇到了新的問題:

兩個函數的參數和局部兩種方言的變量處理完全不同。請看下面的例子:

template<typename T> 
void linearRegression2DAsm(unsigned int p_oNumPoints, T *p_pXcoords, T *p_pYcoords, 
double *oX, double *oY, double *oXY, 
double p_oAvgX, double p_oAvgY) 
{ 
unsigned int p_rLoopsize = p_oNumPoints - (p_oNumPoints % 2); 
double oAvgX[2]; 

,這簡單的計算上面的定義塊發出後:

_cmd(xor  eax, eax;) 
// p_pXccoords 
_cmd(mov  ecx, dword ptr [ebp+12];) 
// p_pYcoords 
_cmd(mov  edx, dword ptr [ebp+16];) 
// p_oAvgX 
_cmd(movhpd xmm6, qword ptr [ebp+20];) 
// p_oAvgY 
_cmd(movhpd xmm7, qword ptr [ebp+28];) 
_cmd(movlpd xmm6, qword ptr [ebp+20];) 
_cmd(movlpd xmm7, qword ptr [ebp+20];) 
_cmd(addpd  xmm7, xmm6;) 
// result into oAvgX 
_cmd(mov eax, [ebp-32];) 
_cmd(movupd xmmword ptr [ebp-32], xmm7;) 

結果應該是在oAvgX,其正常工作與英特爾,但使用不會導致成功intel標記AT & T asm編譯器。其次,我擔心額外的O2-Flag可能會優化其他變量,因此不能保證在不同的編譯中構建相同的堆棧。

我需要內聯,但是看不到解決雙重方言問題的任何方法。

回答

0

您可以在GCC的內聯程序集中通過名稱訪問本地變量,這只是您必須以不同於在VS2010中執行此操作的方式來執行此操作。在內聯程序集的最後,您應該/必須提供一個輸入列表,一個輸出列表和一個「clobbered」列表;輸入列表和輸出列表都可以包含局部變量。還要注意,「clobbered」列表非常重要(編譯器假定沒有列出爲破壞或列爲輸出的內容不會改變,包括所有內存內容等)。不管你使用的是哪個編譯器,不管你使用的是哪種編譯器,依靠偶然的事情(比如發生在哪個寄存器中,或者發生在哪個內存位置或堆棧位置)是一個嚴重的錯誤。在一些有限的測試案例中工作。唯一正確的方法是依賴爲此提供的設施(例如,GCC內聯彙編中的輸入/輸出列表)。

對於任何非平凡的內聯程序集;因爲內聯彙編不是任何(C/C++)標準的一部分;我假設讓它在多個編譯器中可靠工作的唯一方法是複製內聯程序集。

還要注意不同的操作系統有不同的約定(例如不同的ABIs,不同的內核系統調用等)。從本質上講,(作爲最壞的情況下),你可能需要做這樣的事情:

#ifdef WIN32_VS2010 
    /* Inline assembly to suit Visual Studio 2010 for Win32 here */ 
#elifdef WIN32_ICC 
    /* Inline assembly to suit Intel's "ICC" compiler for Win32 here */ 
#elifdef LINUX_ICC 
    /* Inline assembly to suit Intel's "ICC" compiler for Linux here */ 
#elifdef WIN32_GCC 
    /* Inline assembly to suit GCC compiler for Win32 here */ 
#elifdef LINUX_GCC 
    /* Inline assembly to suit GCC compiler for Linux here */ 
#else 
    /* Generate error about unsupported target here */ 
#endif 
+0

問候並感謝您的回覆。我知道at&t/gcc程序集中的列表和clobber列表,但是我可以如何訪問代碼塊中間的變量?使用列表中的內容,將其分配給某些內存,然後再訪問它?而且,最重要的是,如何將變量分配給sse寄存器?我到目前爲止失敗了...... – gilgamash

+0

這是一個巨大的祕密;但變量只是一種幻覺 - 它們不存在。存在的是地址和這些地址的內存內容;並註冊。您可能想要告訴GCC,組件的輸入是某個地址(例如「變量」),以便內聯程序集可以將該地址處的數據加載到SSE寄存器中。你可能想告訴GCC SSE寄存器也是一個輸入(雖然我不確定GCC是否以這種方式正確支持SSE)。 – Brendan

0

一種方法可能是變量包裝成一個結構,有足夠的虛擬變量來強制清潔對準4,8或16.然後可以使用offsetof(struct x,member)計算成員的相對位置,並希望這些數字可以作爲兼容字符串在編譯時注入。

#define LOCAL(a) ((offsetof(struct mystruct,a)==0?"0":offsetof(a)==4?"4":"error")) 

asm(" push ebp \n\t" 
    " mov ebp, %0 \n\t" 
    " mov %0, " LOCAL(a) "\n\t" // this would convert to [ebp + 4] 
    " pop ebp \n\t" 
    :"=0" (&my_struct) :::); 

VC版可以開始:

asm(" push ebp "); 
asm(" lea ebp, struct.a "); 

之後,相同的(醜陋的)語法和相同數量的局部變量。