go编译的四个阶段

golangç¼è¯åç

 

一、词法分析与语法分析

1.词法分析

lex3 是用于生成词法分析器的工具,lex 生成的代码能够将一个文件中的字符分解成 Token 序列。

lexer 通过正则匹配的方式将机器原本很难理解的字符串进行分解成很多的 Token。

词法分析器Scanner:对输入的字符流进行扫描,最终解析成token。

golangç¼è¯åç

2.语法分析器Parser

语法分析的输入就是词法分析器输出的 Token 序列,然后将编程语言的所有生产规则映射到对应的方法上,这些方法构成的树形结构最终会返回一个抽象语法树(go源文件)。

 AST(抽象语法树):

*AST是源代码语法的结构的一种抽象表示,它用树状的方式表示编程语言的语法结构1。抽象语法树中的每一个节点都表示源代码中的一个元素,每一颗子树都表示一个语法元素,例如一个 if else 语句,我们可以从 2 3 + 7 这一表达式中解析出下图所示的抽象语法树。**

golangç¼è¯åç

 

二、类型检查

1. 静态类型检查

静态类型检查是基于对源代码的分析来确定运行程序类型安全的过程,如果我们的代码能够通过静态类型检查,那么当前程序在一定程度上就满足了类型安全的要求,它也可以被看作是一种代码优化的方式,能够减少程序在运行时的类型检查。

2. 动态类型检查、

动态类型检查就是在运行时确定程序类型安全的过程,这个过程需要编程语言在编译时为所有的对象加入类型标签和信息,运行时就可以使用这些存储的类型信息来实现动态派发、向下转型、反射以及其他特性

3. golang 类型检查

Go 语言的编译器不仅使用静态类型检查来保证程序运行的类型安全,还会在编程期引入类型信息,让工程师能够使用反射来判断参数和变量的类型。

golang编译原理

 

类型检查分别会按照以下的顺序对不同类型的节点进行验证和处理:

① 常量、类型和函数名及类型;

② 变量的赋值和初始化;

③ 函数和闭包的主体;

④ 哈希键值对的类型;

⑤ 导入函数体;

⑥外部的声明;

关键词OMAKE节点,根据make的第一个参数

golangç¼è¯åç

 三、中间代码生成

在类型检查之后,就会通过一个名为 compileFunctions 的函数开始对整个 Go 语言项目中的全部函数进行编译,这些函数会在一个编译队列中等待几个后端工作协程的消费,这些并发执行的 Goroutine 会将所有函数对应的抽象语法树转换成中间代码。

1. SSA配置初始化

SSA 配置的初始化过程其实就是做中间代码生成之前的准备工作,在这个过程中我们会缓存可能用到的类型指针、初始化 SSA 配置和一些之后会调用的运行时函数,还有** CPU 架构设置用于生成中间代码和机器码的函数,当前编译器使用的指针、寄存器大小、可用寄存器列表、掩码等编译选项**

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

SSA是中间代码的一个特性,如果一个中间代码具有静态单赋值的特性,那么每个变量就只会被赋值一次。在实践中我们通常会用添加下标的方式实现每个变量只能被赋值一次的特性

x := 1x := 2y := x

SSA的特性优化点

  • 常数传播(constant propagation)
  • 值域传播(value range propagation)
  • 稀疏有条件的常数传播(sparse conditional constant propagation)
  • 消除无用的程式码(dead code elimination)
  • 全域数值编号(global value numbering)
  • 消除部分的冗余(partial redundancy elimination)
  • 强度折减(strength reduction)
  • 寄存器分配(register allocation)

2. 遍历和替换

在生成中间代码之前,我们还需要对抽象语法树中节点的一些元素进行替换。

3. SSA代码生成

经过 walk 系列函数的处理之后,AST 的抽象语法树就不再会改变了,Go 语言的编译器会使用 compileSSA 函数(/src/cmd/compile/internal/gc/pgen.go**)将抽象语法树转换成中间代码。

四、最终机器码生成

机器码的生成过程其实就是对 SSA 中间代码的降级(lower)过程,在 SSA 中间代码降级的过程中,编译器将一些值重写成了目标 CPU 架构的特定值,降级的过程处理了所有机器特定的重写规则并对代码进行了一定程度的优化

1. 复杂指令集(CISC)和精简指令集(RISC)

  • 复杂指令集通过增加指令的数量减少需要执行的指令数;(x86)
  • 精简指令集能使用更少的指令完成目标的计算任务;(arm)

2. Go 语言支持的架构

golangç¼è¯åç

 

交叉编译

$ Mac 下编译 Linux 和 Windows 64位可执行程序CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.goCGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.goLinux 下编译 Mac 和 Windows 64位可执行程序CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.goCGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go

3. 机器码生成

src/cmd/compile/internal/ssasrc/cmd/internal/obj
  1. // 通过命令输出汇编结果

  2. GOOS=linux GOARCH=amd64 go tool compile -S hello.go

五、总结

golangç¼è¯åç