前置技能: 本教程要求你有 C++ 基础,但你并不需要先去做一个编译器再来看这个教程。

欢迎来到“我的第一个基于 LLVM 的语言前端”教程。在本教程我们将会自己动手实现一个简单的语言,你将会看到这有多简单以及有趣。在接下来的教程里,你将会快速而确切地学习到使用 LLVM 来生成程序的方法。

本教程引入了一门简单的叫 Kaleidoscope(万花筒) 的语言。在接下来的几章内,我们将会一步步向你展示构建它的过程。在这个过程中我们会介绍一系列的语言设计与 LLVM 思想,给予你实现这门语言的程序的详细代码,并尽量减少各种各样的细节。我们强烈建议你跟着教程一起动手,在我们给予的代码的基础上去 hack,去实验,去探索新的可能。

警告: 为了专注于教授编译技术与 LLVM,本教程 不会 展示最佳的工程实践代码。比如说,我们的代码将会到处全局变量,并且不使用访问者模式 (来从 AST 生成代码/干其他活). 这种风格有助于我们保持代码简单并且专注于主题。

本教程分为以下几章,分别介绍了不同领域的内容。如果你想跳过某些章节的话,请随意:

译者注:每章的最后有直到本章为止的全部代码,可以直接去复制一下然后跳到下一章

  • 第一章: Kaleidoscope 与 lexer (未翻译): 本章介绍了我们要实现的语言 Kaleidoscope 的基础功能。构建 lexer 是构建一门语言的 parser 的第一步,我们将手写一个简单易懂的 C++ lexer.
  • 第二章:实现 Parser 与 AST (未翻译): 有了 lexer,我们就可以着手于解析技术与简单的 AST 构建了。本章描述了递归下降解析法与运算符优先级解析法 (operator-precedence parsing).
  • 第三章:生成 LLVM IR (未翻译): 一旦有了 AST,生成 LLVM IR 就不在话下了。在本章我们将会讨论从 AST 生成 IR 的方法,与此同时你也能学习到将 LLVM 集成进你的项目里的一个简单方法。
  • 第四章:增加 JIT 与优化: LLVM 的一个优势在于它支持 JIT 编译,所以我们稍微深入地去介绍一下这项技术。有了 LLVM,仅需 3 行代码就能使你的语言具有 JIT 功能!在后续章节中我们将会介绍如何生成目标文件。
  • 第五章:扩展语言:控制流: 现在我们有了一个能跑的语言了!我们将会给它加上控制流操作符 (iffor). 我们也将趁此机会介绍 SSA 构造与控制流。
  • 第六章:扩展语言:自定义运算符: 本章扩展了语言,使用户能自定义他们自己的一元与二元操作符 – 包括其优先级!这将允许我们将显著规模的 “语言特性” 改为由基本库实现。
  • 第七章:扩展语言:变量: 本章将介绍如何为语言增加用户定义的局部变量与赋值运算符。这也将会展示出在 LLVM 里构造 SSA Form 的简单 – 使用 LLVM 并 要求前端直接生成 SSA Form!
  • 第八章:编译到目标文件: 本章解释了如何像普通的编译器一样生成目标文件 – 产生 LLVM IR 并将其编译到目标文件。
  • 第九章:调试信息 (未翻译): 一门实用的语言需要支持调试器,所以我们要给 Kaleidoscope 增加在函数内设置断点、打印参数、调用函数的能力!
  • 第十章:结语与其他 (未翻译): 本章讨论扩展语言的不同方式,借此串联起整个教程。本章还包括了指向特定主题的"指针", 比如 GC,异常,调试与对 “意大利面条式调用栈 (spaghetti stacks)” 的支持。

当教程介绍后,我们将写出大概 1000 行少一点的代码 (不计空白与注释). 仅仅是使用这么少的代码,我们就能实现一个不平凡的语言的编译器,包括了一个手写的 lexer, parser, AST 与代码生成 – 同时支持 JIT 与静态代码生成!这一千行代码所实现的广泛功能只是 LLVM 力量的冰山一角 – 这正是 LLVM 在语言设计者与优化者里如此流行的原因。