編譯器設計-代碼優化
Compiler Design - Code Optimization
優化是一種程序轉換技術,它試圖通過使代碼消耗更少的資源(如CPU、內存)來改進代碼,並提供高速。
在優化中,高級通用編程結構被非常高效的低級編程代碼所代替。代碼優化過程必須遵循以下三條規則:
輸出代碼無論如何不能改變程序的含義。
優化應該提高程序的速度,如果可能的話,程序應該需要更少的資源。
優化本身應該是快速的,不應該延遲整個編譯過程。
優化代碼的工作可以在編譯過程的不同級別進行。
在開始時,用戶可以更改/重新排列代碼或使用更好的算法來編寫代碼。
生成中間代碼后,編譯器可以通過地址計算和改進循環來修改中間代碼。
在生成目標機器代碼時,編譯器可以使用內存層次結構和CPU寄存器。
優化可以大致分為兩類:獨立於機器的優化和依賴於機器的優化。
機器獨立優化Machine-independent Optimization
在這種優化中,編譯器接受中間代碼,並轉換不涉及任何CPU寄存器和/或絕對內存位置的部分代碼。例如:
do
{
item = 10;
value = value + item;
} while(value<100);
此代碼涉及重復分配標識符項,一旦:
Item = 10;
do
{
value = value + item;
} while(value<100);
不僅可以節省CPU周期,而且可以在任何處理器上使用。
機器相關優化Machine-dependent Optimization
在生成目標代碼之后,以及根據目標機器體系結構轉換代碼時,執行與機器相關的優化。它涉及CPU寄存器,可能有絕對內存引用,而不是相對引用。依賴於機器的優化器努力最大限度地利用內存層次結構。
基本塊Basic Blocks
源代碼通常有許多指令,這些指令總是按順序執行,並被視為代碼的基本塊。這些基本塊之間沒有任何跳轉語句,即當執行第一條指令時,同一基本塊中的所有指令將按其出現順序執行,而不會失去程序的流控制。
程序可以有各種各樣的結構作為基本塊,如IF-THEN-ELSE、SWITCH-CASE條件語句和循環(如DO-WHILE、FOR和REPEAT-UNTIL等)。
基本塊識別Basic block identification
我們可以使用以下算法來查找程序中的基本塊:
從基本塊開始的所有基本塊的搜索頭語句:
程序的第一個語句。
任何分支的目標語句(條件/無條件)。
任何分支語句之后的語句。
Header語句及其后面的語句構成一個基本塊。
基本塊不包括任何其他基本塊的任何頭語句。
從代碼生成和優化的角度來看,基本塊都是重要的概念。
基本塊在識別變量方面起着重要作用,這些變量在一個基本塊中被多次使用。如果某個變量被多次使用,則分配給該變量的寄存器內存不需要清空,除非塊完成執行。
控制流圖Control Flow Graph
程序中的基本塊可以用控制流圖來表示。控制流圖描述程序控制如何在塊之間傳遞。它是一個有用的工具,通過幫助定位程序中任何不需要的循環來幫助優化。
回路優化Loop Optimization
大多數程序在系統中作為循環運行。為了節省CPU周期和內存,有必要對循環進行優化。循環可以通過以下技術進行優化:
不變代碼Invariant code:駐留在循環中並在每次迭代時計算相同值的代碼片段稱為循環不變代碼。通過將代碼保存為只計算一次而不是每次迭代,可以將此代碼移出循環。
歸納分析Induction analysis:如果一個變量的值在循環中被循環不變的值改變,則稱之為歸納變量。
強度降低Strength reduction:有些表達式消耗更多的CPU周期、時間和內存。在不影響表達式輸出的情況下,這些表達式應替換為更便宜的表達式。例如,乘法(x*2)在CPU周期方面比(x<1)昂貴,並且產生相同的結果。
死碼消除Dead-code Elimination
死代碼是一個或多個代碼語句,它們是:
要么從未執行,要么無法實現,
或者如果執行了,它們的輸出就永遠不會被使用。
因此,死代碼在任何程序操作中都不起作用,因此可以簡單地消除它。
部分死代碼Partially dead code
有些代碼語句的計算值僅在某些情況下使用,即有時使用值,有時不使用。這種代碼稱為部分死代碼。
上面的控制流圖描述了一個程序塊,其中變量“a”用於分配表達式“x*y”的輸出。假設分配給“a”的值從未在立即循環在控件離開循環后,“a”被賦給變量“z”的值,該值稍后將在程序中使用。我們在此得出結論,“a”的賦值代碼從未在任何地方使用過,因此它有資格被刪除。
同樣,上面的圖片描述了條件語句總是false,這意味着以true case編寫的代碼永遠不會執行,因此可以刪除它。
部分冗余Partial Redundancy
在並行路徑中多次計算冗余表達式,而不改變操作數在路徑中多次計算部分冗余表達式,操作數不做任何更改。例如,
循環不變代碼是部分冗余的,可以通過使用代碼運動技術來消除。
部分冗余代碼的另一個示例可以是:
if (condition)
{
a = y OP z;
}
else
{
...
}
c = y OP z;
我們假設操作數(y和z)的值不會從變量a的賦值更改為變量c。這里,如果條件語句為true,則y OP z計算兩次,否則為一次。代碼操作可以用來消除這種冗余,如下所示:
if (condition)
{
...
tmp = y OP z;
a = tmp;
...
}
else
{
...
tmp = y OP z;
}
c = tmp;
這里,條件是真是假;y OP z只應計算一次。