编译原理基础
业务场景
下面两段程序,哪个快一些?
1 | type person struct { |
类型转换是啥原理?
1 | func main() { |
怎么找make和new这些函数的具体实现?
会员服务,给用户分等级
- 初级会员,发帖数>10;
- 中级会员,充值>1000人民币;
- 高级会员,发帖数>100,充值>10000人民币。
如果项目数 = 几百,每个项目都有自己的会员规则,怎么办?
封装统一的数值查询服务
用户提供查询条件,会经常变,代理去不同的模块查数据,外部模块没有统一的数据获取规范。
每次我们的用户提了需求,我们就一定要写一遍代码吗?
公司想从Thrift切换到gRPC
已经有了大量的Thrift IDL,想提供gRPC接口,手工把Thrift IDL抄写成pb文件效率太低,怎么办?
SQL审计
我是SQL专家,我知道怎么获取到表的索引,我可以把用户代码里的SQL扫描出来…
我想在上线的时候能自动做一些拦截,提醒用户去给表加索引,怎么办?
公司卖出去的服务软件,客户要自己定制,但不想给客户源码
要支持客户写的扩展代码能在我们的模块上运行。
编译过程
词法分析
1 | func main() { |
语法分析
语义分析
1 | func main() { |
在抽象语法树AST上做类型检查
中间代码(SSA)生成与优化
1 | func main() { |
SSA的两大要点:
- Static:每个变量只能赋值一次;
- Single:每个表达式只能做一个简单运算,对于复杂的表达式
a*b+c*d
要拆分成图片里的形式。
机器码生成
虚拟地址重定位
编译与反编译⼯具
编译
1 | //hello.go |
命令行输入go tool compile -S ./hello.go | grep "hello.go:5"
1 | "".main STEXT size=109 args=0x0 locals=0x58 funcid=0x0 |
使⽤调试⼯具
调试汇编时,使用SI到JMP目标位置
使用c(continue)从上一个断点到下一个断点
用disass反汇编
语法实现分析
go func
1 | import "time" |
channel send & recv
1 | func main() { |
非阻塞recv
1 | func main() { |
找到这张图中所有报错的内部代码位置
1 | func main() { |
运行:
1 | $ go run hello.go |
Parser 场景示例
内置AST工具
简单的规则引擎
从Go的注释到swagger
使用社区Parser
从Thrift切换到gRPC
SQL审计
- Vitess
- PingCAP
统一数据接入服务
客户定制需求
函数调⽤规约
函数栈
为什么Go可以一个函数多个返回值
局部变量,只要不逃逸,都在栈上分配空间。
当我们调用其它函数时:
函数调用规约:
- The order in which atomic parameters,or individual parts of a complex parameters,are allocated
- How parameters are passed(push 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.
References
Go 的词法分析和语法/语义分析过程
编译器各阶段的简单介绍
Linkers and loaders,只看内部对 linker 的职责描述就⾏,不⽤看原理
SSA 的简单介绍(*只做了解)
⽼外的写的如何定制 Go 编译器,⾥⾯对 Go 的编译过程介绍更详细,SSA 也说明得很好,只做了解
如何阅读 go 的 SSA,难,只做了解
CMU 的编译器课,讲 SSA,难,只做了解
对逆向感兴趣的话扩展内容
Vitess 的 SQL Parser
PingCAP 的 TiDB 的 SQL Parser
GoCN 上的 dlv 的新译⽂
[C语⾔调⽤规约](https://github.com/cch123/llp-trans/blob/master/part3/translation-details/function-calling-sequence/callingconvention.md)
Go 语⾔新版调⽤规约
其它说明
- C 语⾔的调⽤规约是寄存器与栈混合使⽤的,Go 语⾔⽬前只使⽤了栈(1.17 中会有⽤上寄存器的新⽅式)
- 有⼀些⽐较奇怪的问题,可以⽤
SSA
的⽹⻚来看到底是哪个阶段导致的,⽐如:https://xargin.com/addr-of-empty-struct-may-noteq/,对于⼤多数 Gopher 只要知道这件事情是优化阶段⼲的就可以了,不需要研究明⽩优化每个阶段都在⼲什么 - Go 语⾔的参数和返回值都是 caller 提供空间,在
callee
中需要根据FP
寄存器找到位置,需要⼀定的汇编知识。