1. 程式人生 > >編譯器後端,暫存器分配演算法

編譯器後端,暫存器分配演算法

暫存器分配,是通過將程式變數儘可能地分配到暫存器,從而提高程式執行速度的一種方法。暫存器是編譯器優化中最為重要的問題之一(好的暫存器分配能夠提高程式執行速度超過250%);也是編譯器理論中最熱點的研究領域之一(研究界已經提出來大量暫存器分配相關的演算法)。 1. 圖著色(graph coloring)方法是解決暫存器分配問題最常用的方法。     利用相交圖(interference graph)來表示程式變數的生命期是否相交,將暫存器分配給變數的問題,可以近似地看成是給相交圖著色:相交圖中,相交的節點不能著同一顏色;每一種顏色對應一個暫存器。Chaitin等人最早提出了基於圖著色的暫存器分配方法其著色思路採用了Kempe的著色方法,即,任意一個鄰居節點數目少於k的節點,都能夠被k著色。判斷一個圖是否能夠被k(k>=3)種顏色著色,即k著色問題,被Karp證明是一個NP-complete問題。
    但是,暫存器分配不僅僅是圖著色的問題。當暫存器數目不足以分配某些變數時,就必須將這些變數溢位到記憶體中,該過程成為spill。最小化溢位代價的問題,也是一個NP-complete問題。如果簡化該問題——假設所有溢位代價相等,那麼最小化溢位代價的問題,等價於k著色問題,仍然是NP-complete問題。     此外,如果兩個變數的生命期僅僅因為出現在同一個拷貝指令中而相鄰,那麼,通過將這兩個變數分配到同一個暫存器,就可以消除該拷貝指令,成為coalescing。這個方向的努力在Chaitin的文章以後的1/4個世紀,成為推動暫存器分配的主要動力之一,湧現出了包括aggressive coalescing,conservative coalescing和optimistic coalescing。但是,將兩個變數分配到同一個暫存器,等價於將這兩個變數合併成同一個變數,生命期合併,因而會加劇相交圖的聚簇現象,降低相交圖的可著色性。Bouchez等人證明了目前的coalescing問題都是NP-complete的。
    為了降低相交圖的聚簇現象,提高相交圖的可著色性,可以通過將變數拷貝給一個臨時變數,並將以後對該變數的使用替換成對該臨時變數的使用,從而將一個變數的生命期分解成兩個變數的生命期,稱為live range splitting。顯然,這是一個與coalescing的作用相反的過程。Bouchez等人考慮了該方法的複雜度。     此外,暫存器分配還需要考慮暫存器別名(aliasing)和預著色(pre-coloring)的問題。暫存器別名是指,在某些體系結構中,一個暫存器的賦值可能會影響到另外一個暫存器。比如,在x86中,對AX暫存器的賦值,會影響AL和AH暫存器。預著色是指,某些變數必須被分配到特定的暫存器。比如,許多體系結構會採用特定暫存器來傳遞函式引數。
    George和Appel發展了Chaitin的演算法,更好地考慮了coalescing過程和賦值過程,以及各過程之間的迭代,在基於圖著色的暫存器分配方法中具有廣泛的影響。 3. 線性掃描演算法     線性掃描演算法(linear scan)最早由Poletto和Sarkar提出,具有很大的影響力,在gcc、llvm和Java HotSpot編譯器中得到了實現。線性掃描演算法簡化了基於圖著色的分配問題,考慮的是對一個有序的生命期序列的著色,提高了暫存器分配的速度(線性速度),而沒有過度降低對暫存器的利用。 4. 整數線性規劃演算法     Goodwin和Wilken提出了最早的對暫存器分配問題的整數線性規劃演算法(integer linear programming),雖然在最壞情況下具有指數級複雜度,但是能夠更充分的利用暫存器。 5. PBQP演算法     在編譯器領域,Partitioned Boolean Quadratic Problem被用於解決指令選擇和暫存器分配問題。對於暫存器分配而言,PBQP演算法的複雜度為VK^3,其中V是變數的數目,K是暫存器的數目。Hames等人的實驗表明,他們的PBQP實現能夠為SPEC CPU 2000中的97.4%的函式找到最優暫存器分配方案。 6. Multi-Flow Commodities演算法     Koes和Goldstein等人最先將暫存器分配視為Multi-Flow Commodities問題加以解決。 7. 基於Static Single Assignment (SSA)的暫存器分配     暫存器分配問題的一個重要突破發生在2005年,當時3個研究團隊獨立的證明了,採用SSA表示的程式的相交圖是弦圖(chordal graph)。而弦圖是能夠在多項式時間內著色的。基於SSA形式的暫存器分配方法,可以從三個方面獲益:更小的暫存器壓力;spilling和暫存器賦值過程之間的分離;更簡單的暫存器賦值演算法。