複製鏈接
請複製以下鏈接發送給好友

死碼刪除

鎖定
死碼消除(Dead code elimination)是一種編譯器原理中編譯最優化技術,它的用途是移除對程序運行結果沒有任何影響的代碼。
中文名
死碼刪除
外文名
Dead code elimination
應    用
計算機、編程等
作    用
刪除冗餘、優化

死碼刪除概念

死碼消除(Dead code elimination)是一種編譯器原理中編譯最優化技術,它的用途是移除對程序運行結果沒有任何影響的代碼。移除這類的代碼有兩種優點,不但可以減少程序的大小,還可以避免程序在運行中進行不相關的運算行為,減少它運行的時間。不會被運行到的代碼(unreachable code)以及只會影響到無關程序運行結果的變量(Dead Variables),都是死碼(Dead code)的範疇。

死碼刪除死碼的刪除

死碼通常被視為無條件的(unconditionally),所以我們可以在編譯時期透過死碼刪除來移除這些無用的代碼 [1] 
然而,在實現上,只有在特定的情形才會標註一個代碼區塊是無用的,或是不會運行到的,這可能無法在編譯時期所得知。例如在不同的運行環境有不同的結果(舉例來説,目標環境可能會有不同的操作系統版本,或是不同的驅動程序及可用服務的組合),可能會在代碼內要求不同特例的集合,同時在這些案例下就變成有條件的死碼。然而,軟件(例如驅動程序、或是常駐服務)可能會根據用户的設置,而配置或排除特定的功能,使得在一些特定的情境,會變成部分無用的死碼。模塊化軟件實現方式,是在需要時才讀取動態庫,在多數的案例中,不可能僅從特定的庫讀取相關的程序,它仍然會包含一些程序片段,在特定的環境下是可被視為死碼,但是這在編譯時期是無法被排除的。
動態死碼刪除(dynamic dead code elimination)被使用在運行時動態偵測,可辨識及解析相依性,用以移除有條件的死碼,在運行時期重新組合保留的代碼。
多數的計算機語言、編譯器、操作系統不提供,或是僅比動態讀取庫及後鏈接(late linking)提供多一點點的功能,能使用動態死碼刪除的軟件是相當稀少的。

死碼刪除示例

下列的示例,以C語言寫成:
int foo(void)
{
int a = 24;
int b = 25; /* 賦值給一個無用的變量*/
int c;
c = a << 2;
return c;
b = 24; /* 不會被執行的代碼*/
return 0;
}
分析上述程序對於數值的使用,將會發現b的數值在第一次被賦值之後,就不再使用,而且b是在foo函數內宣告,無法在函數外面使用,所以變量b是無用的,最優化的過程可以回收他所使用的空間,並刪除他的初始化 [2] 
當第一個return被運行,則代表函數已經結束,之後變量b的賦值行為則不會被運行,所以賦值行為是可以被刪除的。如果程序有更復雜的控制流程,例如在第一個return之後加上一個標籤,使得程序中任和一個地方都可以用goto來運行到這個程序段,那麼變量b的賦值行為將有可能被運行。
儘管一些計算行為被包裝成函數,他們的數值也無法被函數外所訪問,但仍然還是有些函數僅會回傳一個固定的數值,這或許可以將該數值取代所有函數的調用。(這個簡化的過程被稱之為常量摺疊
更高級的編譯器則會有些選項可以啓動死碼刪除的功能,而有些則是可以選擇不同檔次的死碼刪除,比較低級檔次的死碼刪除僅會移除不會被運行到的指令,而較高階的可能不會保留無用變量的空間,其他高階檔次的做法可能會判斷哪些指令及函數沒有任何用途,並且刪除他們。
死碼刪除最普遍的做法,是透過預處理器來判斷代碼是否需要被編譯,如下列這個示例:
int main(void) {
int a = 5;
int b = 6;
int c;
c = a * (b >> 1);
if (0) { /* DEBUG */
printf("%d\n", c);
}
return c;
}
由於0將永遠被視為False,所以if判斷式內的程序將永遠不會被運行,死碼刪除將會把它移除,這個技術在調試上相當常見,我們可以透過一個數值來決定程序段是否該被編譯,使用死碼刪除的最優化過程,將會使用預處理器來進行相同的工作。
實現中,有些在最優化過程中找到的死碼,是被其他最優化技術產生,舉例來説,典型強度折減的技術,將會在代碼內插入新的運算以取代昂貴的運算行為,而被取代的代碼就成了死碼,隨後,死碼刪除會移除那些計算,以完成這個效果(沒有複雜的強度折減算法)。
從歷史上來看,死碼刪除使用來自數據流分析的信息,Cytronetal在原始文章中發佈了一個基於靜態單賦值形式的算法,Shillingsburg改進了這個算法,並開發了一個算法來移除無用的控制流(Control-flow)。
參考資料
  • 1.    Keith D. Cooper and Linda Torczon, Engineering a Compiler, Morgan Kaufmann, 2003, pages 498ff.
  • 2.    Ron Cytron, Jeanne Ferrante, Barry Rosen, and Ken Zadeck. Efficiently Computing Static Single Assignment Form and the Program Dependence Graph. ACM TOPLAS 13(4), 1991.