9. 代码生成
9. 代碼生成
代碼生成的核心問題;
- 指令選擇
- 寄存器分配
- 指令調度
指令選擇
為每條中間語言語句選擇恰當的目標機指令或指令序列
首先原則是保證語義的一致性
直接為中間語言語句找到語義一致的指令序列模板:
a=b+c
MOV b, R0 // 將b裝入R0 ADD R0, c // 將c加到R0 MOV R0, a // 存R0的內容到a其次要考慮所生成代碼的效率
一個豐富目標指令集的機器可以為一個給定的操作提供幾種實現方法
假設每條指令在操作數準備好后執行其操作的代價為1
每訪問一次內存則增加代價1
上述代碼執行代價為6
若假設R1和R2中已經分別包含了b和c的值,那么代碼如下:
MOV R1, R0 // 將寄存器R1的內容裝入寄存器R0 ADD R0, R2 // R2的內容加到R0 MOV R0, a // 存R0的內容到a代價為4
假設R1和R2中已經包含了b和c的值,并且b的值以后不再需要
ADD R1, R2 // R2的內容加到R1 MOV R1, a // 存R1的內容到a代價為3
寄存器分配
再分配期間,為程序的某一點選擇駐留在寄存器中的一組變量
在隨后的指派階段,挑出變量將要駐留的具體寄存器,即寄存器賦值
- 分配原則
-
- 盡量讓變量的值或計算結果保留在寄存器中
- 不再被引用的變量所占用的寄存器應盡早釋放
- 局部 / 全局寄存器分配
-
- 局部:在基本塊范圍內的寄存器分配
- 全局:在過程范圍內的寄存器分配
指令調度
- 對指令的執行順序進行適當的調整,從而使得整個程序得到優化的執行效果
- RISC體系結構中,從內存中取入寄存器中的值在隨后的某幾個周期中是不能用的,在這幾個周期中,調出不依賴于該取入值的指令來執行是很重要的
- 調度算法可局限于基本塊內,也可以是更大范圍的全局指令調度;
- 可以是靜態完成指令執行順序的調整,也可以實現動態指令調度
代碼生成過程
寄存器計算機:
- 典型的有16、32或更多個寄存器
-
- 所有操作都在寄存器中進行
- 訪存都通過load / store進行
-
- 內存不能直接運算
結構:
內存:存放溢出的變量
寄存器:進行運算的空間,假設有無限多個
執行引擎:指令的執行
初始時x、y等變量都放到內存中
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FpgnaM13-1642042259498)(…/picture/74.png)]
寄存器機器只支持一種數據類型int
在代碼生成的階段,假設寄存器機器上會有無限多個寄存器
- 因此每個聲明變量和臨時變量都會占用一個(虛擬)寄存器
- 有時把虛擬寄存器分配到物理寄存器的過程稱為寄存器分配
以基本塊為單位的一種簡單代碼生成算法
- 假設基本塊中只有形成a:=b op c和a:=b的TAC語句序列
- 假設目標語言僅含兩類指令
-
- MOV x, y
- x和y是變量或寄存器,但至少有一個是寄存器,該指令的執行是將x的值傳給y
- OP x, y
- OP是對應二元運算op的操作符,x是寄存器,y是變量或者是寄存 ,該指令是使x和y的內容做OP對應的運算,結果保存于寄存器x
指令選擇是可以通過直接對應完成的,所以這個代碼生成算法的核心是處理好在基本塊范圍內如何充分利用寄存器的問題
原則:
- 生成某變量的目標對象值時,盡量讓變量的值或計算結果保留在寄存器中
- 盡可能引用變量在寄存器中的值
- 在同一基本塊內,后面不再被引用的變量所占用的寄存器應盡早釋放
- 當到基本塊出口時,需要將變量的值存放在內存中,這樣從基本塊外進入的變量值都在內存中
直接采用語法樹的后序遍歷,暴力進行羅列
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ENAiPauM-1642042259501)(…/picture/80.png)]
啟發式算法
寄存器描述數組RVALUE,RVALUE[R]描述寄存器R當前存放哪些變量
變量描述數組AVALUE,AVALUE[a]表示變量a的值存放在哪個寄存器中(或不在任何寄存器中)
函數getreg的描述
? getreg功能:以 i: x := y op z 或 i: x := y為參數,返回一個偽寄存器
(1) 對于i: x := y op z
若y∈RVALUE[R],且在語句i之后y在基本塊中不再被引用,同時也不是基本塊出口之后的活躍變量,則返回R;否則,返回一個新的偽寄存器R’
(2) 對于i: x := y
若y∈RVALUE[R],則返回R;否則,返回一個新的偽寄存器R’
(1) 對每個TAC語句 i: x := y op z 或 i: x := y,依次執行下述步驟:
以i為參數,調用getreg(i),返回一個寄存器R,作為存放x現行值的寄存器
利用AVALUE[y]和AVALUE[z],確定出y和z現行值的存放位置
? 如果其現行值在寄存器中,則把寄存器取做Ry和Rz;
? 如果其現行值不在寄存器中,則在相應指令中仍用y和z表示
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iPL1jnRS-1642042259502)(…/picture/75.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uNxsbEpW-1642042259503)(…/picture/76.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8yZ7izzL-1642042259504)(…/picture/77.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3QzFEM3A-1642042259505)(…/picture/78.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-hvTzfKBw-1642042259506)(…/picture/79.png)]
設有以下TAC語句序列組成的基本塊,假設在出口處,b和d是活躍的
| t := a – b | MOV a R0; sub R0 b | R0包含t | t在R0中(a不再在R0) |
| a := b | MOV b R1; | R0包含t R1包含a, b | t在R0中 a, b在R1中 |
| u := a – c | MOV R1 b; sub R1 c | R0包含t R1包含u | t在R0中 u在R1中 |
| v := t + u | add R0 R1 | R0包含v R1包含u | u在R1中 v在R0中 |
| d := v + u | add R0 R1; MOV R0 d | R0包含d | d在R0中和內存中 |
u := a – c | MOV R1 b; sub R1 c | R0包含t R1包含u | t在R0中 u在R1中 |
| v := t + u | add R0 R1 | R0包含v R1包含u | u在R1中 v在R0中 |
| d := v + u | add R0 R1; MOV R0 d | R0包含d | d在R0中和內存中 |
都假定寄存器數目沒有上限(采用簡易的寄存器分配算法)
總結
- 上一篇: FreeRTOS-CortexM4-相关
- 下一篇: vux页面跳转