探索 QBE:LLVM 的轻量级替代方案

构建编译器通常被视为系统编程中最艰巨的任务。多年来,业界一直严重依赖 LLVM,它已成为提供巨大优化能力和广泛架构支持的强大引擎。然而,对于许多开发者来说,LLVM 的复杂性和编译时开销过于沉重。于是有了 QBE,一个设计为更精简替代方案的小型、可预测且高效的编译器后端。

什么是 QBE?

QBE 是一个编译器后端,它接收特定的中间表示 (IR) 并为各种架构生成机器码。与采用大规模优化套件和带有显式 phi 节点的复杂静态单赋值 (SSA) 形式的 LLVM 不同,QBE 追求的是简单。它为语言实现者提供了一种目标机器码的方式,而无需为每种架构编写自己的汇编生成器。

生态系统:从 cproc 到 Blaise

QBE 最引人注目的方面之一是围绕它建立起的规模虽小但专注的工具和语言生态系统。由于 QBE 非常轻量,它成为了实验性或极简主义编译器的一个极佳目标平台。

cproc

cproc 是一个基于 QBE 实现的 C 编译器。用户指出,当与 QBE 结合使用时,cproc 可以达到 gcc -O2 性能的约 70%,同时代码体积仅为后者的极小部分。这使得对于那些比起 GCC 或 LLVM 的庞大代码库,更倾向于“简单纯粹的 C 编写的编译器”的人来说,它成为了一个极具吸引力的选择。

Blaise

除了 C 语言,QBE 还被用于驱动更现代的语言。Blaise,一种现代 Object Pascal 实现,利用 QBE 作为其后端,展示了该项目在不同编程范式下的多功能性。

技术权衡与局限性

虽然 QBE 的极简主义是其最大的优势,但它也伴随着固有的技术权衡。资深开发者和编译器工程师强调了在选择 QBE 而非像 LLVM 这样更强大的系统时必须考虑的几个关键局限性:

  • ABI 与结构体处理: QBE 处理在寄存器中传递和返回结构体的 ABI,但它通常会通过在栈与寄存器之间进行多余的拷贝来完成,这可能会引入轻微的性能开销。
  • 调试信息: 为数据生成调试信息目前是一个挑战。这可能是由于缺乏全面的汇编器支持,或者是 DWARF 格式本身的复杂性所致。
  • 内联与行号: 行号调试信息指令目前尚未文档化,且不支持内联函数,这使得对优化后的代码进行深度调试变得更加困难。
  • 内存管理: IR 不原生支持识别 GC (Garbage Collection) 根。这意味着目标为 QBE 的语言必须实现保守式 GC,或者显式地管理栈。

简单的魅力

尽管存在这些局限性,社区发现 QBE 的“smol”特性非常有吸引力。一位用户提到,它非常容易扩展;例如,据报道,通过 AI 提示词 (Claude) 添加 MIPS 支持是相当成功的,甚至可以编译自定义语言到 Nintendo 64。

"但它很 smol,很高效,而且它不会让你去处理 phi 节点!"

对于那些厌倦了现代编译器基础设施复杂性的开发者来说, QBE 提醒着人们,一个可预测、可用且小巧的工具集可以为大多数专门的任务提供足够的动力。

Sources