方法 walkOne() 以参数 root 为根节点,逐个分析指向该顶点的路径(Path)上的所有顶点,例如路径:root <- A … <- P … <- Z 中,如果 root 需要逃逸,而 root 实际上持有的是 P 的指针的话,那么 P 也需要逃逸。而此时我们就需要通过参数 enqueue 将顶点 P 再次压入外层队列,因为 walkOne() 只分析 root 与路径上各个顶点的关系,如果我们发现路径上某节点 P 需要逃逸的话,那么就有必要将 P 作为根节点再次进行分析(可能外围队列之前已经遍历过顶点 P 了,而当时 P 可能不需要逃逸,所以这里是再次分析),所以我们需要将顶点 P 再次压入外层队列。
func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
root.walkgen = walkgen
root.derefs = 0
root.dst = nil
todo := []*location{root} // LIFO queue
// 遍历队列
for len(todo) > 0 {
l := todo[len(todo)-1]
todo = todo[:len(todo)-1]
// 删除对 l 进行逃逸分析的代码
// 在 for 循环末尾将指向该顶点的所有顶点压入队列
for i, edge := range l.edges {
if edge.src.escapes {
continue
}
d := derefs + edge.derefs
if edge.src.walkgen != walkgen || edge.src.derefs > d {
edge.src.walkgen = walkgen
edge.src.derefs = d
edge.src.dst = l
edge.src.dstEdgeIdx = i
todo = append(todo, edge.src)
}
}
}
}
// outlives reports whether values stored in l may survive beyond
// other's lifetime if stack allocated.
func (b *batch) outlives(l, other *location) bool {
// The heap outlives everything.
if l.escapes {
return true
}
// We don't know what callers do with returned values, so
// pessimistically we need to assume they flow to the heap and
// outlive everything too.
if l.isName(ir.PPARAMOUT) {
// Exception: Directly called closures can return
// locations allocated outside of them without forcing
// them to the heap. For example:
//
// var u int // okay to stack allocate
// *(func() *int { return &u }()) = 42
if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
return false
}
return true
}
// If l and other are within the same function, then l
// outlives other if it was declared outside other's loop
// scope. For example:
//
// var l *int
// for {
// l = new(int)
// }
if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
return true
}
// If other is declared within a child closure of where l is
// declared, then l outlives it. For example:
//
// var l *int
// func() {
// l = new(int)
// }
if containsClosure(l.curfn, other.curfn) {
return true
}
return false
}
func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
root.walkgen = walkgen
root.derefs = 0
root.dst = nil
todo := []*location{root} // LIFO queue
for len(todo) > 0 {
l := todo[len(todo)-1]
derefs := l.derefs
// If l.derefs < 0, then l's address flows to root.
addressOf := derefs < 0
if addressOf {
// For a flow path like "root = &l; l = x",
// l's address flows to root, but x's does
// not. We recognize this by lower bounding
// derefs at 0.
derefs = 0
}
if b.outlives(root, l) {
if addressOf && !l.escapes {
l.escapes = true
enqueue(l)
continue
}
}
// 删除掉构建内部队列的代码
}
}
package main
type T struct {
Name string
}
func GetT() **T {
var t T
l1 := &t
l2 := &l1
r3 := l1
r4 :=&r3
var l4 **T
l4 = l2
l4 = r4
return l4
}
main.go:8:6: moved to heap: t
main.go:9:2: moved to heap: l1
main.go:11:2: moved to heap: r3