历史
LLVM(low level virtual machine)起源于伊利诺伊大学的一个编译器实验项目,目前已经发展成一个集编译器和工具链为一体的商业开源项目,因此其英文名称的含义被扩大,不再仅仅是字面意思。其创始人为 Chris Lattner。LLVM项目遵循的开源许可协议是 Apache 2.0License。LLVM 从2003 年10月24 日发布第一个开源版本 LLVM1.0 以来,截止2020 年第一个季度,已经发布到LLVM10.0.0 版本,十几年间开发者社区也在不断壮大,从 2007 年起至今,每年都会举办 1-2 次 LLVM 开发者大会,在大会上有很多优秀的开发者们分享他们对于LLVM项目的一些技术问题研究。2012年LLVM获得了2012ACM软件系统奖。
最开始LLVM在项目初期,定向研究终身程序优化,后来随着项目成熟,维护编译器IP在磁盘上表示的设计成决策仍然是为了实现连接时优化,而较少关注终身优程序优化的原始想法。
最后,LLVM核心库通过放弃低级虚拟机(Low Level Virutal Machine)这个名称,正式表明成为一个平台不感兴趣,而仅仅由于历史原因,所以用LLVM这个名字,而要成为强大和实用的C/C++编译器,而不是java的竞争对手
LLVM是个啥?
经典的编译器如gcc在设计上都是提供一条龙服务的: 你不需要知道它使用的IR是什么样的,它也不会暴露中间接口来给你操作它的IR。 换句话说,从前端到后端,这些编译器的大量代码都是强耦合的
编译原理主要过程分为两个:
- 前端把源代码翻译成中间代码IR
- 后端把IP翻译成机器码,或者给解释器执行
LLVM的核心设计是LLVM IR
它使用的静态单赋值形式(SSA)两个重要特征:
- 代码被组织为三地址指令
- 它有数目不受限制的寄存器
同时作为编译器IR,用于指导核心库开发的两个LLVM IR的基本原则:
- SAA表示和允许快速优化的无限寄存器
- 通过将整个程序存储在磁盘IR表示中以实现更便捷的链接时优化
LLVM采用了功能划分更为明确的模块化设计。LLVM将优化的部分单独提取出来,不再将编译器的优化功能与前端或者后端的功能代码耦合在一起!
LLVM进行代码混淆保护,实际上需要改造的是优化器部分,通过编写用于代码混淆的定制的Pass来完成混淆处理,不需要对后端的Pass进行改造
LLVM Pass
Pass就是“遍历一遍IR,可以同时对它做一些操作”的意思
LLVM Pass分为两种类型:
- 分析Pass:用于获取LLVM IR中的信息,例如函数的调用关系、变量的使用情况等。
- 转换Pass:用于对LLVM IR进行修改,例如优化代码、插入代码等。
LLVM Pass可以用于各种目的,例如:
- 代码优化:LLVM Pass可以用于提高代码的性能、减少代码的大小等。
- 代码分析:LLVM Pass可以用于获取代码的信息,例如代码的调用关系、变量的使用情况等。
- 代码插桩:LLVM Pass可以用于在代码中插入新的代码,例如用于调试或分析。
LLVM Pass是LLVM编译器的核心组件,是LLVM编译器的灵活性和可扩展性的基础。
以下是一些常见的LLVM Pass:
- 死代码删除(Dead Code Elimination):删除不执行的代码。
- 常量折叠(Constant Folding):将常量表达式折叠为常量值。
- 寄存器分配(Register Allocation):将变量分配到寄存器中。
- 指令重排序(Instruction Scheduling):重新安排指令的顺序,以提高性能。
用户可以根据自己的需要来编写或使用LLVM Pass。LLVM提供了丰富的API和文档,可以帮助用户快速入门。
LLVM的核心是一个库,而不是一个具体的二进制程序。 不过,LLVM这个项目本身也基于这个库实现了周边的工具, 下面列出了几个重要的命令行工具,光看名字就可以知道它们大概在做什么
- llvm-as:把LLVM IR从人类能看懂的文本格式汇编成二进制格式。注意:此处得到的不是目标平台的机器码。
- llvm-dis:llvm-as的逆过程,即反汇编。 不过这里的反汇编的对象是LLVM IR的二进制格式,而不是机器码。
- opt:优化LLVM IR。输出新的LLVM IR。
- llc:把LLVM IR编译成汇编码。需要用as进一步得到机器码。
- lli:解释执行LLVM IR。
源代码
- 根目录下,最重要的就是include和lib这两个文件夹。include文件夹包含了其它项目在使用LLVM核心库时需要包含的头文件,而lib文件夹里放的就是LLVM核心库的实现。分别打开lib和include,可以看到很多文件与子文件夹。有经验的读者应该能从名字大概猜到其实现的东西。比如,lib/IR子文件夹肯定是存放了与IR相关的代码,lib/Target子文件夹肯定与生成目标平台机器码有关。又比如,include/llvm/Pass.h文件里面声明了Pass类用来给你继承去遍历、修改LLVM IR。 当然,我们现在不必知道每个模块是干什么的。 等有需要再去查看官方文档吧。
- 根目录下还有一个tools文件夹,这里面就存放了我上面所说的周边工具。 打开这个目录,就可以看到类似llvm-as这样的子目录。显然这就是llvm-as的实现。
Clang
Clang 是 LLVM 编译器工具集的前端,支持 C、C++、Objective-C 和 Objective-C++ 等编程语言。它采用了 LLVM 作为其后端,由 LLVM2.6 开始,一起发布新版本。
Clang 的目标是提供一个 GNU 编译器套装的替代品,支持了 GNU 编译器大多数的编译设置以及非官方语言的扩展。
Clang 的主要功能包括:
- 语法分析:Clang 可以分析 C、C++、Objective-C 和 Objective-C++ 等编程语言的语法。
- 代码生成:Clang 可以生成 LLVM 中间代码(IR)。
- 代码优化:Clang 可以对 LLVM IR 进行优化,以提高代码的性能和可移植性。
- 代码分析:Clang 可以对 LLVM IR 进行分析,以获取代码的信息,例如代码的调用关系、变量的使用情况等。
Clang 在许多操作系统和平台上都有可用版本,包括 macOS、Linux、Windows、iOS 和 Android。
Clang 是一个开源项目,由 LLVM 开发小组维护。
以下是 Clang 的一些优势:
- 速度快:Clang 的编译速度比 GCC 快。
- 体积小:Clang 的代码体积比 GCC 小。
- 可扩展性强:Clang 支持插件式架构,可以通过编写插件来扩展 Clang 的功能。
Clang 是一个功能强大、高效的编译器,可以用于各种目的。