6.3.3 為跳轉指令產生匯編代碼
在這1小節中,我們要為“有條件跳轉”、“無條件跳轉”和“間接跳轉”產生相應的匯編指令。中間指令的4元式以下所示:
<運算符opcode,目的操作數DST,源操作數SRC1,源操作數SRC2>
(1) 有條件跳轉,例如“if (a <= b) goto BB2;”,4元式為:
<JLE,BB2,a,b>
////////對應的匯編代碼//////////
movl a, %eax //把SRC1的值暫存在寄存器eax中
cmpl b, %eax //比較eax和b的大小
jle .BB2 //進行有條件跳轉
(2) 無條件跳轉,例如“goto BB3;”,4元式為:
<JMP,BB3,NULL,NULL>
////////////對應的匯編代碼///////////////
jmp .BB3
(3) 間接跳轉,例如“goto (BB4,BB5,BB6)[t0];”,在翻譯switch語句時會產生間接跳轉指令,其4元式為:
<IJMP,[BB4,BB5,BB6,NULL],t0,NULL>
////////////對應的匯編代碼///////////////
.data
swtchTable1: .long .BB4
.long .BB5
.long .BB6
.text
jmp*swtchTable1(,%eax,4)
由于跳轉指令位于基本塊的最末尾,我們在第6.2節的“圖6.2.3 EmitBBlock()”中,已對x87棧頂寄存器做過回寫操作。在本小節,我們還要中調用ClearRegs函數,對x86 CPU中的寄存器進行回寫,如圖6.3.7第7行、第26行和第54行所示??刂屏餍袑㈦S著跳轉指令的履行而進入另外一個基本塊,而UCC編譯器僅在同1基本塊內對公共子表達式進行重用,因此,即便跳轉指令里的操作數DST、SRC1和SRC2是臨時變量,我們沒有必要為其“長時間”分配寄存器,換言之,我們不會調用在前面的章節中介紹過的AllocateReg函數,而是調用如圖6.3.7第13行所示的PutInReg函數,把操作數SRC1的值暫存到某個寄存器中。
圖6.3.7第1至30行的EmitBranch函數用于為“有條件跳轉”產生匯編代碼,當操作數是浮點數時,我們在第8行調用EmitX87Branch函數來處理。當操作數是整數時,我們會在第11行做進1步判斷。由于常數會以“立即數”的情勢存在于代碼區中,當程序運行時,CPU會從代碼區里預讀機器指令,從而把立即數也加載入CPU,因此當操作數SRC2是常數時,我們可以沒必要把SRC1的值加載到寄存器中,這不會違背“同1條X86匯編指令的兩個操作數不可以都在內存中”的尋址要求。這意味著我們可以生成形如“cmpl $3, a”的比較指令,但不可以生成形如“cmpl b, a”的比較指令。圖6.3.7第11至14行會在“SRC2存在且SRC2不是常數時”,通過第13行調用的PutInReg函數,把SRC1的值加載到某個寄存器中。第17行判斷SRC1的值是不是在寄存器中,如果已載入寄存器,我們可在第18行把中間指令里的源操作數SRC1改成SRC1->reg,以后在便可生成形如“cmpl b, %eax”的比較指令。第28行調用PutASMCode函數來產生比較和跳轉指令,形如“cmpl b, %eax;jle .BB2 ”。
圖6.3.7 EmitBranch()和EmitJump()
圖6.3.7第31至39行的EmitX87Branch用于處理浮點數的有條件跳轉,我們會在第34行先把操作數SRC1加載到x87棧頂寄存器中,然后在第37行生成浮點數比較和跳轉的指令,形如第35至36行的模板所示。我們已在“1.5 結合C語言來學匯編”介紹過這些指令,這里不再重復。圖6.3.7第50至56行的函數EmitJump會為無條件跳轉生成匯編代碼。
接下來,我們來討論1下為“間接跳轉”產生匯編代碼的函數EmitIndirectJump,如圖6.3.8所示。圖6.3.8第7行調用PutInReg函數把操作數SRC1加載到寄存器中,第10行用于產生“.data”,表示接下來的內容為數據區,第11至17行創建1個名稱形如“swtchTable1”的符號對象,第23至33行會在數據區中創建跳轉表,形如第19至21行所示。第36行輸出“.text”,表示接下來的內容為代碼區。由于已在第7行把SRC1加載到寄存器reg中,我們便可在匯編指令中使用該寄存器的名稱,為此我們要在第37行把中間指令的SRC1改成相應的寄存器。第38行調用ClearRegs對x86 CPU中的寄存器進行了必要的回寫,第41行調用PutASMCode產生了形如“jmp*swtchTable1(,%eax,4)”的匯編指令。
圖6.3.8 EmitIndirectJump()
上一篇 狀態模式和策略模式比較