静态单赋值(一)—gcc中的支配树
版權聲明:本文為CSDN博主「ashimida@」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/lidan113lidan/article/details/119336214
更多內容可關注微信公眾號??
一、需要了解的基本概念
在gcc優化的過程中,不論是循環的優化還是數據流分析,支配樹在其中都起到至關重要的作用:
? *?對于循環來說,支配樹對找出代碼中的循環非常有用
? *?對數據流分析來說,支配樹對計算SSA_NAME中的phi函數插入點十分重要
支配樹中的主要概念包括:
? 1)?必經結點(Dominator,又稱支配結點):?如果從(如函數的)起始節點S0到結點n的所有有效路徑都經過結點d,?那么結點d則成為結點n的必經結點,同時每一個結點都是自己的必經結點
??????簡單的說,必經結點的意思就是,從S0開始,如果控制流走到了n,那么在此之前其一定走過了結點d,?此時記為dom(n) = d;
? 2)?嚴格必經結點:?若d是n的必經結點,且d!=n,則d是n的嚴格必經結點;
? 3)?直接必經結點(Immediate Dominator,又稱直接支配結點):
? ? ? 首先對于畢竟節點有定理如下:?如果d,e都是n的必經結點,則d一定是e的必經結點或e一定是d的必經結點;
? ? ? 而直接必經結點的定義為:?若i是n的嚴格必經結點,且i不是n的其他必經結點的必經結點,則i是n的直接必經結點;
? ? ? 簡單說就是 i是距離n最近的n的(非!=n的)必經結點,?即為 idom(n) = i;
? 4)?必經結點樹(Dominator Tree,又稱支配樹):
? ? ? 一棵樹中包含圖中的所有節點,?但只為每個做一條從其直接支配節點到自身的邊(也就是對于所有節點n,只有 idom(n) => n的邊),?這樣構成的一顆樹即為此圖的必經結點樹;
? ? ? 對于流圖來說,?由于其中的每一個結點都至少有一個必經結點(即起始結點),?且每個結點都只能有一個直接必經結點,故一個流圖一定能畫出其對應的必經結點樹.且若流圖中每個節點均可達,則對應的必經結點樹中的每個結點也必可達.?以[1] C18.1.2為例,其中流圖和其對應的支配樹的關系如圖:
? ? ?5)?必經結點邊界(Dominance Frontier,?又稱支配結點邊界):?結點x的必經結點邊界是所有符合下面條件的結點w的集合: x是w前驅的必經結點,但不是w的嚴格必經結點;
? ? ? ? 簡單說就是:?首先支配結點邊界是針對某個結點來說,?其是多個節點的集合;?支配結點是當前結點和當前結點前驅的其他后繼結點可能的控制流匯集處(也是在轉SSA過程中需要插入phi函數的位置),?以[1] C19.1.2為例:
?? ?? ? 其中結點5的必經結點為{4,5,12,13},?這四個結點都是結點5和其他結點控制流的匯集處;
二、gcc中支配樹相關結構體
? ? 支配樹是針對流圖的,而在gcc中則是針對函數的控制流圖(CFG)的,?故一個函數的支配樹信息最終是保存在函數內的(實際上是各個bb->dom中),?在gcc中和支配樹相關的結構體有三個:
? ? 1) enum dom_state:?此枚舉型中記錄當前函數中支配樹的狀態
enum dom_state {DOM_NONE,????????? ? ??/*?代表當前函數的支配樹還沒有計算 *DOM_NO_FAST_QUERY,? ? ?/* 代表當前函數的支配樹已經計算好了(記錄在各個bb->dom中), 但尚未建立快速查詢 */DOM_OK? ? ? ??? ? ?? ? /*?代表當前函數支配樹的快速查詢也建立好了(更新好了bb->dom.dfs_num_in/out字段), 只有支配樹和快速查詢均建立好了才會有DOM_OK狀態 */ };? ? 2) class dom_info:?此類可以保存一個支配樹的完整信息,但在gcc中通常都是臨時的保存支配樹算法的結果(最終結果保存在bb->dom中)
class dom_info {......TBB *m_dfs_parent; /* Lengauer-Tarjan 算法中需要借助DFS來實現高效的支配樹計算,而此結構體實際上是一個數組,其下標為函數CFG在DFS下每個結點的父節點編號 */TBB *m_key;TBB *m_path_min;TBB *m_bucket;TBB *m_next_bucket;TBB *m_dom; /* 這里同樣是一個數組, 其中m_dom[x] 記錄DFS中編號為x的結點的直接必經結點編號 */TBB *m_set_chain;unsigned int *m_set_size;TBB *m_set_child;TBB *m_dfs_order; /* 記錄基本塊在DFS遍歷中的編號,在DFS中編號從1開始 */TBB *m_dfs_last; /* 指向 m_dfs_order中最后一個bb */basic_block *m_dfs_to_bb; /* 記錄DFS中每個節點對應的bb的 basic_block 結構體指針 */unsigned int m_nodes;bitmap m_fake_exit_edge;unsigned m_n_basic_blocks; /* 記錄當前支配樹中的結點個數,實際上來自函數中基本塊的個數 */bool m_reverse; /* 代表當前記錄的是否為后序支配信息, gcc中可以計算支配樹和后序支配樹兩種, 此值來自參數 CDI_DOMINATORS/CDI_POST_DOMINATORS */basic_block m_start_block; /* 記錄當前函數的入口bb */basic_block m_end_block; /* 記錄當前函數的出口bb */ };? ? 3) struct et_node:?此結構體實際上是代表元素樹(Element Tree)中一個結點信息的,而在gcc中,每個bb均通過此元素來保存其支配樹結點
struct basic_block_def {......struct et_node * dom[2]; /* 在基本塊(bb)中, dom[0/1]分別記錄此bb在其支配樹和后序支配樹(若有)中的結點信息, 整個函數的支配樹信息實際上就是記錄在由入口bb開始的各個bb的dom中 */...... }struct et_node {void *data; /* 在支配樹中,data指向當前支配結點對應的基本塊bb的指針 */int dfs_num_in, dfs_num_out; /* 記錄此結點在其對應的樹結點的DFS遍歷過程中,進/出此結點時遍歷到的結點編號. 對于支配樹來說,這兩個字段負責支配樹的快速查詢 */struct et_node *father; /* 記錄當前節點的父節點,在支配樹中當前節點的父節點為其直接支配節點(idom)*/struct et_node *son; /* 當前節點的第一個子節點指針,在支配樹中則是最后插入的子節點 */struct et_node *left; /* 當前結點的左右兄弟結點 */struct et_node *right; /* The brothers of the node. */struct et_occ *rightmost_occ; /* The rightmost occurrence. */struct et_occ *parent_occ; /* The occurrence of the parent node. */ };三、gcc中支配樹的計算
? ? 在gcc中是通過calculate_dominance_info來計算當前函數的支配樹的,?在調用此函數之前要求當前全局的cfun記錄當前要計算的函數,?計算結果會以一個個支配結點的形式記錄在當前函數的各個基本塊的bb->dom中,?而并不是以一個支配樹結構體(如dom_info)保存的,函數定義如下:
/*此函數為當前函數cfun計算[后序]支配樹信息(dir決定是否為后序),計算結果以支配結點的方式保存到cfun的各個bb->dom中,并將當前函數的支配樹計算狀態更新到 cfun->cfg->x_dom_computed (即dom_computed)中;若此函數已計算了支配樹,則此函數需重新計算并驗證之前計算的支配樹的正確性,如果二者不匹配直接報錯,匹配則返回不做任何修改. */ void calculate_dominance_info (cdi_direction dir) {unsigned int dir_index = dom_convert_dir_to_idx (dir); /* 先確定本次是要計算支配樹(dir=0)還是后序支配樹(dir=1),后序均以后序支配樹舉例 */if (dom_computed[dir_index] == DOM_OK) /* 若當前函數已有支配樹,則對當前函數重新計算支配樹信息,并和原有的比較看是否正確 */{checking_verify_dominators (dir); /* 重新計算的支配樹(dom_info)和函數中已有的支配樹(各bb->dom)信息匹配,則直接返回 */return;}if (!dom_info_available_p (dir)) /* 若當前函數的支配樹是完全不可用的(DOM_NONE),則在此循環中重新計算支配樹 */{gcc_assert (!n_bbs_in_dom_tree[dir_index]); /* 支配樹未計算時,cfg中支配節點數量應給為0 */basic_block b;FOR_ALL_BB_FN (b, cfun) /* 在開始計算前先為當前函數cfun的每個bb都分配一個元素樹結點,以存儲其在支配樹中的支配結點的信息 */b->dom[dir_index] = et_new_tree (b);n_bbs_in_dom_tree[dir_index] = n_basic_blocks_for_fn (cfun); /* 在cfg中記錄當前支配樹中結點的個數,支配樹中結點個數正常和函數中的結點個數是相同的 */dom_info di (cfun, dir); /* 創建并初始化一個臨時的dom_info類,用來先保存整個支配樹的計算結果,參數是要計算的函數和方向(支配樹or后序支配樹) */di.calc_dfs_tree (); /* DFS遍歷當前函數中所有的結點,結果都存在這個臨時的dom_info di中, 根據Lengauer-Tarjan 算法,快速計算必經節點的方法中首先就是要構建DFS */di.calc_idoms (); /* 按照LT算法, 計算出當前函數所有bb的直接支配結點,并將其保存在di.m_dom數組中 */FOR_EACH_BB_FN (b, cfun) /* 遍歷當前函數的所有bb, 將計算出的支配樹信息轉換為一個個支配結點的形式保存到當前函數的各個bb->dom中 */{if (basic_block d = di.get_idom (b)) /* 從dom_info di中獲取基本塊b的直接支配節點 d */et_set_father (b->dom[dir_index], d->dom[dir_index]); /* 將idom(b) = d 這個信息記錄到b/d兩個基本塊的 ->dom中(分別更新了d->dom[0]->son/ b->dom[0]->father等信息) */}dom_computed[dir_index] = DOM_NO_FAST_QUERY; /* 標記當前函數的支配樹狀態為尚未開啟快速查詢 */}else /* 若當前函數的支配樹已經計算,只是尚未建立快速查詢,則這里也是要重算并驗證一遍支配樹是否正確 */checking_verify_dominators (dir); /* 重算并驗證的流程實際上和if中的流程十分類似,只是最后的填充bb->dom變為了對比是否正確 */compute_dom_fast_query (dir); /* 建立支配樹的快速查詢,實際上只是修改了各個bb->dom的 dfs_num_in/out字段, 以便于快速查找當前bb在dfs中包含的子結點編號 */ }4. gcc中支配結點邊界的計算
? ? 在gcc中支配結點邊界的信息是通過compute_dominance_frontiers函數計算的,其定義如下:
void compute_dominance_frontiers (bitmap_head *frontiers) {timevar_push (TV_DOM_FRONTIERS);compute_dominance_frontiers_1 (frontiers);timevar_pop (TV_DOM_FRONTIERS); }? ? 其具體實現就不展開了,此函數最關鍵的就是需要知道其返回的是一個n*n的二維數組,記錄在frontiers指向的一個bitmap中, n為當前函數中基本塊的個數,?frontiers[x][y] = 1;?則代表對于基本塊 x,?基本塊y是其支配結點邊界中的一個結點;
參考資料:
[1] 《現代編譯原理—C語言描述》
總結
以上是生活随笔為你收集整理的静态单赋值(一)—gcc中的支配树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows列出系统所有补丁(wmic
- 下一篇: InDesign 教程如何创建风格化的书