Nibble: 用 C 语言实现的极简主义 LLVM 前端
现代编译器构建的格局通常以庞大、复杂的框架和多轮架构为特征。然而,由 glouwbug 开发的项目 Nibble,通过使用大约 3,000 行 C 代码实现了一个单轮(single-pass)LLVM 前端,挑战了这种极简主义。通过剥离抽象语法树(AST)和动态内存分配 (malloc) 等传统编译器组件,Nibble 为语言设计和架构中的极端极简主义提供了一个引人注目的案例。
极简主义架构
Nibble 的主要目标是从源代码到 LLVM IR 的高效、单轮转换。大多数传统编译器遵循词法分析、解析、递归下降到 AST,然后遍历该 AST 以生成代码的流水线。Nibble 跳过了整个过程。
通过消除 AST,编译器不需要为它正在解析的同一种语言构建中间表示。相反,它在处理解析的同一轮过程中直接生成 LLVM IR。这种方法显著降低了开销的复杂性和编译器自身的内存管理内存占用。
关键技术约束
为了进一步推向极简主义的边界,作者实现了以下约束:
- 不使用
malloc:编译器在过程中避免了动态内存分配,这确保了确定性的内存占用,并避免了与动态分配相关的碎片化问题。 - 无外部依赖:该项目使用纯 C 编写,仅依赖语言的核心库,确保了高可移植性和轻量级的占用。
- 单轮(Single Pass):转换发生在对同一份源代码的单次遍历中,这是处理转换过程的一种非常高效的方式。
社区反馈与观察
虽然该项目受到了社区的广泛好评,但 Hacker News 社区也提出了一些技术疑问并要求提供更多文档。
语言规范
主要的批评之一是缺乏全面的语言规范。用户指出,虽然该项目是一个可运行的实现,但语言的其他功能——例如 defer 关键字和内存管理——对于阅读源代码的人来说仍然是一个谜。正如一位用户 @childintime 所指出的:
"喜欢这个。但没有关于语言的解释。例如,我在检查的两个
main.n中没有看到defer,而且内存管理仍然是一个谜。"
性能与可扩展性
社区对 IR 生成的可扩展性感到好奇。由于编译器是单轮的且缺乏 AST,某些可能需要对同一代码进行多次遍历的高级优化和复杂语言特性在本质上是受限的。这导致其他人想知道,随着语言复杂度的增加,IR 可以扩展到何种程度。
结论
Nibble 是对项目能够推向编译器前端约束能力的一种证明。通过证明一个功能完备的 LLVM 前端可以在没有 AST 的情况下实现,即通过一种无 AST 的、单轮架构,它提供了一个关于简洁性的宝贵教训。对于对编译器构建感兴趣的人来说,Nibble 是一个真正交付的项目,超越了许多侧边项目的“仅限 README”阶段。