2011-12-28 109 views
0

我使用的Visual Studio編譯的代碼,在gcc編譯時如何在C++的彙編代碼中使用C變量?

double callVariadicDoubleFunc(double * doubles, unsigned int numDoubles,double(*TestFunc)(double,...)) 

{ 

    // sizeof(double) must be 8! 
    if (numDoubles == 0) 
     return 0; 
    double * lastDouble = doubles + (numDoubles - 1); 
    double res; 
    int temp; 

    __asm mov eax, numDoubles 
    __asm mov edx, lastDouble 
    __asm mov temp,esp 

    __asm label_loop: 
    __asm sub esp, 8 
    __asm fld qword ptr [edx] 
    __asm fstp qword ptr [esp] 
    __asm sub eax, 1 
    __asm sub edx, 8 
    __asm test eax, eax 
    __asm jnz label_loop 

    __asm call TestFunc 
    __asm fstp  qword ptr res; 
    __asm mov esp, temp 

    return res; 
} 

,但現在我想用gcc但也有一些錯誤,我不能解析它來編譯!刪除所有編譯時錯誤,我已經改變了代碼一點這種形狀:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)(...)) 
{ 
    int temp; 
    double res; 
    arguments += numDoubles; 
    asm("mov eax, numDoubles" "\n" 
     "mov ecx, arguments" "\n" 
     "mov temp,esp"   "\n" 

     "label_loop:"   "\n" 
     "sub esp, 8"   "\n" 
     "fld qword ptr [ecx]" "\n" 
     "fstp qword ptr [esp]" "\n" 
     "sub eax, 1"   "\n" 
     "sub ecx, 8"   "\n" 
     "test eax, eax"  "\n" 
     "jnz label_loop"  "\n" 

     "call mFunction"  "\n" 
     "fstp qword ptr res" "\n" 
     "mov esp, temp"  ); 
    return res; 
} 

,但現在我得到鏈接錯誤:

undefined reference to `numDoubles' 
undefined reference to `arguments' 
undefined reference to `temp' 

任何想法我怎麼能解決這些問題?

旁註:我編譯我的代碼與這些選項:「 - 摹-masm =英特爾-O0 -Wall」

回答

2

事情有點複雜。您應該使用帶有約束的擴展GCC組件。此外,使用temp來保存堆棧指針是一個壞主意,因爲局部變量的地址取決於堆棧指針的值。最好創建一個標準的堆棧框架。

另外,您已經忘記將1減去指針。作爲獎勵,在GCC中,數組中最後一個指針不需要臨時變量。

沿東西線:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)()) 
{ 
    double res; 
    asm(
     /* set up the frame pointer */ 
     "push ebp"    "\n" 
     "mov ebp, esp"   "\n" 

     "label_loop:"   "\n" 
     "sub esp, 8"   "\n" 
     "fld qword ptr [%2]" "\n" 
     "fstp qword ptr [esp]" "\n" 
     "sub %1, 1"   "\n" 
     "sub %2, 8"   "\n" 
     "test %1, %1"   "\n" 
     "jnz label_loop"  "\n" 

     "call %3"    "\n" 
     "fstp qword ptr [%0]" "\n" 
     "mov esp, ebp"   "\n" 
     "pop ebp"    "\n" 
     : /* no output */ 
     :"b"(&res), "a"(numDoubles), "c"(arguments + (numDoubles - 1)), "d"(mFunction) /* input */ 
     :"cc" /* clobber */); 
    return res; 
} 

res變量,因爲FSTP指令需要一個指針,它是一個輸入不能被用作輸出。你可以使用m約束條件,如果你沒有對堆棧做有趣的事情(請參閱下面的更正)。

其它校正: 你可以使用r約束,如果你有沒有列出EAX,ECX和EDX中被破壞的名單,因爲它們不是通過函數調用preseved(和你調用一個函數)。但是不能列出用作輸入/輸出的破碎寄存器。

注意&res使用約束「B」是保存槽函數調用,因此如預期fstp將工作。

最後,由於您正在更改標誌寄存器(使用test)並使用函數調用,因此只有「cc」被列入破壞列表中。

運行gcc -masm=intel -save-temps我們可以檢查生成的彙編:

; Before the asm 
mov  eax, DWORD PTR [ebp+12] ; numDoubles 
sub  eax, 1 
sal  eax, 3 
mov  ecx, eax   
add  ecx, DWORD PTR [ebp+8] ; arguments + 8*(numDoubles - 1) 
lea  ebx, [ebp-16]   ; res 
mov  eax, DWORD PTR [ebp+12] ; numDoubles 
mov  edx, DWORD PTR [ebp+16] ; mFunction 

; The asm 
push ebp 
mov ebp, esp 
label_loop: 
sub esp, 8 
fld qword ptr [ecx] 
fstp qword ptr [esp] 
sub eax, 1 
sub ecx, 8 
test eax, eax 
jnz label_loop 
call edx   ; clobbers eax, ecx, edx and flags 
fstp qword ptr [ebx] 
mov esp, ebp 
pop ebp 

; After the asm 
fld QWORD PTR [ebp-16] ; return res 

這似乎大多是正確的給我。

更正:不完全。函數指針和res變量必須保持到寄存器,因爲你正在創建一個編譯器知道任何關於棧幀,所以它無法計算這些局部變量的地址。所以,我最後的一些修改是不錯,但沒用。

此外,指向res變量的指針必須位於ECX中。

恢復。

+0

我第一次粘貼你的代碼我得到一個分段錯誤,大量的時間花費上調試後,我發現了這個問題!一切順利前'呼叫ebx'但當EBX被稱爲是得到炒的寄存器。所以我不得不將我的內聯asm函數分爲兩部分,一部分初始化並調用函數,第二部分收集結果。 – Ali1S232 2011-12-28 10:25:30

+0

嗯,棘手...... x86 ABI表示EAX,ECX和EDX不是通過函數調用保留的,而是EBX是。所以,解決辦法是隻塗用EBX舉行'&res'指針。你不能把它保存在棧中,因爲你的演奏它。請看看我的答案更新它解決了。 – rodrigo 2011-12-28 11:00:44

0

當提到聯彙編變量,他們必須以$開頭。此外,gcc使用AT & T語法進行彙編,所以src和dst與Intel相比相反。

http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

編輯:

這個問題似乎是你的名字,而不是全局指本地人。所以當彙編程序看到名字時,然後嘗試鏈接,它找不到它們。爲了解決這個問題,我認爲你需要使用擴展的asm。

+0

我已經添加了-masm = intel讓gcc使用intel語法。在Intel語法,你不必使用$來引用變量。 (我測試過它和gcc抱怨「未定義引用'$ numDoubles'」 – Ali1S232 2011-12-28 00:55:00

+0

你也必須告訴彙編程序,通過asm(「。intel_syntax noprefix \ n」); – Joel 2011-12-28 01:06:30

+0

沒有變化,我仍然得到相同的錯誤 – Ali1S232 2011-12-28 01:09:40