Go语言基础之Go语言编译|青训营笔记

65 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 2 天

前言

Go语言是编译型语言,代码在运行之前需要通过编译器编译成二进制机器码,理解Go语言的编译过程有助于进一步理解Go语言的实现原理。

预备知识

抽象语法树

源代码语法结构的一种抽象表示,以树状方式呈现。抽象语法树中的每一个节点都表示源代码中的一个元素,每一棵子树都表示一个语法元素。

静态单赋值(Static Single Assignment、SSA)

静态单赋值是中间代码的特性,具有静态单赋值特性的变量只会被赋值一次,可以省去不必要的赋值,优化代码。

指令集

常见的指令集包括x86、arm等,不同的处理器使用不同的架构和机器语言,程序需要将源代码根据架构翻译成不同的机器代码从而实现在不同机器上的运行。

编译的核心过程

Go 的编译器在逻辑上可以被分成四个阶段:词法与语法分析、类型检查和 AST 转换、通用 SSA 生成和最后的机器代码生成。

词法和语法分析

  • 词法分析的作用是解析源代码文件,将文件中的字符串序列转换成Token序列,方便后面的处理和解析,即分解字符串降低理解成本。早期的Go语言使用lex这种以正则匹配为原理的工具生成词法解析期,后期则使用Go来实现词法分析器。
  • 而语法分析的输入是词法分析器输出的Token序列,语法分析器按照顺序解析Token序列,依据规定的语法对序列进行规约并最终得到一个SourceFile结构,即抽象语法树(AST)。每一个 AST 都对应着一个单独的 Go 语言文件,这个抽象语法树中包括当前文件属于的包名、定义的常量、结构体和函数等。
// SourceFile示例
"json.go": SourceFile {
    PackageName: "json",
    ImportDecl: []Import{
        "io",
    },
    TopLevelDecl: ...
}

类型检查

在得到源文件的抽象语法树后,Go语言的编译器会对语法树中的定义和使用的类型进行检查,具体按一定顺序进行验证和处理。

类型检查阶段除了对节点类型进行验证还会改写一些函数,如make关键字被替换为runtime.makesliceruntime.makechan等。

中间代码生成

指将完成类型检查后的抽象语法树转换成中间代码,在这一阶段,编译器会分析出代码的无用变量和片段并对代码进行优化。

机器码生成

Go 语言源代码的 src/cmd/compile/internal 目录中包含了很多机器码生成相关的包,不同类型的 CPU 分别使用了不同的包生成机器码,其中包括 amd64、arm、arm64、mips、mips64、ppc64、s390x、x86 和 wasm。

总结

本文主要是基于学习对go语言的编译过程做了简要总结,不仅是Go语言,编译型的高级语言均需经历类似的编译过程。对于编译过程中还有许多细节文章没有进行展现,若想深入了解推荐阅读下面的参考文章。

引用参考

draveness.me/golang/docs…