Go:编译器概述

2019-09-16  本文已影响0人  开心人开发世界
image.png

本文基于Go 1.13。*

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

Phases

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

为了更好地理解每个阶段,让我们使用一个简单的程序:

package main

func main() {
    a := 1
    b := 2
    if true {
        add(a, b)
    }
}

func add(a, b int) {
    println(a + b)
}

解析

第一阶段非常简单,并在文档中做了很好的解释:

在编译的第一阶段,源代码被标记化(词法分析),解析(语法分析),并且为每个源文件构造语法树。

词法分析器将是第一个为了标记源代码而运行的包。这是前一个示例标记化的输出:

去源代码标记化

一旦被标记化,将被解析并用于构建语法树。

AST转换

由于带有标志的命令,可以显示对抽象语法树的转换:go tool compile``-W


生成AST的样本

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

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

SSA

静态单赋值形式是阶段,其中优化会发生:死代码消除,删除不使用的分支,具有恒定值等替换一些表达式

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

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

SSA代码

这里的变量ab突出显示以及if条件将允许我们稍后查看这些行是如何更改的。该代码也向我们展示了编译器如何管理println是在4个步骤分解函数:printlockprintintprintnlprintunlock。编译器会自动为我们添加一个锁,并根据参数的类型调用相关方法来正确打印它。

在我们的示例中,由于编译时已知a并且b已知,编译器可以计算最终结果并将变量标记为不再需要。通行证opt将优化此部分:

v11这里已经被添加的结果替换,v4并且v5已被标记为死代码。该传递opt deadcode将删除该代码:

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


常量布尔值被删除

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


不必要的控制流程被删除

完成所有传递后,Go编译器现在将生成一个中间汇编代码:


go asm代码

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

机器代码生成

main.o在我们的示例中,编译器的最后一步是生成目标文件。从该文件中,现在可以使用objdump执行相反过程的工具对其进行反汇编。这是Grant Seltzer Richman创建的一个很好的图表:


go 工具编译


go 工具objdump

您可以在“ Dissecting Go Binaries ”中找到有关目标文件和二进制文件的更多信息

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

翻译自:https://medium.com/a-journey-with-go/go-overview-of-the-compiler-4e5a153ca889

上一篇下一篇

猜你喜欢

热点阅读