# 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, 整个函数内联操作随即完成。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/8.-golang-bian-yi-qi-inline/8.4.3-nei-lian-cao-zuo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
