🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 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`),将它们转换为机器代码并写出最终的目标文件。目标文件还将包含反射数据,导出数据和调试信息。