小白的go内核分析之路(一) --go编译原理基础知识

go 版本: go1.16.4
操作系统: Mac

本人小白一枚,突然心血来潮,想要学习go的内核,从此踏上不归路。。。

1.go源码调试方法

那么就得从调试go开始,首先安装cloc方便我们的解析,其他系统自行安装即可。

mac

brew install cloc

linux

yum instal cloc

在这里插入图片描述
在这里插入图片描述
我们首先查看下go的目录,然后到该目录下进行编译

go env | grep GOROOT
cloc ./src/

然后我们可以看到go代码的情况,总共5005个文件,1560439行

在这里插入图片描述

在此举例一场常用的调试方法
在这里插入图片描述

go run main.go

执行该方法会输出如下
在这里插入图片描述

此时我们可以使用编辑器的跟踪,此时我们可以打印。该方法(printLn)为内置的系统函数,所以可以在此处直接运行
在这里插入图片描述
在这里插入图片描述
于是我们可以发现在go的源码中可以直接进行调试

2.学习go编译过程

将go转化为汇编语言,再通过汇编语言来学习它的整个流程

GOSSAFUNC=main go build main.go

还是刚才那个文件,我们来进行转化
在这里插入图片描述
打开ssa.html
在这里插入图片描述

至此我们可以看到go的整个编译过程

3. 前置知识(如掌握请忽略)

3.1 抽象语法树AST

抽象语法树,其实就是用树的结构来表示代码的语法结构
比如说: 1+2+3 +(4*5),用抽象语法树展示如下
在这里插入图片描述

3.2 静态单赋值SSA

在编译器的设计中静态单赋值形式(static single assignment form,通常简写为 SSA form或是SSA)是中介码(IR,intermediate representation)的特性,每个变数仅被赋值一次 ;可以理解为是一种规范,如:

 x = 1
 y = x +1
 x = 2
 z = x + 1 

经过转换后为

x1 = 1
y1= x1+1
x2 = 2
z1 = x2 +1

4.编译器过程

此处省略编译的启动过程,有兴趣的小伙伴可以查看src/cmd/go中的main文件即可了解
go语言编译器的源代码在src/cmd/compile目录中,目录下的文件共同组成了go语言的编译器,编译入口为src/cmd/ompile/main.go
在这里插入图片描述
我们可以根据该文件一路跟踪找到
gc.Main->parseFiles->syntax.Parse

在这里插入图片描述

整个过程主要可以分为4个过程
1.解析(Parsing)
2. 类型检查和AST转换( Type-checking and AST transformations)
3. 通用SSA(Generic SSA)
4. 生成机器代码(Generating machine code)

go的原文描述在(src/cmd/compile/README.md),有需要的可以自行访问go该目录查看

4.1 解析

cmd/compile/internal/syntax (lexer, parser, syntax tree)
        在编译的第一阶段,源代码被标记化(词法分析),Parsed(语法分析),并为每个源构造一个语法树文件。

4.1.1 词法分析

词法分析就是把源码文件,将文件中的字符串序列转换成token序列,执行词法分析的程序称之为词法解析器(lexer)。

比如说将package 转换成_Package,具体的替换单词都在src/cmd/compile/internal/syntax/tokens.go中有说明。
在这里插入图片描述
在此以之前的main.go举例说明
在这里插入图片描述

经过转换(有兴趣的可以去看下lex)
在这里插入图片描述

生成最终token序列

package,main,import...
4.1.2 语法分析

语法的解析过程就是将词法分析生成的Token序列按照语言定义好的文法自下而上或者自上而下的进行规约,每一个Go的源代码文件最终会被归纳成一个SourceFile结构:

SourceFile= PackageClause ";"{ ImportDecl";"} { TopLevelDecl";"} .
4.2 类型检查和AST转换

cmd/compile/internal/gc(创建编译器AST,类型检查,AST转换)

4.2.1 类型检查

顾名思义,检查类型。
首先通过src/cmd/compile/internal/gc/typecheck.go文件gc.typecheck 函数检查常量、类型、函数声明以及变量赋值语句的类型,然后使用 cmd/compile/internal/gc.checkMapKeys 检查哈希中键的类型。
在这里插入图片描述
在这里插入图片描述
然后主要通过gc.typecheck1对一些操作符,函数调用、方法调用等,如加减乘除、进行检查

在这里插入图片描述
切片、哈希、关键字就不再一一举例了。

4.3 通用SSA(Generic SSA)/中间代码生成

在这个环节时,说明生成的抽象语法树已经不存在语法错误。增加这一环节的主要原因是由于直接转为机器码难度较大,中间代码是一种更接近机器语言的表现形式。

4.3.1 配置初始化

在中间代码生成前会缓存可能用到的类型指针、配置、函数。主要分为3个部分

  1. 结构体 cmd/compile/internal/ssa.Types
  2. 函数缓存类型和类型指针 cmd/compile/internal/types.NewPtr
  3. 基本类型 cmd/compile/internal/ssa.Types
4.3.2 walk

生成中间代码前,遍历和替换掉抽象语法树中节点的一些元素,通过cmd/compile/internal/gc.walk实现

4.3.3 SSA生成

通过walk处理之后,采用cmd/compile/internal/gc.compileSSA 函数将抽象语法树转换为中间代码,然后由cmd/compile/internal/gc.buildssa生成具有SSA特性的中间代码

参考文献:
https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-typecheck/

        这个gc包包含一个AST定义,它从编写时就被继承了下来所有的代码都是用c写的,所以gc的第一件事包必须做的是将语法包的语法树转换为编译器的AST表示。这个额外的步骤将来可能会被重构掉。
        然后对AST进行类型检查。第一步是名称解析和类型推断,确定哪个对象属于哪个标识符,以及属于什么键入每个表达式所具有的。类型检查包括某些额外的检查,例如“声明的和未使用的”以及确定一个函数是否终止。
        某些转换也是在AST上完成的,一些节点是基于AST改进的对类型信息,如从算术中分离的字符串加法节点类型。其他一些例子是死代码消除,函数调用内联和逸出分析。

4.4 生成机器代码

将SSA中间代码转换为机器代码,其实是将SSA代码降级并通过进行特定处理的cmd/compile/internal/ssa 包,执行生成机器码的cmd/internal/obj

4.5 参考文献

Go的AST(抽象语法树)
Go 语言设计与实现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值