2015-08-15 131 views
1

在GCC手冊中,部分6.43.2.5 Extended Asm - Clobbers,在「記憶」撞的解釋寄存器,被提及,避免沖洗寄存器一招:避免沖洗內存訪問(GCC內聯彙編)

如果你知道存儲器的大小在編譯時被訪問,可以指定一個存儲器輸入這樣的:

{"m"(({ struct { char x[10]; } *p = (void *)ptr ; *p; }))} 

(假設你訪問的10個字節的字符串。)

我想我理解這個想法,但是我不完全清楚如何使用它,以及這個訣竅會產生什麼影響 - 除了向GCC提供更多類型的信息之外。 最重要的三個問題就出來了:

  • 我還以此爲輸出或輸入/輸出操作數和修改數據?
  • 如果我使用這個技巧,我是否仍然需要「內存」clobber?
    我想我不這樣做,因爲我宣稱內存塊是輸入/輸出,但我不確定。
  • 只有在訪問內存時,我可以安全地刪除所需的volatile限定符嗎?
    我想我可以,因爲它會被聲明爲輸出。

因爲我們喜歡的例子:這段代碼是否有意義,它是合法的嗎?它似乎工作。

#include <iostream> 
#include <cstdint> 

void add_assembly(std::uint64_t * x) { 
    struct memory { std::uint64_t data[2]; } * p = reinterpret_cast<memory*>(x); 
    __asm__ (
     "addq $1, %[x] \t\n" 
     "addq $5, 8%[x] \t\n" 
     : [x] "+m" (*p) // Bonus question: Why don't I need a "&" here? 
     : "m" (*p) 
     : "cc" 
); 
} 

int main() { 
    std::uint64_t x[2]; 
    x[0] = 3000; 
    x[1] = 7253; 
    std::cout << "before: " << x[0] << " " << x[1] << std::endl; 
    add_assembly(&x[0]); // add 1 to x[0], add 5 to x[1] 
    std::cout << "after: " << x[0] << " " << x[1] << std::endl; 
    return 0; 
} 

回答

1

我不是100%確定這個答案的所有內容,但我最近自己也看過這個問題。

我也可以用它作爲輸出或輸入/輸出操作數並修改數據嗎?

是的,但你可能是一個單獨的r(註冊)或m(可能在複雜的尋址模式)約束更好。這是特別的。如果您想在循環中增加指針,並因此需要它在寄存器中,則爲true。 m約束可以使%0擴展到(%rsi, %rdx, 4)什麼的。

如果我使用這個技巧,我還需要「內存」clobber嗎?

不。這告訴gcc哪個內存可能被修改。 "memory"表示全部內存可能被修改。

當只訪問內存時,可以安全地刪除所需的volatile限定符嗎?

您的意思是asm volatile (...)?是。 volatile只有當週圍的代碼不使用它的輸出(或沒有)時才需要讓gcc移動或消除asm語句。只要你告訴gcc你的asm在內存中產生的結果,並使用該結果,你不應該使用volatile,所以gcc可以圍繞任何其他黑盒函數進行優化。 (當然,假設你的asm是一個純粹的函數,它只依賴於它所聲明的輸入)。


例子很好。 :)你的看起來是正確的。

我認爲你是正確的,指定一個讀寫輸入操作數是不夠的;您需要將它指定爲輸入和輸出兩次。 +約束的措辭使我認爲「讀寫」意味着gcc將你留在其中的值視爲變量的新值,但我認爲情況並非如此。

我認爲在x86上,m約束等同於o(可抵消)約束。但要注意語法;你不希望8%[x]變成88(%rsp)。那麼也許8 + %[x]?不確定。留下一個空格,所以這是一個語法錯誤,而不是一個不同的數字。

\t\n很愚蠢。你想要在每一行的開頭選項卡。

asm (
    "inc  %[x] \n\t" // inc is shorter than `add` 
    "addq $5, 8 %[x] \n\t" // this `add` writes all flags, preventing partial-flag stalls/slowdowns 
    : [x] "+m" (*p) // Bonus question: Why don't I need a "&" here? 
     // because you aren't writing a temporary to the output before reading the input 
    : "m" (*p) 
    : "cc" 
);