2011-12-12 111 views
9

我正在嘗試使用GCC的內聯彙編器來熟悉x86彙編。我試圖添加兩個數字(ab)並將結果存儲在c中。我有四個稍微不同的嘗試,其中三個工作;最後不會產生預期的結果。添加兩個數字

前兩個示例使用中間寄存器,並且這兩個示例都正常工作。第三個和第四個示例嘗試直接將這兩個值相加,而不使用中間寄存器,但結果因優化級別和添加輸入值的順序而異。我錯了什麼?

環境是:

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) 

首先,變量的聲明如下:

int a = 4; 
int b = 7; 
int c; 

實施例1:

asm(" movl %1,%%eax;" 
    " addl %2,%%eax;" 
    " movl %%eax,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    : "%eax" 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output: a=4, b=7, c=11 

實施例2:

asm(" movl %2,%%eax;" 
    " addl %1,%%eax;" 
    " movl %%eax,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    : "%eax" 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output: a=4, b=7, c=11 

實施例3:

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=11 
// output with -O3: a=4, b=7, c=14 

實施例4:

// this one appears to calculate a+a instead of a+b 
asm(" movl %1,%0;" 
    " addl %2,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=8 
// output with -O3: a=4, b=7, c=11 

解決。Matthew Slattery's answer是正確的。之前,試圖重用eax兩個bc

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %eax 
addl %eax, %eax 

有了馬修的修復建議,它現在使用ecx分別持有c

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %ecx 
addl %eax, %ecx 
+2

適用於我,無論是否啓用優化。嘗試使用-S編譯,以獲取彙編語言列表。然後您可以看到正在使用哪些寄存器。 – TonyK

+0

只是注意到我根據優化級別得到不同的結果。代碼示例使用新輸出更新。 –

+0

那麼程序集清單告訴你什麼? – TonyK

回答

7

默認情況下,gcc將假定在更新輸出操作數之前,內聯asm塊將使用輸入操作數完成。這意味着輸入和輸出都可以分配給同一個寄存器。

但是,這並不一定是在實施例3和殼體4

例如例3:

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 

...你已經閱讀a%1)之前更新c%0)。如果gcc恰好分配同一個寄存器既%0%1,然後將計算c = b; c += c,因此將在完全相同的方式失敗,你觀察:告訴gcc

printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=11 
// output with -O3: a=4, b=7, c=14 

您可以修復它的輸出操作數可以所使用的輸入被消耗之前,通過添加「&」改性劑對操作數,這樣的:

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=&r" (c) 
    : "r" (a), "r" (b) 
    ); 

(參見在gcc文檔"Constraint Modifier Characters"

+0

就是這樣,謝謝。修復之前和之後更新了彙編列表中的問題。 –

+0

感謝您的支持!這是我需要了解的東西,但文檔是相當可怕的。像這樣的提示是非常有價值的。 – TonyK

0

Hoi,我沒有看到有問題,它編譯和工作正常。然而,一個小提示:我很快與未命名的變量/寄存器混淆,所以我決定使用命名的變量/寄存器。該插件啄你可以例如實現這樣的:

static inline void atomicAdd32(volInt32 *dest, int32_t source) { 
// IMPLEMENTS: add m32, r32 
__asm__ __volatile__(
     "lock; addl %[in], %[out]" 
     : [out] "+m"(*dest) 
     : [in] "ir"(source)//, "[out]" "m"(*dest) 
     ); 
return; 
    } 

(你可以忽略現在的原子/鎖的東西),這使得清楚發生了什麼:

1)什麼寄存器是可寫的,可讀或者兩者皆爲

2)什麼是使用(內存,寄存器),這在性能和時鐘週期中可能很重要,因爲寄存器操作比訪問內存的操作更快。

乾杯, G.

P.S:你檢查你的編譯器是否重新排列代碼?