RA, 暫存器分配

2021-07-12 02:39:37 字數 4666 閱讀 5522

(1) 首先遍歷整個routine,儲存所有def值;

問題:

你如何去儲存這些def值呢?要保證能快速的得到每個def值,因為每一 個 def 值不僅僅只包含dst暫存器號,還包括這條指令,以及其它資訊。

解答之一:

最快速的查詢演算法,就是hash演算法。在我們這裡通過構建乙個含有256個元素的hash表,hash[regno & (256-1)] = defindex;對於hash衝突的處理方法是,在def值的結構中存放乙個變數,用來儲存衝突的defindex.

比如:r1 = r2 + r3;//defindex = x;

r1 = r1 * r4;//defindex = x + 1;

很顯然,此時兩次def的r1的hash值是一樣的,但兩次def的defindex是不一樣的, 假定第一次的defindex = x, 那麼(pdefs + defindex) ->hashnextdefindex = x + 1;其實可以把這理解為鍊錶的形式處理hash衝突,但衝突不是在hash表上處理的而已。

(2) 自頂向下計算每個basic block的liveout def值,這裡的liveout def值表示在離開當前block後還可能活躍的def值。然後通過資料流分析,得到每個basic block的livein def值。

問題:

如何計算?

解答之一 :

根據routine中的def值的數目,來定義乙個長度為32bit倍數的變數(假定變數名字為flowfunction),每一位用來表示對應的defindex, 為1,表示活躍, 為0, 表示不活躍。

1) 分block掃瞄所有指令,在每乙個block裡,對有dst的指令,將flowfunction變數的對應位置為1;如果出現多次定義乙個變數的情況,則只會把最後一次的defindex置為1,也就是在每次掃瞄的過程中,在最後一次def的時候,會把前一次的def clear 掉;這樣每個basic block的flowfunction變數的值就得到了;

2) 上一步只是得到每乙個block中的liveout def變數,因為在整個routine中block是有資料流關係的,如果當前block有predecessor block,則當前block的livein def變數 = 所有predecessor block的liveout的或運算。

比如:

(3) 計算完每個basic block的livein def值後,自頂向下掃瞄每個block。首先檢查每條指令的src, 然後根據src可以得到其所有的defindex, 如果這個defindex在livein中,那麼就將這個usage新增到這個def中。然後檢查這條指令的dst,這個dst是否已經在livein中,如果是,則將 舊的defindex從livein中刪掉,否則將這個dst的defindex新增到livein中。(此時的livein已經不是原來的livein,只能說初始值是原來的livein吧)。

問題:為什麼要計算livein的def變數?如下圖所示,如果不計算第1個block的liveout def值,那麼在其successor block中use變數i就不知道來自於哪個def了

構建web很簡單,遍歷所有的def變數,如果兩個def變數對應的register相同,且有相同的use,則這兩個def屬於相同的web.

在構建暫存器的衝突圖,也就是構建web之間的衝突圖。在構建衝突圖的過程中,主要分兩步,首先計算出cfg中每個basic block的liveout變數,注意這裡的liveout跟上面的的liveout是有區別的,上面的liveout可以理解為計算變數的可達性,而這裡的liveout指變數的活躍性。然後根據liveout變數,以及當前basic block的指令得到衝突圖。

(1)如何計算每個basic block的liveout?

我們採用自底向上的遍歷方式來計算,

1) 首先假定每乙個block的liveout為null,然後根據演算法livein = liveout + use - def,要計算livein,必須知道use和def,此時我們計算出每乙個block的use和def

2) 然後根據迭代演算法, 當前block的liveout = 所有後繼結點的livein的並集。

注意,在整個過程中,我們只計算了liveout,並沒有計算出livein.

(2) 構建衝突圖,從最後一條指令開始掃瞄block,首先檢查當前指令的dst,然後將此dst跟中的所有變數構建衝突邊;最後重新檢查dst,如果這個dst的所有def都已經kill了(注意,必須是所有的def都已經被kill掉了),那麼就將這個def對應的node從中刪除,然後檢查src,並將src的加入到集合中;

我們嘗試著用r種顏色給圖著色,其中r是可用暫存器的個數。當r>=3時,這是乙個np完全問題。一種方法保證能使得圖的一部分是可r色著色的,只要這個圖的剩餘部分是可r色著色的。另一種方法則在第一種方法沒有窮盡的情況下繼續樂觀地進行處理。後一種方法不會直接產生一種r色著色,但與只使用前一種方法相比,它常常能使圖的更多部分成為可著色的,因此非常有助於它的啟發式值。

前一種方法基於一種簡單但非常有效的觀察,我們稱之為度

<

r 的結點來推廣度

<

