业余民科学习者阅读笔记,垃圾内容
在《PLP: 4.2/4.3 Attribute Gramma阅读笔记3》中提到AST在整个编译过程中并不是必须的,同理IR也并不是必须的(AST本来就是IR的一种)。源码被compiler部分模块处理后得到IR,IR以另外一种形式保存了源码的内容,有时可能需要symbol table或者其它数据结构辅助。IR作为抽象出来的一个layer,主要目的是进行target无关的优化和分析。
As the compiler derives knowledge about the code it compiles, it must convey that information from one pass to another. Thus, the compiler needs a representation for all of the facts that it derives about the program. - 《Engineering a Compiler》
IR可以从结构上分为如下三种:
- Graphical IRs,对应的算法主要用于处理nodes, edges, lists, trees。
- Linear IRs,类似于一些抽象机器的伪码。对应的算法主要是迭代地处理这些线性表示。
- Hybrid IRs,结合了前两者,使用Graphical IRs来表示基本块间的数据流,使用Linear IRs表示底层的基本块内部的细节。
其实比较经典的compiler都是Hybrid IRs,例如llvm。另外也通过抽象层次的高低来对IR进行分类。
图形IR
分类
- Syntax-Related Trees
- Parse Tree
- AST
- DAG - Graphs
- CFG
- Dependence Graph
- Call Graph
我曾经做过的基于IR上的工作大致可以分为如下几个部分。
我曾经接触到的IR形式
从代码中学习API使用
这里我需要处理的IR是AST,遍历AST收集函数调用和if-then-else。如果某个API的调用很频繁,那我们就对这些数据进行聚类,得到一些常见的使用模式。例如文件打开操作和关闭,我们对内嵌着分支语句的序列进行聚类然后得到一些API的常见使用模式。
code intelligence
这里我处理的IR也是AST,这里只需要获取某个点的definition在哪个位置?或者遍历AST得到symbol信息从而能够提供代码文件的outline信息,或者是得到reference信息。
clang static analyzer
这里我处理的IR是一个Hybrid IR,只是这里hybrid的是CFG+AST+Call Graph。然后clang static analyzer作为一个executor,依次解释执行CFG基本块内部的AST语句。
Call Graph & Inheritance Graph
这里处理的IR依然是AST,同样是遍历AST收集相关信息。
moses
在moses中我要遍历AST生成线性IR,moses抄了llvm3.3,所以也是CFG+linear IR。
对于DAG,可以用来揭示依赖关系,而moses只是生成了线性IR,并没有生成SSA,更没有后面的instruction select,所以并没有涉及到DAG的工作。
Parse Tree
Parse Tree可以看做parser生成的结果,parse tree“再现”了文法符号推导的过程,所以也=更贴近源码。但是现在的parser都是一步生成出AST,而将源码相关的内容嵌在AST中,因为保留parse tree并没有特别大的必要,除非一些使用场景需要parse tree中的内容。
AST
而更通用的Tree型IR是AST,再剥去了语言相关的一些细节后,AST更精简的展示了程序的结构。但是AST仍然是贴近源码的一种IR形式,所以更多的用在与源码相关的场景中,例如editor。
DAG
DAG通常不是主力IR,主要用做补充。我们可以用DAG来揭示,代码中可能的重复的计算,但是在构造DAG的时候,必须能够证明
a
∗
2
a * 2
a∗2的值不会改变。
其实除此之外,DAG还有更重要的作用,也就是做指令重排。
CFG
CFG是compiler中间阶段最重要的结构之一。后端的许多操作都依赖与CFG,像是instruction scheduling,register allocation。
Compilers typically use a CFG in conjunction with another IR. The CFG represents the relationship among blocks, while the operations inside a block are represented with another IR, such as an expression-level AST, a DAG, or one of the linear IRS. The resulting combination is a hybrid IR.
Dependence Graph
Dependence Graph直观地展示IR中的依赖关系,具体细节可以参照PLP中的Depedence Analysis。
Call Graph
Call Graph主要用来做过程间的分析和优化,但是在构造call graph的过程中可能需要类型信息来减少callee candidates,例如C++中的虚函数调用。
Object-oriented programs with inheritance routinely create ambiguous procedure calls that can only be resolved with additional type information. - 《Engineering a Compiler》
但是有些type information只能在runtime中得到,此时构建call graph可能会有很大的开销。
在本节最后,作者说清楚了Graphical IRs的主要作用。
Graphical IRs encode relationship that may be difficult to represent in a linear IR. A graphical IR can provide the compiler with an efficient way to move between logically connected points in the program.
读书的重点在于思考和理解,否则无疑是向脑子里倾倒垃圾