官方文档:Go编译的四个阶段

共 2754字,需浏览 6分钟

 ·

2021-08-18 15:07

今天给大家带来一篇关于Go编译的四个阶段的文章,大家可以学习一下,正好为后续一篇文章做一下铺垫。原文见https://golang.org/src/cmd/compile/README,由于本人翻译水平有限,翻译不当之处烦请指出。

Go 编译器简介

cmd/compile 目录包含构成 Go 编译器的主包。编译器在逻辑上可以分为四个阶段,我们将在包含其代码的包列表中简要描述这四个阶段。

在提到编译器时,有时可能会听到术语front-end|前端back-end|后端。粗略地说,这些转化为我们将在此列出的前两个和后两个阶段。第三个术语,“middle-end|中间端”,通常指的是在第二阶段进行的大部分工作。

请注意 go/* 路径下的包系列,例如 go/parser 和 go/types,与编译器没有关系。由于编译器最初是用 C编写的,所以开发 go/* 包是为了支持编写使用 Go 代码的工具,比如 gofmt 和 vet

需要澄清一下,名称gc这里代表Go compiler,与大写的GC(garbage collection垃圾回收)没有什么关系。

1. 语法分析

  • cmd/compile/internal/syntax (词法分析器,解析器,语法树)

在编译的第一阶段,对每个源文件代码进行标记(词法分析) ,解析(语法分析) ,并构建一个语法树。

每个语法树都精准地展现了源文件相对应的内容,其中的节点对应于源文件的各种元素,如表达式、声明和语句。语法树还包括了用于错误报告和创建调试信息的位置信息。

2. 类型检查和 AST 转换

  • cmd/compile/internal/gc (创建编译器 AST、类型检查、 AST 转换)

gc 包包含一个 AST 定义,是用C语言编写的。所有代码都是用它编写的,所以 gc 包必须做的第一件事就是将语法包的语法树转换为编译器的 AST 表示。这个额外的步骤将来可能会被重构。

然后对 AST 进行类型检查。第一步是名称解析和类型推断,确定哪个对象属于哪个标识符,以及每个表达式具有哪个类型。类型检查包括某些额外的检查,例如“声明了且未使用的”的变量检查,以及确定函数是否停止。

某些转换也在 AST 上完成的。有些节点根据类型信息进行了细化,例如将字符串添加从算术加法节点类型中分离出来。其他一些例子包括无效代码删除分析、函数调用内联和逃逸分析。

3. 通用 SSA

  • cmd/compile/internal/gc (转换为 SSA)
  • cmd/compile/internal/ssa (SSA 传递和规则)

在这个阶段,AST 被转换为 Static Single Assignment (SSA)形式,这是一种具有特定属性的较低级的中间表现形式,可以更容易地实现优化并最终从中生成机器代码。

在此转换过程中,将会使用函数的内置函数。这些都是特殊的函数,编译器已经学会了在个别例子的基础上使用经过大量优化的代码来替代它。

在 AST 到 SSA 的转换过程中,某些节点也会被降级为更简单的组件,这样编译器的其余部分就可以使用它们。例如,内置的copy函数会被内存移动替代,range循环被重写为 for 循环。由于历史原因,其中一些情况目前是发生在转换为 SSA之前,但长期计划是将所有这些放到这里处理。

然后,应用一系列与机器无关的通行证和规则。它们不会涉及任何单一的计算机体系结构,因此可以在所有 GOARCH 变体上运行。

这些通用通过的一些例子包括无效代码删除,移除不必要的零值检查,以及移除未使用的分支。通用重写规则主要涉及表达式,例如用常量值替换某些表达式,以及优化乘法和浮点运算。

4. 生成机器代码

  • cmd/compile/internal/ssa (SSA 降低和特定架构传递)
  • cmd/internal/obj (机器代码生成)

编译器与机器相关的阶段从lower开始,它将通用的值重写为它们与机器相关的变量。例如,在 amd64架构上可以使用内存操作数,因此可以组合很多负载存储操作。

请注意,下层的传递运行所有特定机器的重写规则,因此它当前也做了许多优化。

一旦 SSA 被lower且更特定于目标体系架构,就会运行最终的代码优化传递。这包括另一个无效代码删除/值传递,移动更接近其用途的值,删除永远不会读取的本地变量,以及寄存器分配。

作为这个步骤的一部分,其他重要的工作还包括栈帧布局,该布局为局部变量分配堆栈偏移量,以及指针存活分析,它计算堆栈上哪些指针在每个GC 安全点处于活跃状态。

在 SSA 生成阶段的末尾,Go 函数会被转换为一系列 obj程序指令。它们被传递给汇编程序(cmd/internal/obj) ,然后将它们转换为机器代码并写出最终的目标文件。对象文件还将包含反射数据、导出数据和调试信息。

进一步阅读

如果你想要更深入了解 SSA 包的工作原理,包括它的传递和规则,请访问cmd/compile/internal/ssa/README.md[1].

参考资料

[1] 

cmd/compile/internal/ssa/README.md: https://golang.org/src/cmd/compile/internal/ssa/README.md




往期推荐


我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。


坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio

浏览 54
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报