在编译器后端,做代码优化和为每个目标平台生成汇编代码,工作量是很大的。那么,有什么办法能降低这方面的工作量,提高我们的工作效率呢?答案就是利用现成的工具。
在前端部分,我就带你使用 Antlr 生成了词法分析器和语法分析器。那么在后端部分,我们也可以获得类似的帮助,比如利用 LLVM 和 GCC 这两个后端框架。
相比前端的编译器工具,如 Lex(Flex)、Yacc(Bison)和 Antlr 等,对于后端工具,了解的人比较少,资料也更稀缺,如果你是初学者,那么上手的确有一些难度。不过我们已经用 20~24 讲,铺垫了必要的基础知识,也尝试了手写汇编代码,这些知识足够你学习和掌握后端工具了。
本节想先让你了解一些背景信息,所以会先概要地介绍一下 LLVM 和 GCC 这两个有代表性的框架的情况,这样,当我再更加详细地讲解 LLVM,带你实际使用一下它的时候,你接受起来就会更加容易了。
两个编译器后端框架:LLVM 和 GCC
LLVM 是一个开源的编译器基础设施项目,主要聚焦于编译器的后端功能(代码生成、代码优化、JIT……)。它最早是美国伊利诺伊大学的一个研究性项目,核心主持人员是 Chris Lattner(克里斯·拉特纳)。
LLVM 的出名是由于苹果公司全面采用了这个框架。苹果系统上的 C 语言、C++、Objective-C 的编译器 Clang 就是基于 LLVM 的,最新的 Swift 编程语言也是基于 LLVM,支撑了无数的移动应用和桌面应用。无独有偶,在 Android 平台上最新的开发语言 Kotlin,也支持基于 LLVM 编译成本地代码。
另外,由 Mozilla 公司(Firefox 就是这个公司的产品)开发的系统级编程语言 RUST,也是基于 LLVM 开发的。还有一门相对小众的科学计算领域的语言,叫做 Julia,它既能像脚本语言一样灵活易用,又可以具有 C 语言一样的速度,在数据计算方面又有特别的优化,它的背后也有 LLVM 的支撑。
OpenGL 和一些图像处理领域也在用 LLVM,还看到一个资料,说阿里云的工程师实现了一个 Cava 脚本语言,用于配合其搜索引擎系统 HA3。
还有,在人工智能领域炙手可热的 TensorFlow 框架,在后端也是用 LLVM 来编译。它把机器学习的 IR 翻译成 LLVM 的 IR,然后再翻译成支持 CPU、GPU 和 TPU 的程序。
所以这样看起来,你所使用的很多语言和工具,背后都有 LLVM 的影子,只不过你可能没有留意罢了。所以在我看来,要了解编译器的后端技术,就不能不了解 LLVM。
与 LLVM 起到类似作用的后端编译框架是 GCC(GNU Compiler Collection,GNU 编译器套件)。它支持了 GNU Linux 上的很多语言,例如 C、C++、Objective-C、Fortran、Go 语言和 Java 语言等。其实,它最初只是一个 C 语言的编译器,后来把公共的后端功能也提炼了出来,形成了框架,支持多种前端语言和后端平台。最近华为发布的方舟编译器,据说也是建立在 GCC 基础上的。
LLVM 和 GCC 很难比较优劣,因为这两个项目都取得了很大的成功。
在本课程中,我们主要采用 LLVM,但其中学到的一些知识,比如 IR 的设计、代码优化算法、适配不同硬件的策略,在学习 GCC 或其他编译器后端的时候,也是有用的,从而大大提升学习效率。
接下来,我们先来看看 LLVM 的构成和特点,让你对它有个宏观的认识。
了解 LLVM 的特点
LLVM 能够支持多种语言的前端、多种后端 CPU 架构。在 LLVM 内部,使用类型化的和 SSA 特点的 IR 进行各种分析、优化和转换:
LLVM 项目包含了很多组成部分:
LLVM 核心(core)。就是上图中的优化和分析工具,还包括了为各种 CPU 生成目标代码的功能;这些库采用的是 LLVM IR,一个良好定义的中间语言,在上一讲,我们已经初步了解它了。
Clang 前端(是基于 LLVM 的 C、C++、Objective