8.4.3 内联操作
我们先看一下内联操作的总体逻辑:
对于递归调用,只有函数调用自己这种场景是不允许内联的,如果多个函数形成一个调用环,那么编译器依然会尝试着对其进行内联。
函数InlineCalls()
完成实际的内联操作,给定函数fn
, 内联操作的步骤如下:
遍历函数体的各个语句,尝试对其进行内联。尝试内联的操作由函数
inlnode()
完成如果当前节点可以内联(肯定是一个函数调用),则创建一个内联节点替换当前节点,内联节点是类型
ir.InlinedCallExpr
, 该操作由mkinlcall
完成对于新创建的内联节点,对其所有语句递归地进行内联操作
回顾遍历调用链中的示例代码:
以及函数调用的SCC:
我们以scc = [C, D]
为例来看一下编译器做内联的详细步骤:
遍历 scc, 处理节点 C, 函数
CanInline(C)
识别出该函数可以内联,并为其创建内联属性 Inl调用
InlineCalls(C)
对 C 进行内联操作 遍历 C 的函数体,并尝试着对每个语句进行内联:第一个语句println("C")
不用内联,第二个语句D()
有可能可以内联,但此时函数 D 还没有经过CanInline()
的内联检查,因为内联属性 Inl
为空,不满足内联条件。到此对 C 的内联操作结束,并没有任何实际的内联操作发生遍历 scc, 处理节点 D, 函数
CanInline(D)
识别出该函数可以内联,并为其创建内联属性 Inl.调用
InlineCalls(D)
对 D 进行内联操作 逻辑同第2步,不同的是编译器发现语句C()
可以进行内联,于是完成内联后函数 D 变为:
5. 完成对 scc 的遍历,结束后两个函数的结果为:
由此可见随着自底向上处理完函数的所有 scc, 整个函数内联操作随即完成。
最后更新于