Comment on page
9.5.1总体逻辑
逃逸分析以函数为单元对代码进行分析,同内联操作一样,编译器自底向上地遍历源代码中函数调用链形成的有向图,然后依次对各个SCC进行分析。入口函数如下:
// file: cmd/compile/internal/escape/escape.go
func Funcs(all []ir.Node) {
ir.VisitFuncsBottomUp(all, Batch)
}
函数
Batch
以一个SCC为输入,对该SCC内的所有函数进行逃逸分析,其总体逻辑如下:- 1.创建静态数据流有向图
- 2.遍历有向图的每个顶点,并分析指向该顶点的所有路径(Path)上的各个顶点是否需要逃逸
- 3.对每个变量设置逃逸标记
函数的主要流程如下,此处仅保留重要步骤的代码:
// file: cmd/compile/internal/escape/escape.go
func Batch(fns []*ir.Func, recursive bool) {
var b batch
b.heapLoc.escapes = true
// 1. 构建数据流有向图,分为三步完成
// 1.1 遍历函数内的变量,为每个变量创建一个有向图的顶点。顶点用 location 表示,下文中会介绍该数据结构
for _, fn := range fns {
b.initFunc(fn)
}
// 1.2 遍历函数的 IR Tree, 根据赋值语句创建有向图的边并计算权重。边用 edge 表示,下文中会介绍该数据结构
for _, fn := range fns {
// 闭包在下一步处理
if !fn.IsHiddenClosure() {
b.walkFunc(fn)
}
}
// 1.3 处理闭包,继续创建有向图的边
for _, closure := range b.closures {
b.flowClosure(closure.k, closure.clo)
}
b.closures = nil
// 2. 先检查各个变量的大小,大对象直接逃逸到堆上
for _, loc := range b.allLocs {
if why := HeapAllocReason(loc.n); why != "" {
b.flow(b.heapHole().addr(loc.n, why), loc)
}
}
// 3. 对有向图的各个顶点进行逃逸分析
b.walkAll()
// 4. 逃逸分析完成,在 IR Tree 中标记各个变量顶点的结果
b.finish(fns)
}