2010-10-10 333 views
15

我很難理解在GCC內聯彙編(x86)中扮演的角色約束。我有read the manual,這正好解釋了每個約束的作用。問題是,即使我理解每個約束所做的事情,但我很少理解爲什麼你會使用一個約束而不是另一個約束,或者可能會產生什麼影響。GCC內聯彙編:約束

我意識到這是一個非常廣泛的話題,所以一個小例子應該有助於縮小焦點。以下是一個簡單的asm例程,它只是增加了兩個數字。如果發生整數溢出,它會將值1寫入輸出C變量。

int32_t a = 10, b = 5; 
int32_t c = 0; // overflow flag 

__asm__ 
(
    "addl %2,%3;"  // Do a + b (the result goes into b) 
    "jno 0f;"   // Jump ahead if an overflow occurred 
    "movl $1, %1;"  // Copy 1 into c 
    "0:"     // We're done. 

    :"=r"(b), "=m"(c) // Output list 
    :"r"(a), "0"(b)  // Input list 
); 

現在,這工作正常,除非我必須任意撥弄約束,直到我得到它正常工作。本來,我用了以下的限制:

:"=r"(b), "=m"(c) // Output list 
    :"r"(a), "m"(b)  // Input list 

注意,而不是「0」時,我用b一個「M」的約束。這有一個奇怪的副作用,如果我使用優化標誌進行編譯並調用了兩次函數,出於某種原因,加法操作的結果也將被存儲在c中。我最終讀到了「matching constraints」,它允許你指定一個變量被用作輸入和輸出操作數。當我將"m"(b)更改爲"0"(b)時,它工作正常。

但我不明白爲什麼你會使用另一個約束。我的意思是說,我知道「r」表示變量應該在一個寄存器中,「m」表示它應該在內存中 - 但我不知道什麼是選擇一個而不是另一個的含義,或者是爲什麼如果我選擇特定的約束組合,則添加操作無法正常工作。

問題:1)在上面的示例代碼中,爲什麼b上的「m」約束會導致c被寫入? 2)是否有任何教程或在線資源更詳細地討論約束條件?

回答

13

下面是一個例子,以更好地說明爲什麼你應該仔細選擇的約束(相同的功能,你,但也許寫多了幾分簡潔):

bool add_and_check_overflow(int32_t& a, int32_t b) 
{ 
    bool result; 
    __asm__("addl %2, %1; seto %b0" 
      : "=q" (result), "+g" (a) 
      : "r" (b)); 
    return result; 
} 

所以,使用的制約因素:qrg

  • q意味着只有eaxecxedx,或ebx可以選擇。這是因爲set*指令必須寫入一個8位尋址寄存器(alah,...)。在%b0中使用b表示使用最低的8位部分(al,cl,...)。
  • 對於大多數雙操作數指令,至少有一個操作數必須是寄存器。因此,不要使用mg;對於至少一個操作數使用r
  • 對於最終的操作數,無論是寄存器還是內存都沒關係,所以使用g(general)。

在上面的例子中,我選擇使用g(而不是r),用於a因爲引用通常作爲存儲器指針來實現,所以使用r約束將要求第一複製指涉到寄存器,然後複製回來。使用g,所指對象可以直接更新。


至於爲什麼你原來的版本中添加的值改寫你的c,那是因爲你在輸出插槽指定=m,而不是(說)+m;這意味着編譯器可以重複使用相同的內存位置進行輸入和輸出。

在你的情況,這意味着兩種結果(因爲在對bc相同的內存位置):

  • 的加入並沒有溢出:那麼,c得到了改寫的b值(添加的結果)。
  • 加法確實溢出:然後,c變成1(並且b也可能變成1,這取決於代碼的生成方式)。
+0

謝謝 - 這是一個很好的答案。只需要澄清一下:即使'b'和'c'是內存中不同位置的不同變量,爲什麼'='(只寫)約束脩飾符賦予編譯器重用相同內存位置的權利? – Channel72 2010-10-10 03:06:01

+0

@ Channel72:「即使'b'和'c'是在記憶中不同位置的不同變量」 - 這實際上是一個主要假設,通常不適用。如果'b'和'c'是局部變量,那麼很可能它們都是由寄存器實際支持的,而不是內存位置。在這種情況下,內存位置僅僅是一個臨時保存位置,它的設置純粹是爲了適應您的'm'約束 - 在這種情況下,'b'和'c'可以很好地使用相同的臨時位置。 – 2010-10-10 03:17:21

+0

現在,如果'b'和'c'實際上都是由內存位置真正支持的,那麼你就是對的,通常它們不應該重疊。而且,如果一個由內存支持,另一個由註冊支持......那麼這兩種情況都是可能的。 – 2010-10-10 03:19:22