編譯器如何以 ISA 為目標
編譯器是一個翻譯員,把人寫的高階程式碼(C、Rust、Go 等)轉成某個特定 ISA 看得懂的確切機器指令。你自然地寫一次「a = b + c」;編譯器把它改寫成目標 ISA 的特定載入、加法與儲存指令,挑選真實的暫存器並安排記憶體。以一個 ISA 為目標,意思是產生遵守那個 ISA 契約——它的指令、它的暫存器、它的呼叫慣例——的程式碼,使產出的程式能在那個家族的晶片上跑。
走一遍這份工作的一小片。給定「a = b + c」,且變數住在記憶體裡,編譯器必須:決定用哪些暫存器、發出一條載入把 b 帶進一個暫存器、一條載入把 c 帶進另一個、一條加法把它們合起來、一條儲存把 a 寫回去——全用目標 ISA 真實的指令編碼。它也遵循 ISA 的呼叫慣例,好讓函式能互相呼叫(哪些暫存器傳遞引數、哪些必須保留),並挑選符合指令格式的定址模式與立即值。同一個原始程式,為兩個不同的 ISA 編譯,會產出兩串完全不同的機器指令。
這就是「ISA 作為軟硬體握手」的日常意義:編譯器是軟體那一側伸過去搆硬體的手,而 ISA 是它們相會所用的約定語言。這也是為什麼一個 ISA 能承載一整個軟體世界——任何擁有針對該 ISA 之後端的東西,都能在它上面跑。一個誠實的提醒:編譯器做的遠不只是機械式翻譯。為了讓一台 RISC 機器的簡單指令具有競爭力,它在最佳化上下足功夫——把熱門的值留在暫存器、重排與消除指令、展開迴圈——這是簡單 ISA 在實務上表現如此優異的一大原因。
原始碼:「a = b + c」。為 RISC-V 編譯後,它可能變成「lw x5, 0(x10)」(載入 b)、「lw x6, 4(x10)」(載入 c)、「add x5, x5, x6」(加)、「sw x5, 8(x10)」(儲存 a)。一行 C,四條目標指令。
編譯器把一行原始碼變成目標 ISA 真實的載入/計算/儲存指令。
編譯器不是一個逐字死譯的翻譯員。一個快速程式的速度有很大部分來自編譯器最佳化——暫存器配置、重排、移除多餘工作——這正是簡單的 RISC 指令得以保持競爭力的方式。