我的第一个使用 LLVM 的语言前端教程

要求: 本教程假设您了解 C++,但不需要任何以前的编译器经验。

欢迎来到“我的第一个使用 LLVM 的语言前端”教程。在这里,我们将逐步实现一个简单的语言,展示它是多么有趣和容易。本教程将使您快速启动并运行,并展示一个使用 LLVM 生成代码的具体示例。

本教程介绍了简单的 “Kaleidoscope” 语言,并在几个章节中逐步构建它,展示它是如何随着时间推移构建的。这使我们能够涵盖一系列语言设计和 LLVM 特有的概念,沿途展示和解释所有代码,并减少了预先需要掌握的大量细节。我们强烈建议您使用此代码 - 复制一份并进行修改和实验。

警告:为了专注于教授编译器技术和特别是 LLVM,本教程展示软件工程原则中的最佳实践。例如,代码大量使用全局变量,不使用 访问者模式 等等……而是保持简单并专注于手头的主题。

本教程分为涵盖各个主题的章节,您可以根据需要跳过

  • 第 1 章:Kaleidoscope 语言和词法分析器 - 这展示了我们的目标以及我们想要构建的基本功能。词法分析器也是构建语言解析器的第一部分,我们使用一个简单的 C++ 词法分析器,它很容易理解。

  • 第 2 章:实现解析器和 AST - 有了词法分析器,我们可以讨论解析技术和基本的 AST 构建。本教程介绍了递归下降解析和运算符优先级解析。

  • 第 3 章:代码生成到 LLVM IR - 准备好 AST 后,我们将展示生成 LLVM IR 是多么容易,并展示一种将 LLVM 纳入您的项目的简单方法。

  • 第 4 章:添加 JIT 和优化器支持 - LLVM 的一个优点是它对 JIT 编译的支持,因此我们将直接深入研究它,并向您展示添加 JIT 支持所需的 3 行代码。后面的章节将展示如何生成 .o 文件。

  • 第 5 章:扩展语言:控制流 - 随着基本语言的启动和运行,我们将展示如何使用控制流操作(‘if’ 语句和 ‘for’ 循环)来扩展它。这让我们有机会讨论 SSA 构造和控制流。

  • 第 6 章:扩展语言:用户定义的运算符 - 本章扩展了语言,允许用户定义任意一元和二元运算符 - 具有可分配的优先级!这使我们能够将“语言”的重要部分构建为库例程。

  • 第 7 章:扩展语言:可变变量 - 本章讨论了添加用户定义的局部变量以及赋值运算符。这展示了在 LLVM 中构建 SSA 形式是多么容易:LLVM 要求您的前端为了使用它而构建 SSA 形式!

  • 第 8 章:编译为目标文件 - 本章解释了如何获取 LLVM IR 并将其编译为目标文件,就像静态编译器一样。

  • 第 9 章:调试信息 - 一种真正的语言需要支持调试器,因此我们添加了调试信息,允许在 Kaleidoscope 函数中设置断点、打印出参数变量和调用函数!

  • 第 10 章:结论和其他花絮 - 本章总结了本系列,讨论了扩展语言的方法,并包括指向“特殊主题”信息的指针,例如添加垃圾回收支持、异常、调试、对“意大利面条式堆栈”的支持等等。

在本教程结束时,我们将编写少于 1000 行(非注释、非空白)代码。通过这么少量的代码,我们将为一个重要的语言构建一个非常好的小型编译器,包括手写词法分析器、解析器、AST,以及代码生成支持 - 包括静态和 JIT!这广度是对 LLVM 优势的有力证明,并表明了为什么它是语言设计者和其他需要高性能代码生成的人如此受欢迎的目标。