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