官方文檔:Go編譯的四個階段
https://golang.org/src/cmd/compile/README,由于本人翻譯水平有限,翻譯不當(dāng)之處煩請指出。Go 編譯器簡介
cmd/compile 目錄包含構(gòu)成 Go 編譯器的主包。編譯器在邏輯上可以分為四個階段,我們將在包含其代碼的包列表中簡要描述這四個階段。
在提到編譯器時,有時可能會聽到術(shù)語front-end|前端和back-end|后端。粗略地說,這些轉(zhuǎn)化為我們將在此列出的前兩個和后兩個階段。第三個術(shù)語,“middle-end|中間端”,通常指的是在第二階段進(jìn)行的大部分工作。

請注意 go/* 路徑下的包系列,例如 go/parser 和 go/types,與編譯器沒有關(guān)系。由于編譯器最初是用 C編寫的,所以開發(fā) go/* 包是為了支持編寫使用 Go 代碼的工具,比如 gofmt 和 vet。
需要澄清一下,名稱gc這里代表Go compiler,與大寫的GC(garbage collection垃圾回收)沒有什么關(guān)系。
1. 語法分析
cmd/compile/internal/syntax(詞法分析器,解析器,語法樹)
在編譯的第一階段,對每個源文件代碼進(jìn)行標(biāo)記(詞法分析) ,解析(語法分析) ,并構(gòu)建一個語法樹。
每個語法樹都精準(zhǔn)地展現(xiàn)了源文件相對應(yīng)的內(nèi)容,其中的節(jié)點(diǎn)對應(yīng)于源文件的各種元素,如表達(dá)式、聲明和語句。語法樹還包括了用于錯誤報告和創(chuàng)建調(diào)試信息的位置信息。
2. 類型檢查和 AST 轉(zhuǎn)換
cmd/compile/internal/gc(創(chuàng)建編譯器AST、類型檢查、AST轉(zhuǎn)換)
gc 包包含一個 AST 定義,是用C語言編寫的。所有代碼都是用它編寫的,所以 gc 包必須做的第一件事就是將語法包的語法樹轉(zhuǎn)換為編譯器的 AST 表示。這個額外的步驟將來可能會被重構(gòu)。
然后對 AST 進(jìn)行類型檢查。第一步是名稱解析和類型推斷,確定哪個對象屬于哪個標(biāo)識符,以及每個表達(dá)式具有哪個類型。類型檢查包括某些額外的檢查,例如“聲明了且未使用的”的變量檢查,以及確定函數(shù)是否停止。
某些轉(zhuǎn)換也在 AST 上完成的。有些節(jié)點(diǎn)根據(jù)類型信息進(jìn)行了細(xì)化,例如將字符串添加從算術(shù)加法節(jié)點(diǎn)類型中分離出來。其他一些例子包括無效代碼刪除分析、函數(shù)調(diào)用內(nèi)聯(lián)和逃逸分析。
3. 通用 SSA
cmd/compile/internal/gc(轉(zhuǎn)換為SSA)cmd/compile/internal/ssa(SSA傳遞和規(guī)則)
在這個階段,AST 被轉(zhuǎn)換為 Static Single Assignment (SSA)形式,這是一種具有特定屬性的較低級的中間表現(xiàn)形式,可以更容易地實(shí)現(xiàn)優(yōu)化并最終從中生成機(jī)器代碼。
在此轉(zhuǎn)換過程中,將會使用函數(shù)的內(nèi)置函數(shù)。這些都是特殊的函數(shù),編譯器已經(jīng)學(xué)會了在個別例子的基礎(chǔ)上使用經(jīng)過大量優(yōu)化的代碼來替代它。
在 AST 到 SSA 的轉(zhuǎn)換過程中,某些節(jié)點(diǎn)也會被降級為更簡單的組件,這樣編譯器的其余部分就可以使用它們。例如,內(nèi)置的copy函數(shù)會被內(nèi)存移動替代,range循環(huán)被重寫為 for 循環(huán)。由于歷史原因,其中一些情況目前是發(fā)生在轉(zhuǎn)換為 SSA之前,但長期計(jì)劃是將所有這些放到這里處理。
然后,應(yīng)用一系列與機(jī)器無關(guān)的通行證和規(guī)則。它們不會涉及任何單一的計(jì)算機(jī)體系結(jié)構(gòu),因此可以在所有 GOARCH 變體上運(yùn)行。
這些通用通過的一些例子包括無效代碼刪除,移除不必要的零值檢查,以及移除未使用的分支。通用重寫規(guī)則主要涉及表達(dá)式,例如用常量值替換某些表達(dá)式,以及優(yōu)化乘法和浮點(diǎn)運(yùn)算。
4. 生成機(jī)器代碼
cmd/compile/internal/ssa(SSA 降低和特定架構(gòu)傳遞)cmd/internal/obj(機(jī)器代碼生成)
編譯器與機(jī)器相關(guān)的階段從lower開始,它將通用的值重寫為它們與機(jī)器相關(guān)的變量。例如,在 amd64架構(gòu)上可以使用內(nèi)存操作數(shù),因此可以組合很多負(fù)載存儲操作。
請注意,下層的傳遞運(yùn)行所有特定機(jī)器的重寫規(guī)則,因此它當(dāng)前也做了許多優(yōu)化。
一旦 SSA 被lower且更特定于目標(biāo)體系架構(gòu),就會運(yùn)行最終的代碼優(yōu)化傳遞。這包括另一個無效代碼刪除/值傳遞,移動更接近其用途的值,刪除永遠(yuǎn)不會讀取的本地變量,以及寄存器分配。
作為這個步驟的一部分,其他重要的工作還包括棧幀布局,該布局為局部變量分配堆棧偏移量,以及指針存活分析,它計(jì)算堆棧上哪些指針在每個GC 安全點(diǎn)處于活躍狀態(tài)。
在 SSA 生成階段的末尾,Go 函數(shù)會被轉(zhuǎn)換為一系列 obj程序指令。它們被傳遞給匯編程序(cmd/internal/obj) ,然后將它們轉(zhuǎn)換為機(jī)器代碼并寫出最終的目標(biāo)文件。對象文件還將包含反射數(shù)據(jù)、導(dǎo)出數(shù)據(jù)和調(diào)試信息。
進(jìn)一步閱讀
如果你想要更深入了解 SSA 包的工作原理,包括它的傳遞和規(guī)則,請?jiān)L問cmd/compile/internal/ssa/README.md[1].
參考資料
cmd/compile/internal/ssa/README.md: https://golang.org/src/cmd/compile/internal/ssa/README.md
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。
堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio
