Alan Hou的个人博客

跟着曹大学Golang第二回 – Go 语法背后的秘密

安装多版本的 Go:https://golang.org/doc/manage-install

编译原理基础

编译过程

词法分析(Lexical Analysis)

语法分析(Syntax Analysis)

https://astexplorer.net/

语义分析(Semantic Analysis)

在抽象语法树 AST 上做类型检查

中间代码(SSA)⽣成与优化

https://golang.design/gossa,或者GOSSAFUNC=funcname go build x.go

SSA(Single Static Assignment)的两大要点是:

机器码⽣成

编译的最后一个阶段是机器码:

https://godbolt.org/

链接过程

最重要的就是进行虚拟地址重定位(Relocation)。编译后所有函数地址都是从 0 开始,每条指令是相对函数第一条指令的偏移。而链接后所有指令都有了全局唯一的地址:

编译与反编译⼯具

编译

该命令会生成 .o目标文件,并把目标的汇编内容输出:

例如要对比以下两段代码的执行效率,一种方法就是使用go tool compile来进行测试,本例会发现两者在框出部分输出的汇编是一致的:

反编译

go tool objdump 查找 make 的实现 (https://go.dev/ref/spec,官⽅ spec),make.go 内容如下:

执行命令go build make.go && go tool objdump ./make | grep -E "make.go:6|make.go:10|make.go:14",得到的结果如下:

接下来可进一步查看runtime.makesliceruntime.makechan以及runtime.makemap来进行分析。

使⽤调试⼯具

https://github.com/go-delve/delve/tree/master/Documentation/cli

语法实现分析

go func

通过go build hello.go && go tool objdump ./hello | grep "hello.go:6"命令可得到

可查看其中的 runtime 函数

channel send && recv

关键点就是 chansend1 和 chanrecv1 函数

非阻塞 recv

使用go tool compile -S nonblock_recv.go | grep nonblock_recv.go:6得到

可进一步查看runtime.selectnbrecv

Parser 应⽤场景示例

内置 AST 工具-简单的规则引擎

假设有一个简单的需求,初中高级会员的规则如下(字段posts表示发帖数,invest表示充值消费):

社区 Parser-SQL 审计

SQL Parser 的例子:https://github.com/cch123/elasticsql

如何在交付二进制的同时使我们的 Go 模块具备一定的扩展性?

函数调⽤规约

函数栈

为什么 Go 可以一个函数多个返回值?

局部变量只要不逃逸,都在栈上分配空间,从低地址向高地址不断压入栈中

在我们调用其它函数时,参数和返回值都 caller 提供调用空间的,因此可以有多个返回值:

函数调用规约
• The order in which atomic (scalar) parameters, or individual parts of a complex parameter, are allocated
• How parameters are passed (pushed on the stack, placed in registers, or a mix of both)
• Which registers the called function must preserve for the caller (also known as: callee-saved registers or non-volatile registers)
• How the task of preparing the stack for, and restoring after, a function call is divided between the caller and the callee

参考资料

补充

编译原理相关书籍:

 

退出移动版