2011-10-11 44 views
1

爲什麼gcc編譯器在創建彙編代碼時將while循環轉換爲do-while構造?我知道任何while循環,可以在Cgcc循環構造修改爲彙編代碼

while (test) { ... }

可以寫成一個做,而例如可以改寫爲

if (!test) goto skip; 
do { 
. . . 
} while (test); 
skip: 
+3

因爲在彙編語言中沒有'for'或'while'循環。 –

+4

如果您想討論Assembly,請發佈編譯器生成的彙編代碼。 –

回答

4

大廈,你可能不知道爲什麼 do-while構造更高效,特別是考慮到如圖所示,測試已被複制(因此生成的代碼更大)。答案是很經常的測試表現總是能夠證明是在循環入口真,所以

if (!test) goto skip; 
loop: 
    . . . // loop body 
    if (!test) goto loop; 
skip: 
    . . . // continue the program 

可以簡化爲

loop: 
    . . . // loop body 
    if (!test) goto loop; 
    . . . // continue program 

現在,爲什麼不這樣做,在同一時間原始轉換,並避免原始轉換,如果編譯器不能證明循環會至少循環一次?因爲證明循環的算法至少循環一次實際上是一個通用的if-condition優化器。優化的通常順序是這樣的:

  1. 變換所有環路成「規範」的形式(或多或少如上面的第一碼塊)
  2. 做許許多多該期望環路優化以那種規範的形式,例如顯示的if語句消除。
  3. 當你完成了所有關心循環的事情之後,試圖去規範化那些可以消除冗餘的地方。

另一件需要知道的事情是,有時會因爲編譯器期望產生更好的運行時行爲而故意留下重複的測試。例如,如果編譯器有理由相信循環很多次循環,但不能證明它至少循環了一次,那麼循環上方的條件分支指令幾乎總是通過,並且循環下的條件分支幾乎總是跳轉。在這種情況下,將它們分開可能會使CPU的分支預測器更加準確。分支預測精度僅次於緩存友好性,因爲它是現代無序CPU速度的限制因素。

+0

每次循環時只有1個分支(只是向後一個)而不是兩個分支。 – harold

+0

確實如此,但是如果編譯器在循環頂部重新組合了測試表達式,那麼底部會有一個*無條件*分支,在現代CPU上幾乎是免費的 - 它佔用一個解碼單元,但從不會使它進入主管道。 – zwol

+0

非常感謝您的出色答覆。 – hopup

3

在一般do-while環比while循環更有效。由於編譯器想要創建一個快速(而不是可讀)的程序,因此它將大多數while循環轉換爲do-while循環。

事實上,如果採用只ifgoto(接近彙編語言)一while循環可以做如下:

start: 
    if (!test) goto skip; 
    . . . // loop body 
    goto start; 
skip: 
    ; // continue the program 

這是等效的,但是可能比do-while執行效率較低(因爲後者有代碼迴路內小1線):上anatolyg的答案

if (!test) goto skip; 
loop: 
    . . . // loop body 
    if (!test) goto loop; 
skip: 
    ; // continue the program