本文是閱讀了vladimir makarov的《fighting register pressure in gcc》後的小結,一些文字是直接翻譯過來的。
gcc暫存器分配工作依次執行下面階段:
一 regmove(對應regmove.c)
當目的和源暫存器應該相同時,生成move指令,以滿足雙運算元指令的約束。
二 指令排程(the instruction scheduler)
降低「暫存器壓力」(即分配過程中硬暫存器短缺的現象)。
三 尋找暫存器類(對應regclass.c)
執行2遍工作。
1 為每個待分配的偽暫存器找到首選的(preferred)和候選的(alternative)暫存器類。
2 暗中執行**選擇(code selection)。
3 掃瞄偽暫存器的一般資訊(引用次數、賦值次數、引用偽暫存器的第一條和最後一條指令)。
四 區域性分配(對應local-alloc.c)
1 對活躍於乙個基本塊內的偽暫存器分配硬暫存器。結果存到全域性陣列reg_renumber中。
2 執行一些暫存器拼接工作,例如,如果二個或更多個不衝突的偽暫存器被若干move指令攪亂了,它們通常能夠分配給相同的硬暫存器。
3 執行簡單的複製傳播和常量傳播(在update_reg_equiv函式中實現)。
五 全域性分配(對應global.c)
1 對活躍於多個基本塊內的偽暫存器分配硬暫存器。當它找到比區域性分配更有利的硬暫存器時,它可能會改變區域性分配的結果。
為每個偽暫存器生成乙個位向量,該向量包含與偽暫存器衝突的硬暫存器,為每個偽暫存器構建乙個衝突圖,並根據下面的優先權值對所有偽暫存器排序:
( log2(nrefs) * freq / live_length ) * size
這裡nrefs是偽暫存器出現的次數,freq是它使用的頻率,live_length是偽暫存器在指令中活躍範圍的長度,size是它在硬暫存器中的大小。
之後,全域性分配器試圖按由高到低的優先權順序將硬暫存器分配給偽暫存器。
如果當前偽暫存器獲得了乙個硬暫存器,該硬暫存器被加入(與該偽暫存器衝突的所有偽暫存器的)硬暫存器衝突向量。
2 通過將偽暫存器賦給硬暫存器,試圖將move指令中遇到的偽暫存器與硬暫存器接合起來。
六 reload階段(對應reload.c和reload1.c)
1 如果rtl中的運算元的不滿足約束,那麼就轉換使其滿足這些約束。這裡的偽暫存器可能會被轉換為硬暫存器、記憶體或常量。
reload遍跟在全域性和區域性分配之後,根據需要它可能會改變前二者的賦值。
例如:如果硬暫存器a分配給了偽暫存器v1,但引用v1的指令要求用其它暫存器類,則會生成
move a to c
move c to b
其中b是滿足v1指令暫存器類的硬暫存器,而c可能是記憶體。如果b或c已被偽暫存器v2占用,則呼叫global.c中的retry_global函式為v2分配其它的硬暫存器,如果分配失敗,則v2被放於程式的堆疊中。
2 消除虛擬的硬暫存器(如實參指標)、和實際的硬暫存器(如幀指標)。
3 對那些溢位的硬暫存器,以及最終未獲得硬暫存器的偽暫存器,將堆疊槽賦給它們。
4 複製傳播等。
七 postreload階段(對應postreload.c)
根據基本塊上下文,刪除reload階段生成的多餘的move, load, store等指令。
編譯原理 編譯器各階段工作
1.詞法分析 詞法分析器根據詞法規則識別出源程式中的各個記號 token 每個記號代表一類單詞 lexeme 源程式中常見的記號可以歸為幾大類 關鍵字 識別符號 字面量和特殊符號。詞法分析器的輸入是源程式,輸出是識別的記號流。詞法分析器的任務是把原始檔的字元流轉換成記號流。本質上它檢視連續的字元然後...
編譯原理 編譯器各階段工作
1.詞法分析 詞法分析器根據詞法規則識別出源程式中的各個記號 token 每個記號代表一類單詞 lexeme 源程式中常見的記號可以歸為幾大類 關鍵字 識別符號 字面量和特殊符號。詞法分析器的輸入是源程式,輸出是識別的記號流。詞法分析器的任務是把原始檔的字元流轉換成記號流。本質上它檢視連續的字元然後...
LCC編譯器的源程式分析 48 暫存器分配
在 lcc裡是使用非常簡單的暫存器分配演算法,並且侷限於森林裡的臨時變數的分配。下面就來分析暫存器分配的 001 int askregvar symbol p,symbol regs 011 else if p temporary 015 else if r askreg regs,vmask nu...