9.5.2 数据结构
编译器定义了各种数据结构来抽象逃逸分析的各个概念,在详细分析处理逻辑之前,我们先看一下重要的几类:
type batch struct {
allLocs []*location
closures []closure
heapLoc location
blankLoc location
}
batch 保存着
Batch()
函数一次逃逸分析的所有状态。其中 allLocs
代表整个数据流有向图,所有需要在堆上分配内存的变量都有一条边指向 heapLoc
顶点。type location struct {
n ir.Node // represented variable or expression, if any
curfn *ir.Func // enclosing function
edges []edge // incoming edges
loopDepth int // loopDepth at declaration
// resultIndex records the tuple index (starting at 1) for
// PPARAMOUT variables within their function's result type.
// For non-PPARAMOUT variables it's 0.
resultIndex int
// derefs and walkgen are used during walkOne to track the
// minimal dereferences from the walk root.
derefs int // >= -1
walkgen uint32
// dst and dstEdgeindex track the next immediate assignment
// destination location during walkone, along with the index
// of the edge pointing back to this location.
dst *location
dstEdgeIdx int
// queued is used by walkAll to track whether this location is
// in the walk queue.
queued bool
// escapes reports whether the represented variable's address
// escapes; that is, whether the variable must be heap
// allocated.
escapes bool
// transient reports whether the represented expression's
// address does not outlive the statement; that is, whether
// its storage can be immediately reused.
transient bool
// paramEsc records the represented parameter's leak set.
paramEsc leaks
captured bool // has a closure captured this variable?
reassigned bool // has this variable been reassigned?
addrtaken bool // has this variable's address been taken?
}
可以简单地将 location 理解成函数中的一个变量,但更准确地说:location 代表的是程序中需要使用的一个内存区块,例如赋值语句
c := make(chan string)
右侧并没有变量,但表达式 make(chan string)
依然需要一个 location 来表示,各种字面量的初始化表达式均是如此。location 同时也是数据流有向图中的一个顶点(Vertex),这是逃逸分析的重要数据结构,包含的属性也比较多,其中需要特别注意的有:
- edges: 有向图中指向本顶点的所有边
- derefs: 该属性在逃逸分析时使用,不是前文讨论的权重
- escapes: 标记该变量是否需要逃逸
type edge struct {
src *location
derefs int // >= -1
notes *note
}
edge 代表数据流有向图中的一条边(Edge)以及方向(Direction),属性
derefs
表示该边的权重,一条边代表程序中的一个赋值语句。其中 notes
用来记录编译器逃逸分析时的 dump 信息。type hole struct {
dst *location
derefs int // >= -1
notes *note
// addrtaken indicates whether this context is taking the address of
// the expression, independent of whether the address will actually
// be stored into a variable.
addrtaken bool
// uintptrEscapesHack indicates this context is evaluating an
// argument for a //go:uintptrescapes function.
uintptrEscapesHack bool
}
hole 封装了赋值语句中对右侧表达式的执行策略。例如对于语句
x = &p
, 对应的 hole 实例的内容如下:hole.dst: location instance of xhole.derefs: -1hole.addrtaken: true
除了常量运算,编译阶段并不会真正执行任何表达式,该结构体主要用来记录是对右侧表达式进行
*
与 &
操作的情况。type note struct {
next *note
where ir.Node
why string
}
用以记录逃逸分析 dump 信息的辅助数据结构,编译日志一节所打印的内容,便是通过该数据结构记录的信息。