> 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/8.-golang-bian-yi-qi-inline/8.4.3-nei-lian-cao-zuo.md).

# 8.4.3 内联操作

我们先看一下内联操作的总体逻辑：

```go
func InlinePackage() {
    ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(scc []*ir.Func, recursive bool) {
        numfns := numNonClosures(scc)
        for _, n := range scc {
            // 判断递归调用场景
            if !recursive || numfns > 1 {
                CanInline(n)
            } else {
                if base.Flag.LowerM > 1 {
                    fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
                }
            }
            InlineCalls(n)
        }
    })
}
```

对于递归调用，只有函数调用自己这种场景是不允许内联的，如果多个函数形成一个调用环，那么编译器依然会尝试着对其进行内联。

函数`InlineCalls()`完成实际的内联操作，给定函数`fn`, 内联操作的步骤如下：&#x20;

1. 遍历函数体的各个语句，尝试对其进行内联。尝试内联的操作由函数`inlnode()`完成&#x20;
2. 如果当前节点可以内联（肯定是一个函数调用），则创建一个内联节点替换当前节点，内联节点是类型`ir.InlinedCallExpr`, 该操作由`mkinlcall`完成&#x20;
3. 对于新创建的内联节点，对其所有语句递归地进行内联操作

回顾[遍历调用链](/8.-golang-bian-yi-qi-inline/8.4.1-bian-li-tiao-yong-lian.md)中的示例代码：

```go
func B() {
    println("B")
}

func A() {
    println("A")
    B()
}

func C() {
    println("C")
    D()
}

func D() {
    println("D")
    C()
}

func main() {
    A()
    C()
}
```

以及函数调用的SCC：

![Func Call Stack & SCC](/files/-MapjLeBZSdGxZ1TneeU)

我们以`scc = [C, D]`为例来看一下编译器做内联的详细步骤：&#x20;

1. 遍历 scc, 处理节点 C, 函数`CanInline(C)`识别出该函数可以内联，并为其创建内联属性 Inl&#x20;
2. 调用`InlineCalls(C)`对 C 进行内联操作 遍历 C 的函数体，并尝试着对每个语句进行内联：第一个语句`println("C")`不用内联，第二个语句`D()`有可能可以内联，但此时函数 D 还没有经过`CanInline()`的内联检查，因为`内联属性 Inl`为空，不满足内联条件。到此对 C 的内联操作结束，并没有任何实际的内联操作发生&#x20;
3. 遍历 scc, 处理节点 D, 函数`CanInline(D)`识别出该函数可以内联，并为其创建内联属性 Inl.&#x20;
4. 调用`InlineCalls(D)`对 D 进行内联操作 逻辑同第2步，不同的是编译器发现语句`C()`可以进行内联，于是完成内联后函数 D 变为：

```go
func D() {
    println("D")
    // Inlined statements
    println("C")
    D()
}
```

&#x20;    5\. 完成对 scc 的遍历，结束后两个函数的结果为：

```go
func C() {
    println("C")
    D()
}

func D() {
    println("D")
    // Inlined statements
    println("C")
    D()
}
```

由此可见随着自底向上处理完函数的所有 scc, 整个函数内联操作随即完成。
