前言
在熟悉LLVM的编译以后,我们将从以下几个方面对LLVM进行深入的探究:
- LLVM核心库基本设计原则
- 编译器驱动程序如何工作
- LLVM驱动程序的交互
- 独立组件的应用
- 了解LLVM基本库
- 介绍LLVM中的C++实践
- 编写第一个LLVM项目
1.LLVM基本设计原则
LLVM设计的一个核心方面是IR(SSA:单静态赋值),具有如下两个特征:
- 它被组织成三地址指令
- 有无限个寄存器
LLVM在不同的编译阶段使用以下额外的数据结构:
- 当将C或C++转换为LLVM IR时,Clang通过使用抽象语法树(AST)结构代表其在内存中的表示形式(TranslationUnitDecl class)。
- 当将LLVM IR转换成机器专用的汇编语言时,LLVM将首先将程序转换为有向无环图(DAG)表单,其将允许简单的指令选择 (SelectionDAG class),然后它转换回一个三地址形式,以允许指令调度(MachineFunction class)。
- 为了实现汇编器和链接器,LLVM在形式对象的上下文中使用第四种中介数据结构(MCModule class)来保存程序。
2.LLVM编译器驱动程序如何工作
图1:LLVM的执行流程如上图所示
这些编译器部件之间的交互可以在下面两种方式下进行:
- 在内存中:这发生在通过一个单一的管理组件,例如Clang。利用LLVM组件作为库,并依赖于数据结构在内存中的分配, 将一个阶段的输出提供给另一个阶段的输入。
- 通过文件:这发生在一个启动较小独立组件的用户,将特定组件的结果写入磁盘文件。并将文件作为下一个组件的输入。
图2:LLVM库介绍
在上面的例子中:LLVM后端工具llc,使用libLLVMCodeGen库实现它的部分功能,与此同时只负责启动LLVM IR级优化器的opt命令使用另一个库(libLLVMipa)来实现与目标无关的过程间优化。然而,我们看到clang更强大之处是,它使用两个库覆盖llc和opt并向用户呈现一个更简单的界面。通过这样的高级工具执行任何任务可以分解成一系列的低级工具执行结果的组合。
3.LLVM编译器驱动程序的交互
驱动程序负责集成所有必要的库和工具,以便为用户提供更友好的体验,使用户不必使用单独的编译器阶段,如前端、后端、汇编器和链接器。一旦将程序源代码提供给编译器驱动程序,它就可以生成可执行文件。在LLVM和Clang中,编译器驱动程序是Clang。
查看一个简单的C程序例子,hello.c:
#include<stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
要为这个简单的程序生成可执行文件,请使用以下命令:
clang hello.c -o hello
对于熟悉GCC的人来说,请注意前面的命令非常类似于GCC的方法。事实上,Clang编译驱动程序的设计初衷就是为与GCC标志和命令结构兼容,允许使用LLVM在项目中作为GCC的替代品。对于Windows,Clang也有一个版本名为clang-cl.exe,它模仿Visual Stadio C++命令行接口。它隐式的调用从前端到链接器的所有的组件。
可以使用 -### 命令可以查看驱动程序为完成操作而调用的所有的后续的组件。
图3:clang -### 操作
如图3所示,Clang驱动程序调用的第一个工具是带有-cc1参数的Clang工具本身,它禁用编译器-驱动程序模式,同时启用编译器模式。它还使用了大量的参数来调整C/ C++选项。