> For the complete documentation index, see [llms.txt](https://gocompiler.shizhz.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gocompiler.shizhz.me/9.-golang-bian-yi-qi-tao-yi-fen-xi/9.5.2-shu-ju-jie-gou.md).

# 9.5.2 数据结构

编译器定义了各种数据结构来抽象逃逸分析的各个概念，在详细分析处理逻辑之前，我们先看一下重要的几类：

### batch

```go
type batch struct {
    allLocs  []*location
    closures []closure

    heapLoc  location
    blankLoc location
}
```

batch 保存着 `Batch()` 函数一次逃逸分析的所有状态。其中 `allLocs` 代表整个数据流有向图，所有需要在堆上分配内存的变量都有一条边指向 `heapLoc` 顶点。

### location

```go
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: 标记该变量是否需要逃逸

### edge

```go
type edge struct {
    src    *location
    derefs int // >= -1
    notes  *note
}
```

edge 代表数据流有向图中的一条边（Edge）以及方向（Direction），属性 `derefs` 表示该边的权重，一条边代表程序中的一个赋值语句。其中 `notes` 用来记录编译器逃逸分析时的 dump 信息。

### hole

```go
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 实例的内容如下：

> &#x20;hole.dst: location instance of x&#x20;
>
> hole.derefs: -1&#x20;
>
> hole.addrtaken: true

除了常量运算，编译阶段并不会真正执行任何表达式，该结构体主要用来记录是对右侧表达式进行 `*` 与 `&` 操作的情况。

### note

```go
type note struct {
    next  *note
    where ir.Node
    why   string
}
```

用以记录逃逸分析 dump 信息的辅助数据结构，编译日志一节所打印的内容，便是通过该数据结构记录的信息。
