在 Hacker News 上,一篇 2008 年的老文《Want to write a compiler? Just read these two papers》最近再次引发热议。文章推荐 Jack Crenshaw 的《Let's Build a Compiler!》系列与 Nanopass Framework 论文作为入门资源,引发了大量关于编译器学习路径的讨论。然而,如果我们把目标从「写一个玩具编译器」转向「从事生产级编译器工程」,就会发现这两条路径之间存在显著差异。
传统学术路径的局限
传统路径的核心是增量式构建一个端到端的编译器,从词法分析到目标代码生成,每一步都产生可运行的结果。Crenshaw 的教程以单遍编译器为主,解析与代码生成交织在一起,这种方式适合理解编译器的基本流程,但存在一个关键问题:没有引入中间表示(Intermediate Representation,IR)。
HN 评论中一位用户指出了这个痛点:「Crenshaw 的系列有一个主要缺陷:根本没有内部程序表示,即没有抽象语法树。绕过这一步意味着放弃灵活性,而在更高层次的语言中,树结构的创建和操作非常 trivial,这也是 Lisp、Erlang、Haskell 的设计初忠。」这种缺失直接导致后续优化能力的上限。
Nanopass 框架虽然引入了分阶段变换的思想 —— 将编译器分解为数十甚至数百个小的 Pass,每个 Pass 完成一个简单转换 —— 但其教学目标是让学习者理解编译器各阶段的职责,而非掌握生产环境中真正需要的技能。评论中提到,真正区分玩具编译器与可扩展编译器的,是每个 Pass 都有明确的输入输出语言,这种纪律性虽然能捕获许多 bug,但与工业级编译器的复杂度相比仍然相差甚远。
生产级编译器的能力矩阵
现代生产级编译器(GCC、LLVM、Rustc、Swift 等)的核心技能栈与传统学术路径有本质区别。一位在编译器领域工作多年的开发者在 HN 上分享:「Dragon Book 几乎让我放弃了写编译器的念头。我不知道为什么有人推荐它。我想你可能比我聪明得多。」这不是个案 —— 大量评论指出 Dragon Book 过于注重理论,从 350 页的解析理论开始,在没有足够 RAM 存储完整源文件的年代优化词法分析器,却跳过了大多数想写编译器的人真正关心的内容:类型推断优化、IR 优化、目标代码生成。
生产级编译器工程师需要掌握的能力可以拆解为以下几个层次。第一层是编译器基础,包括词法与语法分析、语义分析、类型系统、SSA(静态单赋值)形式与数据流分析。第二层是 LLVM IR,包括 IR 语法与结构、常用 Pass(死代码消除、常量折叠、循环优化等)、调试技巧与优化策略。第三层是代码生成与 ABI,包括寄存器分配、调用约定、目标指令选择与调度。第四层是 MLIR 高级特性,包括方言(Dialect)设计、模式重写(Pattern Rewriting)、多级 IR 降级(Lowering)管道。
LLVM 与 MLIR 的学习路径参数
对于目标是从事生产编译器开发的工程师,建议的学习路径应当是:首先在一个玩具语言上完成完整的编译器管道,理解从源码到目标码的完整流程;然后学习 LLVM IR 的阅读与编写,通过 Kaleidoscope 教程入门,掌握基本的优化概念;接着构建若干优化或分析 Pass,理解真实编译器中的 Pass 架构;之后学习 MLIR 核心概念,包括方言、类型、属性、模式重写与 Pass 管道;最后实现一个端到端的 MLIR 管道,涵盖从高级 IR 降级到 LLVM IR 再到目标后端的完整流程。
关于学习资源的选择,HN 评论中推荐了几条实用路径。Niklaus Wirth 的《Compilers》全书不足 100 页,包含完整编译器的源代码,是最短平快的入门材料。Nora Sandler 的《Writing a C Compiler》受 Ghuloum 论文启发,以现代视角重写了增量式编译器构建过程。Cornell 的 CS6120 课程提供了现代编译器理论的系统学习路径,涵盖 SSA、循环优化与向量化等主题。
工程实践中的关键差异
传统学术路径强调「从零实现」,而生产环境更强调与现有基础设施的集成。HN 评论中一位开发者指出:「大多数现代新语言项目选择发射 LLVM IR 而不是尝试匹配 Clang 的内部 AST。Rust、Swift、Zig 都使用 LLVM 作为后端,你可以免费获得优化 Pass 与多架构代码生成,代价是跳过了后端学习 —— 但后端恰恰是最有趣的部分。」
这揭示了一个核心矛盾:如果目标是学习编译器全栈,LLVM 会封装掉许多底层细节;如果目标是构建生产级语言,LLVM 又是事实上的行业标准。务实的做法是先用传统路径建立直观理解,再用 LLVM/MLIR 学习工业级实现。
HN 上另一位用户的建议更具实操性:「如果你有一小时入门,我建议打开《Engineering a Compiler》这本书,学习第 9.3 节的 SSA(静态单赋值)。这本书以 SSA 处理著称。第 1-8 章不是理解 SSA 的前置条件,你可以直接带着一个清晰的收获离开。如果在支配性与活跃性分析方面有困难,可以参考第 9.2 节。」
总结与建议
传统两篇论文路径的价值在于降低入门门槛、建立直观理解,但生产级编译器工程需要的技能远超出这些。核心差异在于:学术路径产出可运行但无法扩展的玩具编译器,生产路径产出可维护、可优化、可移植的专业工具链。建议的学习组合是先用 Wirth 或 Crenshaw 建立直觉,再用 LLVM 系统学习工业级实现,最后根据目标选择深入后端(LLVM Pass 开发)或前端高级特性(MLIR 方言设计)。
资料来源:Hacker News 讨论「Want to write a compiler? Just read these two papers」(https://news.ycombinator.com/item?id=47776796)、Mike Acton 原博客文章 (https://prog21.dadgum.com/30.html)。