调试源码
./src/make.bash 脚本会编译 Go 语言的二进制、工具链以及标准库和命令并将源代码和编译好的二进制文件移动到对应的位置上
中间代码 go在编译的过程中会经过中间代码生成阶段,Go语言编译器的中间代码具有静态单赋值(Static Single Assignment、SSA)的特性
go build -gcflags -S main.go
汇编指令的优化过程(输出html):
GOSSAFUNC=main go build main.go
编译原理
抽象语法树(Abstract Syntax Tree、AST)
静态单赋值(Static Single Assignment、SSA)是中间代码的特性,如果中间代码具有静态单赋值的特性,那么每个变量就只会被赋值一次。因为 SSA 的主要作用是对代码进行优化,所以它是编译器后端的一部分
指令集
- x86指令集
- arm指令集
Go语言编译器的源代码在src/cmd/compile中
编译器的前端一般承担着词法分析、语法分析、类型检查和中间代码生成几部分工作,而编译器后端主要负责目标代码的生成和优化,也就是将中间代码翻译成目标机器能够运行的二进制机器码。
Go 的编译器在逻辑上可以被分成四个阶段:词法与语法分析、类型检查和 AST 转换、通用 SSA 生成和最后的机器代码生成
词法分析 语法分析
类型检查阶段对make进行改写
Go 语言的很多关键字都依赖类型检查期间的展开和改写
源文件转换为抽象语法树,对整棵树的语法进行解析并进行类型检查之后,则认为树合法,转而将语法树转换为中间代码
Go 语言的编译器入口在 src/cmd/compile/internal/gc/main.go 文件中
会调用 cmd/compile/internal/gc.parseFiles 对输入的文件进行词法与语法分析得到对应的抽象语法树
得到抽象语法树后会分九个阶段对抽象语法树进行更新和编译,就像我们在上面介绍的,抽象语法树会经历类型检查、SSA 中间代码生成以及机器码生成三个阶段:
检查常量、类型和函数的类型;
处理变量的赋值;
对函数的主体进行类型检查;
决定如何捕获变量;
检查内联函数的类型;
进行逃逸分析;
将闭包的主体转换成引用的捕获变量;
编译顶层函数;
检查外部依赖的声明;
类型检查会遍历传入节点的全部子节点,这个过程会展开和重写 make 等关键字,在类型检查会改变语法树中的一些节点,不会生成新的变量或者语法树