簡體   English   中英

C ++ CPU寄存器用法

[英]C++ CPU Register Usage

在C ++中,局部變量總是在堆棧上分配。 堆棧是應用程序可以占用的允許內存的一部分。 該內存保存在RAM中(如果沒有換成磁盤)。 現在,C ++編譯器是否總是創建在堆棧中存儲局部變量的匯編程序代碼?

舉例來說,以下簡單代碼:

int foo( int n ) {
   return ++n;
}

在MIPS匯編程序代碼中,這可能如下所示:

foo:
addi $v0, $a0, 1
jr $ra

如您所見,我根本不需要使用堆棧。 C ++編譯器會識別出來,並直接使用CPU的寄存器嗎?

編輯:哇,非常感謝您幾乎立即和廣泛的答案! foo的函數體當然應該是return ++n; ,不是return n++; :)

是。 沒有規則“變量總是在堆棧上分配”。 C ++標准沒有說明堆棧。它不假設堆棧存在,或存在寄存器。 它只是說代碼應該如何表現,而不是如何實現。

編譯器只在必要時將變量存儲在堆棧中 - 例如,當它們必須通過函數調用時,或者如果您嘗試獲取它們的地址。

編譯器不是傻瓜。 ;)

免責聲明:我不知道MIPS,但我確實知道一些x86,我認為原則應該是一樣的..

在通常的函數調用約定中,編譯器會將n的值壓入堆棧以將其傳遞給函數foo 但是,有一個fastcall約定可以用來告訴gcc通過寄存器傳遞值。 (MSVC也有這個選項,但我不確定它的語法是什么。)

test.cpp:

int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
    return ++n;
}

使用g++ -O3 -fomit-frame-pointer -c test.cpp編譯上面的g++ -O3 -fomit-frame-pointer -c test.cpp ,我得到了foo1

mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret

如您所見,它從堆棧中讀取值。

這是foo2

lea eax,[ecx+0x1]
ret

現在它直接從寄存器中獲取值。

當然,如果您內聯函數,編譯器將在較大函數的主體中進行簡單的添加,而不管您指定的調用約定。 但是當你無法內聯時,這將會發生。

免責聲明2:我不是說你應該不斷猜測編譯器。 在大多數情況下,這可能是不實際和必要的。 但不要以為它會產生完美的代碼。

編輯1:如果您正在談論普通的局部變量(不是函數參數),那么是的,編譯器會在它認為合適的情況下將它們分配到寄存器或堆棧中。

編輯2:看起來調用約定是特定於體系結構的,MIPS將傳遞堆棧上的前四個參數,正如Richard Pennington在他的回答中所述。 因此,在您的情況下,您不必指定額外屬性(實際上是x86特定的屬性。)

是的,一個好的,優化的C / C ++將優化它。 甚至更多在這里看到:費利克斯·馮·Leitners編譯調查

普通的C / C ++編譯器無論如何都不會將每個變量放在堆棧上。 你的foo()函數的問題可能是變量可以通過堆棧傳遞給函數(系統的ABI(硬件/操作系統)定義了)。

使用C的register關鍵字,您可以給編譯器一個提示 ,即將變量存儲在寄存器中可能會很好。 樣品:

register int x = 10;

但請記住:如果想要,編譯器可以自由地不將x存儲在寄存器中!

答案是肯定的,也許吧。 它取決於編譯器,優化級別和目標處理器。

在mips的情況下,前四個參數(如果很小)在寄存器中傳遞,返回值在寄存器中返回。 因此,您的示例無需在堆棧上分配任何內容。

實際上,真相比小說更奇怪。 在您的情況下,參數返回不變:返回的值是++運算符之前的n:

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

由於你的示例foo函數是一個標識函數(它只返回它的參數),我的C ++編譯器(VS 2008)完全刪除了這個函數調用。 如果我將其更改為:

int foo( int n ) {
   return ++n;
}

編譯器用這個內聯

lea edx, [eax+1] 

是的,寄存器在C ++中使用。 MDR(存儲器數據寄存器)包含被提取和存儲的數據。 例如,為了檢索單元格123的內容,我們將值123(以二進制形式)加載到MAR中並執行獲取操作。 當操作完成時,單元123的內容的副本將在MDR中。 為了將值98存儲到單元格4中,我們將4加載到MAR中,將98加載到MDR中並執行存儲。 當操作完成時,通過丟棄之前的任何內容,單元4的內容將被設置為98。 數據和地址寄存器與它們一起工作以實現此目的。 在C ++中,當我們使用值初始化var或詢問其值時,會發生相同的現象。

此外,現代編譯器還執行寄存器分配,這比內存分配更快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM