編譯器與程式碼產生

後端(compiler back end)

當最佳化器完成時,程式仍然是編譯器中立、與機器無關的形式——一串對無限多個想像暫存器進行的抽象操作。但真實的處理器有固定的指令集,且只有少數幾個真正的暫存器。後端就是編譯器跨越這道鴻溝的部分:它把抽象的 IR 轉成某個特定目標(x86-64、ARM、RISC-V)的具體機器指令。

後端有四個經典工作,大致依序執行。指令選擇(instruction selection)把每個抽象 IR 操作對應到一個或多個真正的機器指令(如果晶片有融合指令,一個加法接乘法可能變成單一融合指令)。指令排程(instruction scheduling)重排這些指令,好讓處理器的管線保持忙碌、避免停頓。暫存器配置(register allocation)把無限的想像暫存器對應到晶片少數幾個真實暫存器,當不夠用時把多出來的溢出(spill)到堆疊。最後輸出(emission)寫出程式真正的位元組(或組合語言文字)。這些階段極度依賴目標,因為它們正好編碼了「這顆晶片能做什麼」。

它重要在於:後端正是為什麼同一個 C 函式在 Intel 筆電與在手機上編譯出截然不同的組合語言——相同的 IR、不同的目標。當你讀反組譯、發現你沒寫過的指令時,也是看這裡:那些是後端的選擇。一個提醒:後端只能用中端給它的東西。如果最佳化器已經刪掉一個檢查,後端再小心也救不回來;你讀到的組合語言反映了管線中每一個更早的決定,不只是程式碼產生。

// IR: %sum = add i32 %a, %b // x86-64 後端可能輸出: // lea eax, [rdi + rsi] ; 指令選擇 + 暫存器配置 // ret $ gcc -O2 -S add.c # 產生 add.s,也就是後端寫出的組合語言

後端把一個抽象的加法轉成真正的 x86-64 lea 指令,並選定了指令與暫存器。

連結與目的檔格式不屬於這裡所談後端的程式碼產生——輸出階段產生一個目的檔,但跨檔解析符號是連結器的工作,另行討論。

又称
backendcode generatorcodegenthe target-machine end編譯器後端程式碼產生器