[英]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.