編譯—連結—載入工具鏈

編譯器的各階段(詞法分析、語法分析、語意分析、最佳化、產生程式碼)

編譯器是工具鏈的大腦:它接受一個編譯單元的人類可讀文字,把它轉成給機器的低階指令。它不是一躍而成,而是經由一連串內部階段,每一步把程式轉成更貼近底層一點的形式。你從不直接看到這些階段,但認識它們能精確解釋各類錯誤訊息從何而來。

依序走過它們:首先是詞法分析(又稱掃描),把原始文字切成 token,即最小的有意義片段,就像你在心裡把句子拆成單字;int x = 3; 變成 token:int、x、=、3 以及分號。接著語法分析依語言的文法把這些 token 組成捕捉結構的樹狀形狀,能抓出像漏掉分號或大括號不成對之類的問題。然後語意分析檢查意義:這個變數存在嗎、型別相符嗎、你是不是把數字加到字串上。之後,最佳化重寫程式,讓它以更快或更省空間的方式做同樣的工作,例如在編譯期把 2+3 摺疊成 5,或刪除永遠不會執行的程式碼。最後產生程式碼(codegen)為你的目標處理器發出真正的組合語言指令。

前三個階段合起來鬆散地稱為前端(它們理解你的語言);後兩個是後端(它們產生特定機器的輸出)。這種切分正是為何一個編譯器能藉由搭配不同前端與後端來支援多種語言或多種處理器。實務上:關於多餘 token 的錯誤來自詞法或語法分析;「use of undeclared identifier」或「incompatible types」來自語意分析;而一個能建構、卻只在最佳化時行為失常的程式,往往是最佳化器被允許利用的未定義行為的徵兆。

原始碼: total = a + b * 2; 詞法分析 -> token: total = a + b * 2 ; 語法分析 -> 樹: assign(total, add(a, mul(b, 2))) 語意分析 -> 檢查 a、b 已宣告且為數值 最佳化 -> 若 b 是常數 3: total = a + 6; 產生程式碼 -> imul / add 機器指令

一行小程式碼穿過每個階段,每一步都更接近機器碼。

最佳化絕不可改變正確程式可觀察的行為,但它被允許假設你的程式碼沒有未定義行為,這就是為何未定義行為會讓最佳化過的建構與未最佳化的版本行為大不相同。

又稱
compiler pipelinecompiler front-end and back-end編譯器流程