r 的node,將其壓入棧(用來存放暫存器分配順序的棧colorstackdepth),並刪除與這個結點相關的衝突邊,同時標記這個node已經被spruned;直到遍歷完所有node;

2.第1步做完之後,如果發現還有node沒有壓入棧colorstackdepth, 很顯然這些沒有壓入棧的node可能是需要spill的node, 因此找出這些沒有壓入棧中spill代價最小的node,並將其壓入棧;並刪除與這個結點有關的衝突邊,同時標記這個node已經被spruned,然後再執行第1步;

3.直到第2步中沒有其它的node結束;

4.然後從棧頂拿出乙個node,根據衝突圖,為其按序分配暫存器,如果沒有可用的暫存器用來分配,則將這個node標記為spill;

5.如果存在某些node需要spill,則執行相應的spill操作;

下面描述下簡單的spill過程,由於在執行prf spill操作時,是通過將prf spill 到crf的,下面舉個簡單的例子:

p4 = p2 & p3; //def p4;

… p5 = p4 | p6; //use p4;

現在假定要將p4 spill到crf中,經過處理後的**如下:

rx = p2 & p3;

… mov py, rx;

p5 = py | p6;

這裡有2點需要注意:

1)這裡的rx是臨時的crf,因為首先是做prf的分配,然後才是crf的分配,因此,這裡可以用乙個臨時的crf處理;

2)在prf spill的時候,首先找到需要需要spill的prf,然後尋找其use,找到use後,在use的指令前新增一條mov py, rx指令,並將這個use的prf替換為py;在這裡有乙個技巧,我們沒有必要碰到乙個use就為其新增乙個mov指令,這就太浪費了,但我們也不能只新增一條mov指令,這樣py的生存期又太長了;折中的方法是適當新增mov指令,比如碰到如下的情況:

p4 = p2 & p3; //def p4;

… (p4) r1 = r2 + r3;

p5 = p4 | p6; //use p4;

那麼此時通過spill操作的**為:

rx = p2 & p3;

… mov py, rx;

(py) r1 = r2 + r3;

p5 = py | p6;

在這裡我們只考慮兩種型別的crf的分配,也就是range crf和single crf.具體步驟如下:

1.首先統計range crf的node數目和single crf的node數目,比如standalone crf的node數目為singlecrfnodecolorstackdepth;

2.遍歷所有的node,如果發現其為range crf的node,則將其儲存到pcolorstack中,但此時的偏移為singlecrfnodecolorstackdepth,也就是range crf從位址

pcolorstack + singlecrfnodecolorstackdepth開始進行儲存;如果發現是single crf,則先通過位域變數先儲存在plivenodemask中;

3.然後通過掃瞄plivenodemask,來確定single crf的node,找出其中衝突邊最少的node,如果這個node的衝突邊< r,則將其壓入棧pcolorstack,並將plivenodemask對應位置為0,並刪除與這個結點有關的衝突邊,如果這個node的衝突邊》=r,則從剩下的node中選擇乙個spillcost最小的node 壓入棧;並將plivenodemask對應位置為0,並刪除與這個結點有關的衝突邊;然後繼續執行第3步,直到plivenodemask為0時結束;

4.然後從棧頂(pcolorstack)拿出乙個node(很顯然,從前面的過程來看,先是給range crf分配暫存器的),根據衝突圖,為其按序分配暫存器,如果沒有可用的暫存器用來分配,則將這個node標記為spill;這裡需要注意的是,對於range crf,必須是為其分配連續的crf;對於single crf來說,選擇乙個最小的沒有分配的crf即可;

暫存器(通用暫存器)

因為學習使用的是王爽的 組合語言 第3版 因此也只能提到8086cpo的暫存器。對於其他而言,原理都是相通的。對於8086暫存器,有14個暫存器,主要是 ax bx cx dx si di sp bp ip cs ss ds es psw。一 通用暫存器 8086的通用暫存器有ax bx cx dx...

暫存器,標誌暫存器

涉及硬體傳輸資料的,往往包含以下內容 1.資料傳輸引腳 資料放 2.控制引腳 怎麼控制 3.狀態引腳 結果如何 暫存器相當於cpu內部的儲存單元,可能是連續排列,相當於 c語言中的陣列。一 8個通用暫存器 16 bit accumulate axah alr0 count cxch clr1 dat...

CS 暫存器 和 IP 暫存器

下面將要介紹的是一組非常非常重要的暫存器,即 cs ip cs ip 兩個暫存器指示了 cpu 當前將要讀取的指令的位址,其中cs 為 段暫存器,而ip 為指令指標暫存器。什麼叫做指示了 cpu 當前將要讀取的指令呢?在 8086 cpu 中,為什麼 cpu 會自動的執行指令呢?這些指令肯定是存放在...