2012-05-29 43 views
3

考慮下面的代碼:何時可以安全使用「= r」寄存器操作數約束?

#include <stdio.h> 

void main() { 
    uint32_t num = 2; 

    __asm__ __volatile__ ("CPUID"); 
    __asm__ __volatile__ ("movl $1, %%ecx":); 
    __asm__ __volatile__ ("andl $0, %%ecx": "=r"(num)); 

    printf("%i\n", num); 
} 

我最初的預期是,該代碼將打印0,如果我註釋掉CPUID線確實如此,但,是它給我的垃圾。經過一些試驗,錯誤和研究後,我意識到我正在獲得一個隨機寄存器的價值。顯然GCC不會假定我想要執行語句的結果。

問題是,我已經看到(其他人的)代碼依賴於該語句正確地獲取AND的結果,而不管其他寄存器正在發生什麼。根據我的觀察,顯然這樣的代碼被打破了,"=r"應該被替換爲"=c"

我的問題是,我們是否可以依靠"=r"約束行爲一致或根據顯而易見的期望?或者GCC的實現過於不透明/怪異/其他,最好只是在任何情況下都避免它?

+0

您使用的是哪個版本的gcc?還有什麼優化級別?還有你在用什麼操作系統? –

+0

@Gabriel 4.5.3在Windows 7 x64 SP1的Cygwin中未指定優化級別。 –

回答

2

爲了使用=r輸出說明符,您需要讓gcc自由選擇要使用的寄存器。您可以通過輸入%0來指定輸入和輸出,輸入爲%1,輸入爲第一個輸入。

在你的情況下,你說num可以在註冊表中。但是asm指令中沒有任何內容使用輸出寄存器。所以gcc會基本上忽略這一點。

,你得到一個不同的值,如果你對此有何評論或不發表意見CPUID指令的原因是CPUID可以寫信給eaxebxecxedx。我在我的系統上嘗試了你的例子,並得到0作爲結果在這兩種情況下。但我注意到生成的程序集正在打印eax的值。所以我想當我跑這個程序CPUID正在寫0eax

如果你確實想使用=r約束你需要做這樣的事情:

asm("CPUID \n\t" 
     "movl $1, %0 \n\t" 
     "andl $0, %0 \n\t" 
     :"=r"(num)); 

否則,如果你的彙編代碼,特別提到一個寄存器,那麼你將需要在約束列表中指定。在你的例子中,這意味着使用=c