2008-10-26 52 views
14

我曾經在嵌入式系統的項目中工作過,在那裏我們重新安排了堆棧變量的聲明順序,以減小生成的可執行文件的大小。舉例來說,如果我們有:C編譯器能否重新安排堆棧變量?

void func() 
{ 
    char c; 
    int i; 
    short s; 
    ... 
} 

我們將重新排序這是:

void func() 
{ 
    int i; 
    short s; 
    char c; 
    ... 
} 

由於對準問題的第一個導致12個字節的堆棧空間被使用,第二個導致只有8個字節。

這是C編譯器的標準行爲還是我們使用的編譯器的缺點?

在我看來,編譯器應該能夠重新排序堆棧變量,以支持更小的可執行文件大小,如果它想的話。有人向我建議,C標準的某些方面可以防止這種情況發生,但我無法以任何方式找到有信譽的來源。

作爲一個額外的問題,這是否也適用於C++編譯器?

編輯

如果答案是肯定的,C/C++編譯器可以重新排列堆棧變量,你可以給一個編譯器,絕對做到這一點的例子嗎?我希望看到編譯器文檔或類似的東西來支持它。

再次編輯

謝謝大家的幫助。對於文檔,我能找到的最好的東西是文件Optimal Stack Slot Assignment in GCC(pdf),由Naveen Sharma和Sanjiv Kumar Gupta在2003年GCC峯會會議上提交。

這裏的問題項目是使用ADS編譯器進行ARM開發。在該編譯器的文檔中提到,由於ARM-Thumb體系結構如何計算本地堆棧幀中的地址,因此像我所示的排序聲明可以提高性能以及堆棧大小。該編譯器不會自動重新安排當地人利用這一點。這裏鏈接的文章說,從2003年起,GCC也沒有重新安排堆棧框架來改善ARM-Thumb處理器的參考位置,但這意味着您可以。

我無法找到任何明確表示這已在GCC中實施的任何內容,但我認爲這篇論文被認爲是證明你們都是正確的。再次感謝。

回答

22

由於標準中沒有任何內容禁止C或C++編譯器,所以編譯器可以做到這一點。

聚合(即結構)的不同之處在於,必須保持相對順序,但編譯器仍然可以插入填充字節以實現更好的對齊。

IIRC較新的MSVC編譯器在與當地人的緩衝區溢出作鬥爭時使用這種自由度。

作爲一個提示,在C++中,即使編譯器重新對內存佈局進行了重新排序,銷燬順序也必須與聲明的反向順序相同。

(我不能提出旁證,雖然,這是從內存。)

0

它是編譯器的細節,可以讓他自己的編譯器,如果他想這樣做會反過來。

10

編譯器是免費的,甚至從堆棧中刪除該變量,使其只註冊如果分析結果顯示的地址該變量從不被使用/使用。

+0

這是一個很好的觀點。當我和我的同事討論這件事時,我並沒有這樣做。 – 2008-10-26 19:27:52

+1

它甚至可以將多個變量分配到相同的寄存器或堆棧位置,如果它可以證明變量在相同的代碼段中從不存在。這是很常見的做法,特別是內聯代碼會導致短暫的可變生命。 – 2008-10-26 21:05:51

4

編譯器甚至可能根本沒有使用數據堆棧。如果你的平臺非常小,以至於你擔心8到12個字節的堆棧,那麼很可能會有編譯器具有非常特殊的方法。 (想起一些PIC和8051編譯器)

你在編譯什麼處理器?

+0

這是我剛剛工作的一個項目,我們正在使用舊版本的Arm Developer Suite(ADS)編譯器爲幾個ARM處理器構建。我真的只是要求解決關於其他編譯器如何處理這個問題的討論。 – 2008-10-26 19:41:29

0

體面的編譯器會將局部變量放入寄存器中(如果可以的話)。如果寄存器壓力過大(空間不足)或者變量的地址已被佔用,這意味着它需要存在內存中,變量只能放在堆棧上。

據我所知,沒有什麼說變量需要放置在C/C++堆棧的任何特定位置或對齊方式;編譯器會將它們放在最適合性能和/或適合編譯器編寫者的任何地方。

+0

當地人幾乎總是必須在某個時候傾倒到堆棧。唯一的例外是如果你的函數*從不*調用另一個函數,在這種情況下,一切都是暫時的,並且可以在沒有堆棧幀的情況下處理。 – 2008-10-26 19:53:53

10

堆棧甚至不需要存在(實際上,C99標準沒有單個「堆棧」單詞)。所以是的,編譯器可以自由地執行任何想要的操作,只要它保留具有自動存儲持續時間的變量的語義。

舉一個例子:我遇到很多情況,我無法在調試器中顯示局部變量,因爲它存儲在寄存器中。

0

AFAIK C或C++的定義中沒有指定編譯器應如何在堆棧中排序局部變量。我會說,依靠編譯器在這種情況下可能做的是一個壞主意,因爲下一個版本的編譯器可能會有所不同。如果您花費時間和精力來訂購本地變量以節省堆棧的幾個字節,那麼對於系統的功能而言,這些少量字節最好是非常關鍵的。

4

德州儀器62xx系列DSP的編譯器能夠完成「整個程序優化」。 (你可以把它關掉)

這是你的代碼被重新安排的地方,而不僅僅是當地人。所以執行順序最終不會達到你所期望的。

C和C++不要實際上承諾一個內存模型(就JVM而言),所以事情可能會非常不同,並且仍然合法。

對於那些不瞭解它們的人來說,62xx系列每個時鐘週期DSP有8個指令;在750Mhz時,它們在6e + 9指令處達到峯值。無論如何,有些時候。它們執行並行執行,但是指令排序是在編譯器中完成的,而不是像Intel x86那樣的CPU。

PIC和兔子嵌入式板不堆棧,除非你問得特別好。

0

沒有必要對C標準要求或不要求的空閒猜測:最近的草稿可從ANSI/ISO working group在線免費獲得。

0

這並不回答你的問題,但這是我的2美分關於一個相關的問題...

我沒有堆棧空間優化的問題,但是我遇到了堆棧中雙變量錯誤對齊的問題。可以從任何其他函數調用函數,並且堆棧指針值可能有任何未對齊的值。所以我想出了下面的想法。這是不是原來的代碼,我只是寫它...

#pragma pack(push, 16) 

typedef struct _S_speedy_struct{ 

double fval[4]; 
int64 lval[4]; 
int32 ival[8]; 

}S_speedy_struct; 

#pragma pack(pop) 

int function(...) 
{ 
    int i, t, rv; 
    S_speedy_struct *ptr; 
    char buff[112]; // sizeof(struct) + alignment 

    // ugly , I know , but it works... 
    t = (int)buff; 
    t += 15; // alignment - 1 
    t &= -16; // alignment 
    ptr = (S_speedy_struct *)t; 

    // speedy code goes on... 
} 
38

不僅可以編譯器重新安排局部變量的堆棧佈局,它可以將它們分配到寄存器,將其分配到寄存器有時住和有時在堆棧中,它可以將兩個本地人分配到內存中的同一個插槽(如果他們的生存區域不重疊),甚至可以完全消除變量。