聲明:本系列文章,是根據中國大學MOOC網 哈工大的編譯原理 這門課學習而成的學習筆記。
一、流圖
基本塊(Basic Block)
基本塊是滿足下列條件的最大的連續三地址指令序列
- 控制流只能從基本塊的第一個指令進入該塊。也就是說,沒有跳轉到基本塊中間或末尾指令的轉移指令
- 除了基本塊的最後一個指令,控制流在離開基本塊之前不會跳轉或者停機
基本塊劃分算法
首先(1)指令是首指令,其次跟在跳轉指令的目標指令(5)(9)(23)是首指令,最後緊跟跟在跳轉指令之後的指令(9)(13)(14)(23)是首指令。
流圖(Flow Graphs)
- 流圖的結點是一些基本塊
- 從基本塊B到基本塊C之間有一條邊當且僅當基本塊C的第一個指令可能緊跟在B的最後一條指令之後執行
- 此時稱B是C的前驅(predecessor) ,C是B的後繼(successor)
- 有兩種方式可以確認這樣的邊:
- 有一個從B的結尾跳轉到C的開頭的條件或無條件跳轉語句
- 按照原來的三地址語句序列中的順序,C緊跟在之B後,且B的結尾不存在無條件跳轉語句
圖中,B6不是B5的後繼,因爲B5的結尾存在一個無條件跳轉指令
二、常用的代碼優化方法
優化的分類
- 機器無關優化 :針對中間代碼
- 機器相關優化 :針對目標代碼
- 局部代碼優化 :單個基本塊範圍內的優化
- 全局代碼優化 :面向多項基本塊的優化
常用的優化方法
- 刪除公共子表達式
- 刪除無用代碼
- 常量合併
- 代碼移動
- 強度削弱
- 刪除歸納變量
① 刪除公共子表達式
公共子表達式
如果表達式x op y先前已被計算過,並且從先前的計算到現在,x op y中變量的值沒有改變,那麼x op y的這次出現就稱爲公共子表達式
B5中的t7和t6重複,t8和T10重複,構成局部公共子表達式,可以刪除t7,t10,並將對t7和t10的引用換成t6和t8,如右上角所示。
B5中的t6和B2中的t2一樣,並且,從B2到B5的過程中i的值沒有改變,故t6和t2構成全局公共子表達式,可以用t2代替t6,同理t4可以代替t8
替換後,發現再次構成公共子表達式,可以繼續替換
② 刪除無用代碼
複製傳播
- 常用的公共子表達式消除算法和其它一些優化算法會引入一些複製語句(形如x = y的賦值語句)
- 複製傳播:在複製語句x = y之後儘可能地用y代替x
複製傳播給刪除無用代碼帶來機會
- 無用代碼(死代碼Dead-Code ) :其計算結果永遠不會被使用的語句
③ 常量合併(Constant Folding)
如果在編譯時刻推導出一個表達式的值是常量,就可以使用該常量來替代這個表達式。該技術被稱爲常量合併
④ 代碼移動(Code Motion)
這個轉換處理的是那些不管循環執行多少次都得到相同結果的表達式(即循環不變計算,loop-invariant computation) ,在進入循環之前就對它們求值
循環不變計算的相對性:對於多重嵌套的循環,循環不變計算是相對於某個循環而言的。可能對於更加外層的循環,它就不是循環不變計算
⑤ 強度削弱(Strength Reduction)
用較快的操作代替較慢的操作,如用加代替乘
循環中的強度削弱
- 歸納變量:對於一個變量x ,如果存在一個正的或負的常數c使得每次x被賦值時它的值總增加c ,那麼x就稱爲歸納變量(Induction Variable)
⑥ 刪除歸納變量
在沿着循環運行時,如果有一組歸納變量的值的變化保持步調一致,常常可以將這組變量刪除爲只剩一個
三、基本塊的優化
- 很多重要的局部優化技術首先把一個基本塊轉換成爲一個無環有向圖(directed acyclic graph,DAG)
基本塊的 DAG 表示
基於基本塊的 DAG 刪除無用代碼
e刪除後,b和c就根節點。
數組元素賦值指令的表示
根據基本塊的DAG可以獲得一些非常有用的信息
從 DAG 到基本塊的重組
首先L是活躍變量,故保留L,刪除M;其次判斷根節點,G不是活躍變量,故可以刪除,其次,有兩個名字的保留一個名字,最後直接使用常量,刪除B和K
四、數據流分析
數據流分析(data-flow analysis)
- 數據流分析
一組用來獲取程序執行路徑上的數據流信息的技術 - 數據流分析應用
到達-定值分析 (Reaching-Definition Analysis)
活躍變量分析 (Live-Variable Analysis)
可用表達式分析 (Available-Expression Analysis) - 在每一種數據流分析應用中,都會把每個程序點和一個數據流值關聯起來
數據流分析模式
基本塊上的數據流模式
到達定值分析
B1中d1,d2,d3到B2的入口處,沒有對i,j,a進行重新賦值,故這三個定值都可以到底B2的入口處;從d4之後的路徑到底B2入口處,必然會經過d7對i的重新賦值,d7殺死了d4,故d4不能到達B2入口處,其他類似。其中,注意d3可以到達B4的入口處,可以不經過B3,所以,注意可能這個說法
到達定值分析的主要用途
- 循環不變計算的檢測
如果循環中含有賦值x=y+z ,而y和z所有可能的定值都在循環外面(包括y或z是常數的特殊情況) ,那麼y+z就是循環不變計算 - 常量合併
如果對變量x的某次使用只有一個定值可以到達,並且該定值把一個常量賦給x ,那麼可以簡單地把x替換爲該常量 - 判定變量x在p點上是否未經定值就被引用
“生成”與“殺死”定值
到達定值的傳遞函數
到達定值的數據流方程
到達定值方程的計算
計算到達定值的迭代算法
引用-定值鏈 (Use-Definition Chains)
活躍變量分析
活躍變量
對於變量x和程序點p,如果在流圖中沿着從p開始的某條路徑會引用變量x在p點的值,則稱變量x在點p是活躍(live)的,否則稱變量x在點p不活躍(dead)
- 在所有的塊中,都沒有對a的應用,故a是不活躍的。
- i在B1後,必然會經過B2,B2中對i進行了引用,故i在B1出口處是活躍的,經過B2後,必然經B4對i進行重新賦值,故i在B2和b3的出口處不是活躍的,但在B4後,i可能會經過B2對其引用,故i在B4出口是活躍的。
- 控制重任意一個塊離開後,都會進入B2,故,j在任何出口處都是活躍的,其他的類似。
活躍變量信息的主要用途
刪除無用賦值
- 無用賦值:如果x在點p的定值在基本塊內所有後繼點都不被引用,且x在基本塊出口之後又是不活躍的,那麼x在點p的定值就是無用的
爲基本塊分配寄存器
- 如果所有寄存器都被佔用,並且還需要申請一個寄存器,則應該考慮使用已經存放了死亡值的寄存器,因爲這個值不需要保存到內存
- 如果一個值在基本塊結尾處是死的就不必在結尾處保存這個值
活躍變量的傳遞函數
活躍變量數據流方程
計算活躍變量的迭代算法
定值-引用鏈 (Definition-Use Chains)
可用表達式分析
可用表達式信息的主要用途
可用表達式的傳遞函數
可用表達式的數據流方程
計算可用表達式的迭代算法
爲什麼將OUT[B]集合初始化爲U?
四、支配節點和回邊
支配結點 (Dominators)
- 如果從流圖的入口結點到結點n的每條路徑都經過結點d,則稱結點d支配(dominate)結點n,記爲d dom n
- 每個結點都支配它自己
直接支配結點 (Immediate Dominator)
- 從入口結點到達n的所有路徑上,結點n的最後一個支配結點稱爲直接支配結點
尋找支配結點
計算支配結點的迭代算法
回邊 (Back Edges)
五、自然循環及其識別
自然循環的識別
算法:構造一條回邊的自然循環
自然循環的一個重要性質
- 除非兩個自然循環的首結點相同,否則,它們或者互不相交,或者一個完全包含(嵌入)在另外一個裏面
- 如果兩個循環具有相同的首結點,那麼很難說哪個是最內循環。此時把兩個循環合併
六、刪除全局公共子表達式和複製語句
刪除全局公共子表達式
全局公共子表達式刪除算法
刪除複製語句
刪除輔助語句的算法
七、代碼移動
代碼移動
- 循環不變計算的檢測
- 代碼外提
循環不變計算檢測算法
代碼外提
循環不變計算語句 s : x = y + z 移動的條件
代碼移動算法
八、作用於歸納變量的強度削弱
歸納變量檢測算法