2011-08-03 75 views
1

我需要加載18h並將其輸出到端口60h,以下工作(內部asm(「」))。GCC內聯彙編:讓編譯器決定使用什麼寄存器來臨時值

ldi r1, 0x18 ; 0x18 -> r1 
sts 0x60, r1 ; output r1 -> 0x60 

我不在乎註冊r1或任何其他用於此。有沒有簡單的方法讓編譯器決定使用哪個寄存器?

我可以使用外部R/W的變量,但它產生了一些不必要的開銷:

register uint8_t tmp; 
asm volatile (
    "ldi %[tmp], 0x18 \n\t" 
    "sts 0x60, %[tmp]" 
    : [tmp] "=r"(tmp) :); 

這是AVR ATMEGA(8位)處理器。使用GCC 4.3.2

+0

它創建了多少開銷? – osgx

+1

'mov '不得不創建一個tmp變量,然後在輸入塊中描述它也是開銷。 –

+0

你可以嘗試更新的gcc和更高的-O3級別嗎? – osgx

回答

1

我不知道爲什麼我以前看過開銷,但是使用外部register臨時變量即使使用-O0(沒有優化)也沒有開銷。所以我使用:

register uint8_t tmp; 
asm volatile (
    "ldi %[tmp], 0x18 \n\t" 
    "sts 0x60, %[tmp]" 
    : [tmp] "=r"(tmp) :); 

它仍然需要將聲明的變量,比彙編語句塊的輸出描述,但它確實創造彙編代碼我想(無開銷)。

0

我認爲你應該對輸出操作數使用「= &」約束。

的機制是這樣的:

  • 對於任何輸入操作數,編譯器會提供一些寄存器,它會加載他們與他們的操作數的內聯彙編開始前的值。輸入操作數是只讀,這意味着編譯器會進一步期望您在程序集中保持寄存器不變。也就是說,編譯器希望寄存器在裝配後具有相同的值,因爲它可能決定隨後使用這些值。

  • 對於輸出操作數,編譯器也會爲您分配一系列寄存器。在內聯彙編結束時,您應該將這些寄存器加載到結果中,因爲編譯器希望在那裏找到它們。 注意事項:您爲輸出操作數提供的寄存器可能與您爲輸入操作數提供的寄存器一致!然而,這是非常有意義的:編譯器爲您提供在之前的某個寄存器中的輸入,並在之後將您的結果收集到與您的程序集相同的寄存器中。

  • 您可以使用輸出專用修飾符('&')來防止發生這種情況。編譯器保證只有輸出寄存器永遠不會加倍作爲輸入寄存器。

  • 您可以使用輸入輸出修飾符('+')明確要求編譯器對操作數的輸入和輸出使用完全相同的寄存器。這實際上使您可以透明地訪問操作數。重述相反不成立:不是使用此修飾符確實不是防止編譯器使用相同的寄存器兩倍 - 使用輸出修飾符。

所以基本上,你的選擇很簡單:

  • 不要請求一個臨時變量輸入操作數由於其只讀性質和編譯器的假設。例如,如果您創建一個臨時變量並使用某個值(可能爲零)對其進行初始化,則編譯器可能會重複使用它提供給您的寄存器,以便稍後再次需要它的值(零)。
  • 不要求輸出操作數,因爲它可能與某些輸入操作數重合。當您使用所謂的臨時性時,您可能會意外地碰撞您的輸入操作數。
  • 使用帶有輸出的輸出修飾符。通過這種方式,編譯器將爲您分配一些可以安全地進行抓取的寄存器(因爲它期望從那裏得到一些輸出)。

使用輸入輸出也爲您提供安全的註冊。但是,操作數需要初始化(因爲編譯器假定您將其作爲輸入讀取),並且如果您在內聯彙編的多個片段中使用相同的操作數,則需要恢復(出於同樣的原因)。相反,當使用輸出專用修改器時,編譯器可以很容易地發現該變量從不讀取。

相關問題