SSA 形式的设计取舍: Phi 函数的新形式?
SSA, 静态单赋值形式, 是 “单赋值” 的, 所有的量都只会被赋值一次.
在控制流汇聚的时候, 我们可能需要在汇点后的控制流中取得汇点前的多个控制流中更新过后的数据. 此时, 由于其单赋值的特性, 我们没办法简单地表示, 我们拿到的具体是哪一个数据.
为了解决这个问题, Phi 函数应运而生.
SSA, 静态单赋值形式, 是 “单赋值” 的, 所有的量都只会被赋值一次.
在控制流汇聚的时候, 我们可能需要在汇点后的控制流中取得汇点前的多个控制流中更新过后的数据. 此时, 由于其单赋值的特性, 我们没办法简单地表示, 我们拿到的具体是哪一个数据.
为了解决这个问题, Phi 函数应运而生.
在 NNVM 执行计算图优化的过程中,会在计算图上运行多种不同的算法,来修改计算图本身的结构或者属性。这些算法在 NNVM 中被组织成了 pass 的形式,可以使用 API ApplyPass
进行调用。
说起来,pass 这个概念在编译原理中是很常见的:在编译过程中,对输入的源代码或等价的中间表示执行一次遍历的过程,就叫做一个 pass。
传统意义上,我们可以把编译器的各个阶段都实现成一个 pass,比如:先对源代码做词法分析,待全部完成后对所有 token 进行语法分析,得到所有 AST 后再进行语义分析等等……但是通常情况下,为了节省时间,编译器会将部分或者全部的流程合并在同一个 pass 里,例如:在词法分析的过程中,语法分析器“同时”工作,构建 AST;然后在当前 AST 构建完毕后,立即对其执行语义分析和 IR 生成。
而 NNVM 中出现的 pass 则是狭义的 pass。熟悉 LLVM 的读者应该知道,LLVM 中有一个 PassManager
用于管理所有的 pass,而每一个 pass 又可以对 IR 进行一次独立的操作(优化等)。这一概念的应用使 LLVM 变得高度模块化,添加一种新的优化只需要实现一个新的 pass 即可,而不必大幅度改动 LLVM 本身的其他代码。NNVM 中 pass 的概念和 LLVM 中的基本一致。
以上是对 pass 这一概念的简单介绍。接下来的这篇文章记录了 NNVM 在执行编译优化过程中用到的一些 pass,以及这些 pass 的具体作用。