了解编译过程的出发点,是在看一些源码的过程中,不知道内建函数的执行原理,比如:
定义
ch := make(chan int64, 10)
,在执行中到底是通过哪段源码实现的呢?
基于以上疑惑,这篇笔记大致了解下Golang的编译过程
几个基本概念
抽象语法树AST
AST, Abstract Syntax Tree, 是源代码语法的结构的一种抽象表示,它用树状的方式表示编程语言的语法结构。编译器在执行完语法分析之后会输出一个抽象语法树,这个抽象语法树会辅助编译器进行语义分析,我们可以用它来确定语法正确的程序是否存在一些类型不匹配或不一致的问题
静态单赋值SSA
SSA, Static Single Assignment, 是中间代码的一个特性,如果一个中间代码具有静态单赋值的特性,那么每个变量就只会被赋值一次。主要作用是对代码进行优化。是编译器后端的一部分
go编译器的几个阶段
词法分析
词法分析主要是将源码文件中的字符串序列转换成不包含空格、换行等字符的Token序列,例如:package, json, import, (, io, ), ...
等
go语言前期使用lex
工具定义词法分析器解析go源码,其大致过程包括:
- 定义
.l
文件。类似于正则表达式,匹配源码中的字符,并转换成定义好的TOKEN
序列 - 使用
lex
工具将.l
文件展开成C语言文件,然后编译成二进制文件 - 使用上述的二进制文件,则可以对go源文件进行词法解析
后期,go使用go实现了词法分析器,实现了"自举"
语法分析
语法分析器,将词法分析器的输出按照golang语法转换成有意义的结构体,即抽象语法树AST,每一个AST都对应着一个单独的Go源码文件,其中包括当前文件属于的包名,定义的常量、结构体和函数等,如:
"json.go": SourceFile {
PackageName: "json",
ImportDecl: []Import{
"io",
},
TopLevelDecl: ...
}
类型检查
- 遍历上述产生的抽象语法树,依次对每个节点进行语法和类型等校验
- 同时改写一些内建函数,比如
make
关键字会被替换成runtime.makeslice
或者runtime.makechan
等函数
中间代码生成
中间代码的生成过程是从抽象语法树AST到SSA中间代码的转换过程,在这期间会对语法树中的关键字再进行改写,改写后的语法树会经过多轮处理转变成最后的SSA中间代码,相关代码中包括了大量switch语句、复杂的函数和调用栈
机器代码生成
机器代码生成主要包括两个主要阶段:
- 将上述产生的中间代码进行降级
(lower)
,生成目标机器上的汇编代码 - 通过汇编器将上述汇编代码转换成机器码
本篇笔记主要参考draveness大神的文章链接