# 2.2 Go 程序編譯流程
## 2.2.1 第一階段:詞法和語法分析
* `cmd/compile/internal/syntax`(詞法分析器,解析器,語法樹)
在編譯的第一階段,源代碼被 token 化(詞法分析),解析(語法分析),并為每個源構造語法樹文件。每個語法樹都是相應源文件的精確表示對應于源的各種元素的節點,如表達式,聲明和陳述。語法樹還包括位置信息用于錯誤報告和調試信息的創建。
~~~
main -> gc.Main -> amd64.Init -> amd64.LinkArch.Init
-> typecheck -> typecheck -> saveerrors -> typecheckslice
-> checkreturn -> checkMapKeys -> capturevars ->
typecheckinl -> inlcalls -> escapes ->
newNowritebarrierrecChecker -> transformclosure
~~~
## 2.2.2 第二階段:語義分析
* `cmd/compile/internal/gc`(類型檢查,AST變換)
對 AST 進行類型檢查。第一步是名稱解析和類型推斷,它們確定哪個對象屬于哪個標識符,以及每個表達式具有的類型。類型檢查包括某些額外的檢查,例如 “聲明和未使用” 以及確定函數是否終止。
在 AST 上也進行了某些轉換。一些節點基于類型信息被細化,例如從算術加法節點類型分割的字符串添加。其他一些例子是死代碼消除,函數調用內聯和轉義分析。
語義分析的過程中包含幾個重要的操作:逃逸分析、變量捕獲、函數內聯、閉包處理。
## 2.2.3 第三階段:SSA 生成
* `cmd/compile/internal/gc`(轉換為SSA)
* `cmd/compile/internal/ssa`(SSA 傳遞與規則)
在此階段,AST將轉換為靜態單一分配(SSA)形式,這是一種具有特定屬性的低級中間表示,可以更輕松地實現優化并最終從中生成機器代碼。
在此轉換期間,將應用函數內在函數。 這些是特殊功能,編譯器已經教導它們根據具體情況用大量優化的代碼替換。
在AST到SSA轉換期間,某些節點也被降級為更簡單的組件,因此編譯器的其余部分可以使用它們。 例如,內置復制替換為內存移動,并且范圍循環被重寫為for循環。 其中一些目前發生在轉換為SSA之前,由于歷史原因,但長期計劃是將所有這些都移到這里。
然后,應用一系列與機器無關的傳遞和規則。 這些不涉及任何單個計算機體系結構,因此可以在所有`GOARCH`?變體上運行。
這些通用過程的一些示例包括消除死代碼,刪除不需要的零檢查以及刪除未使用的分支。通用重寫規則主要涉及表達式,例如用常量值替換某些表達式,以及優化乘法和浮點運算。
~~~
initssaconfig -> peekitabs -> funccompile ->
finit -> compileFunctions -> compileSSA -> buildssa -> genssa ->
-> typecheck -> checkMapKeys -> dumpdata -> dumpobj
~~~
## 2.2.4 第四階段:機器碼生成
* `cmd/compile/internal/ssa`(底層SSA和架構特定的傳遞)
* `cmd/internal/obj`(生成機器碼)
編譯器的機器相關階段以“底層”傳遞開始,該傳遞將通用值重寫為其機器特定的變體。例如,在 amd64 存儲器操作數上是可能的,因此可以組合許多加載存儲操作。
請注意,較低的通道運行所有特定于機器的重寫規則,因此它當前也應用了大量優化。
一旦SSA“降低”并且更加特定于目標體系結構,就會運行最終的代碼優化過程。這包括另一個死代碼消除傳遞,移動值更接近它們的使用,刪除從未讀取的局部變量,以及寄存器分配。
作為此步驟的一部分完成的其他重要工作包括堆棧框架布局,它將堆棧偏移分配給局部變量,以及指針活動分析,它計算每個 GC 安全點上的堆棧指針。
在SSA生成階段結束時,Go 函數已轉換為一系列`obj.Prog`?指令。它們會被傳遞給裝載器(`cmd/internal/obj`),將它們轉換為機器代碼并寫出最終的目標文件。目標文件還將包含反射數據,導出數據和調試信息。
- 第一部分 :基礎篇
- 第1章 Go語言的前世今生
- 1.2 Go語言綜述
- 1.3 順序進程通訊
- 1.4 Plan9匯編語言
- 第2章 程序生命周期
- 2.1 從go命令談起
- 2.2 Go程序編譯流程
- 2.3 Go 程序啟動引導
- 2.4 主Goroutine的生與死
- 第3 章 語言核心
- 3.1 數組.切片與字符串
- 3.2 散列表
- 3.3 函數調用
- 3.4 延遲語句
- 3.5 恐慌與恢復內建函數
- 3.6 通信原語
- 3.7 接口
- 3.8 運行時類型系統
- 3.9 類型別名
- 3.10 進一步閱讀的參考文獻
- 第4章 錯誤
- 4.1 問題的演化
- 4.2 錯誤值檢查
- 4.3 錯誤格式與上下文
- 4.4 錯誤語義
- 4.5 錯誤處理的未來
- 4.6 進一步閱讀的參考文獻
- 第5章 同步模式
- 5.1 共享內存式同步模式
- 5.2 互斥鎖
- 5.3 原子操作
- 5.4 條件變量
- 5.5 同步組
- 5.6 緩存池
- 5.7 并發安全散列表
- 5.8 上下文
- 5.9 內存一致模型
- 5.10 進一步閱讀的文獻參考
- 第二部分 運行時篇
- 第6章 并發調度
- 6.1 隨機調度的基本概念
- 6.2 工作竊取式調度
- 6.3 MPG模型與并發調度單
- 6.4 調度循環
- 6.5 線程管理
- 6.6 信號處理機制
- 6.7 執行棧管理
- 6.8 協作與搶占
- 6.9 系統監控
- 6.10 網絡輪詢器
- 6.11 計時器
- 6.12 非均勻訪存下的調度模型
- 6.13 進一步閱讀的參考文獻
- 第7章 內存分配
- 7.1 設計原則
- 7.2 組件
- 7.3 初始化
- 7.4 大對象分配
- 7.5 小對象分配
- 7.6 微對象分配
- 7.7 頁分配器
- 7.8 內存統計
- 第8章 垃圾回收
- 8.1 垃圾回收的基本想法
- 8.2 寫屏幕技術
- 8.3 調步模型與強弱觸發邊界
- 8.4 掃描標記與標記輔助
- 8.5 免清掃式位圖技術
- 8.6 前進保障與終止檢測
- 8.7 安全點分析
- 8.8 分代假設與代際回收
- 8.9 請求假設與實務制導回收
- 8.10 終結器
- 8.11 過去,現在與未來
- 8.12 垃圾回收統一理論
- 8.13 進一步閱讀的參考文獻
- 第三部分 工具鏈篇
- 第9章 代碼分析
- 9.1 死鎖檢測
- 9.2 競爭檢測
- 9.3 性能追蹤
- 9.4 代碼測試
- 9.5 基準測試
- 9.6 運行時統計量
- 9.7 語言服務協議
- 第10章 依賴管理
- 10.1 依賴管理的難點
- 10.2 語義化版本管理
- 10.3 最小版本選擇算法
- 10.4 Vgo 與dep之爭
- 第12章 泛型
- 12.1 泛型設計的演進
- 12.2 基于合約的泛型
- 12.3 類型檢查技術
- 12.4 泛型的未來
- 12.5 進一步閱讀的的參考文獻
- 第13章 編譯技術
- 13.1 詞法與文法
- 13.2 中間表示
- 13.3 優化器
- 13.4 指針檢查器
- 13.5 逃逸分析
- 13.6 自舉
- 13.7 鏈接器
- 13.8 匯編器
- 13.9 調用規約
- 13.10 cgo與系統調用
- 結束語: Go去向何方?