编译时如何看到每个文件的编译选项_硬核文章:图解 Go 编译器的高级操作

bdeca7695ddc626c4ec013935d326537.png

本文基于 Go 1.13

Go 编译器是 Go 生态系统中的一个重要工具,因为它是将程序构建为可执行二进制文件的基本步骤之一。编译器的历程是漫长的,它先用 C 语言编写,迁移到 Go,许多优化和清理将在未来继续发生,让我们来看看它的高级操作。

阶段(phases)

Go 编译器由四个阶段组成,可以分为两类:

  • 前端(frontend):这个阶段从源代码进行分析,并生成一个抽象的源代码语法结构,称为 AST[1]
  • 后端(backend):第二阶段将把源代码的表示转换为机器码,并进行一些优化。
2b5f2ecd5b54dd1f099775f528652afc.png

编译器文档[2]

为了更好理解每个阶段,我们看个简单的程序:

package mainfunc main() {    a := 1    b := 2    if true {        add(a, b)    }}func add(a, b int) {    println(a + b)}

解析

第一阶段非常简单,在 文档[3] 中有很好的解释:

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

lexer 是第一个运行用来标记源代码的包。下面是上边例子的 标记化[4] 输出:

e6e280e3d1570eb5461ff55b882b7c23.png

Go 源码标记化

一旦被标记化,代码将被解析、构建代码树。

AST(抽象语法树) 转换

可以通过 go tool compile 命令和标志 -w 展示 抽象语法树[5] 的转换:

8deb15d8f1e37a5b67f2c460d2c83657.png

构建 AST 的简单过程

此阶段还将包括内联等优化。在我们的示例中,由于我们没有看到 CALLFUNC 该方法的任何 add 指令,该方法 add 已经内联。让我们使用禁用内联的标志 -l 再次运行。

ce209868cbf12d10c40eeb5b4d9c11b8.png

构建 AST 的简单过程

AST 生成后,它允许编译器使用 SSA 表示转到较低级别的中间表示。

SSA(静态单赋值)的生成

静态单赋值[6] 阶段进行优化:消除死代码,删除不使用的分支,替换一些常量表达式等等。

使用 GOSSAFUNC=main Go tool compile main.go && open ssa.html 命令,生成 HTML 文档的命令将在 SSA 包中完成所有不同的过程,因此可以转储 SSA 代码:

1ea92c5862408f6b7199faabee7ba9a8.png

SSA 过程

生成的 SSA 位于 “start” 选项卡中:

187ece1ad155d806cd4e929c8dce66a1.png

SSA 代码

在这里,高亮显示变量 a 和 b 以及 if 条件表达式,将向我们展示这些行是怎么变化的。这些代码也向我们描述了编译器如何管理 println 函数,该函数被分解为 4 个步骤:printlock、printint、printnl、printunlock。编译器会自动为我们添加一个锁,并根据参数的类型,调用相关的方法来正确输出。

在我们的示例中,由于编译时已知 a 和 b,所以编译器可以计算最终结果并将变量标记为不必要的。通过 opt 优化这部分:

d9bb2cdd44fb33dc470c3acabc4928f5.png

SSA code — “opt” 过程

在这里,v11 已经被添加的 v4 和 v5 所替代,这两个 v4 和 v5 被标记为死代码。然后通过 opt deadcode 将删除这些代码。

bcd727b884d42b5bf08b8cb4c2283915.png

SSA code — “opt deadcode” 过程

对于 if 条件,opt 阶段将常量 true 标记为死代码,然后删除:

c9b941997cd54a5d5faf447beddcd0a5.png

然后,通过将不必要的块和条件标记为无效,另一次传递将简化控制流。这些块稍后将被另一个专用于死代码的阶段删除

43156119cd8aae94aa6dcf237364e20d.png

删除不必要控制流

完成所有过程之后,Go 编译器现在将生成一个中间汇编代码

077f24d9722e0508b32d9083202e6c21.png

Go 汇编码

下一阶段将把机器码生成到二进制文件中。

生成机器码

编译器的最后一步是生成目标(object)文件,在我们的例子中生成 main.c。从这个文件中,现在可以使用 objdumptool 对其进行反编译。下面是一个很好的图表,由 Grant Seltzer Richman 创建:

d161d3a1c58ad4e27aa598477256f04d.png

compile 工具

fb2f39e09c4c7903162bd4784c003162.png

objdump 工具

您可以在“Dissecting Go Binaries[7]”中找到有关对象文件和二进制文件的更多信息。

生成目标文件后,现在可以使用 go tool link 将其直接传递给链接器,二进制文件将最终就绪。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值