首先简要介绍一下LLVM。LLVM是一个针对LLVM Intermediate Representation(IR,中间语言)的跨平台优化编译器,它的模块化设计很好,使得这个编译器中的很多功能可以被单独实现或者改进,这与其C++实现无法分开。由此,LLVM可以被设计成很多语言编译器实现的后端,负责处理程序优化和跨平台,而前端只需将程序转换成LLVM IR即可。比如说,Clang就是基于LLVM实现的C/C++编译器,它的主要功能就是将C/C++程序转换成LLVM IR,然后由LLVM负责后续的工作。
LLVM技术上的(最大)优势就在于它的模块化设计。在LLVM中,IR的解析,优化,汇编码的生成,寄存器分配,汇编码优化以及机器码生成,各种类型的二进制文件生成全部都是接口定义清晰的模块完成的,很容易分别改进或者添加定制功能。而且由于LLVM的C++实现,很多模块理解和使用比较容易。这些特性使得LLVM可以很容易地被用在科研和生产实践当中。反观GCC,模块化做得不如LLVM好,这使得它定制或者改进比较不方便。
我觉得也许不仅仅在于 LLVM 是开源的……
我个人认为它最大的优势是模块化。
LLVM 的 backend 把每个阶段都分的如此的清晰,于是我们作为使用者可以轻松的(相对GCC)做扩展,比如写一个自定义的 Pass。
而我就可以举一个例子给大家看一下:
如下图是正常 clang + llvm 编译后的 IDA Pro 6.5 - HexRay Disassembly C-代码:
然后,给 LLVM 增加 "代码混淆变型加密" (防逆向)的 Pass 后,再给大家截个图看一下同一个 C 函数被编译成了什么样子:
对的,你没有看错,我只截了一小部分图,因为实在太长了~
如果大家想感受一下有多震撼,那么我就贴一下他们的 Control - Flow - Graph 对比图:
1)
//////////////////////////////////////////// 加密前 /////////////////////////////////////////
2) /////////////////////////////////////////////// 加密后 /////////////////////////////////////////////////////////////
值得注意的是,看上去代码量会膨胀不少,性能也会有所损失。 不过这确确实实的是 LLVM 模块化带来的难以想象的优势。
没有LLVM之前的编译器设计与维护和没有计算机之前的历史研究有点类似,都是所谓“聪明人的苦学问”,搞历史一方面要求博闻强记,另一方面还要耐得住寂寞,除了自己的圈子,基本没人知道你在干什么。从前的编译器设计也是一样,首先要有个好记性还要吃得了苦,而且一旦跳了这个坑就不好爬出来了,这一切在LLVM出现之后得到了极大的改善,头脑一般的票友也可以进来玩玩了,聪明人更是没必要把时间都花在记忆和理解那些混乱不堪的代码上,极大地提高了工作效率。
各种各样的应用(DSL、GPU数据库、TVM、安全、区块链等)----> 生成 LLVM IR ----> 编写针对自己特定应用的优化Pass ----> LLVM的优化 ----> LLVM Code Gen ----> 目标代码(ARM、x86、Hexagon、NVPTX、AMDGPU、WebAssembly...)
也包括:
各种各样的应用(DSL、GPU数据库、TVM、安全、区块链等)----> 生成 LLVM IR ----> 编写针对自己特定应用的优化Pass ----> LLVM的优化 ----> LLVM Code Gen ----> 目标代码(ARM、x86、Hexagon、NVPTX、AMDGPU、WebAssembly,自己的后端(如AI芯片)...)
有了LLVM以后,利用编译器,做编译器,比以往时候都要更简单(笑,真的笑,笑出声,大快所有人心的大好事)。
LLVM has a dad -- Apple