作为一名业余编译器开发者,我在选择合适的编译器目标时遇到了困难。与 80 年代不同,当时人们必须直接针对各种机器架构,现在有许多成熟的选项可供选择。这是一份关于一些流行和有趣选项的简要且不完整的调查。
编译器可以直接输出针对一个或多个架构的机器代码或汇编代码。一个著名的例子是 Tiny C 编译器。它以速度快和体积小而闻名,可以即时编译和运行 C 代码。另一个例子是 Turbo Pascal。您也可以使用自己的编译器这样做,但您需要弄清楚每个架构的 指令集(ISA)的复杂性,以及 寄存器分配 等概念。
大多数现代编译器实际上并不直接发出机器代码或汇编代码。它们首先将源代码降低到语言无关的 中间表示(IR),然后从中生成主要架构(x86-64、ARM64 等)的机器代码。
该领域最著名的工具是 LLVM。它是一个大型的开源编译器库。许多语言的编译器,例如 Rust、Swift、C/C++(通过 Clang)和 Julia,都使用 LLVM 作为 IR 来发出机器代码。
另一个选择是 GNU 编译器集合(GCC),通过其 GIMPLE IR,尽管似乎没有编译器直接使用它。GCC 可以像 LLVM 一样用作库来编译代码,通过 libgccjit。它被用于 Emacs 中即时编译 Elisp。Cranelift 是该领域的另一个新选项,尽管它只支持少数 ISA。
对于那些认为 LLVM 或 GCC 太大或太慢的编译者,存在一些极简主义的替代方案。QBE 是一个专注于简单性的后端,针对“70% 的性能在 10% 的代码中”。它被语言 Hare 使用,该语言优先考虑快速的编译时间。另一个选项是 libFIRM,它使用基于图的 SSA 表示而不是线性 IR。
有时您可以让其他编译器/运行时处理繁重的工作。您可以将代码转换为另一种成熟的高级语言,并利用该语言的现有编译器/运行时和工具链。
在这种情况下,一个常见的目标是 C。由于 C 编译器几乎存在于所有平台,因此生成 C 代码使您的语言具有高度的可移植性。这是 Chicken Scheme 和 Vala 使用的策略。或者,您可以编译到 C++,就像 Jank 一样,如果您喜欢的话。还有 C–,一种由 GHC 和 OCaml 目标的 C 子集。
另一个无处不在的目标是 JavaScript(JS),它是运行代码在 Web 浏览器或 JS 运行时(Node、Deno、Bun)中的一种选择(另一种选择是 WebAssembly)。多种语言,例如 TypeScript、PureScript、Reason、ClojureScript、Dart 和 Elm,都可以转换为 JS。Nim 有趣的是,可以转换为 C、C++ 或 JS。
另一个与 JS 类似的目标是 Lua,一种轻量级且可嵌入的脚本语言,语言如 MoonScript 和 Fennel 可以转换为 Lua。
一种更为小众的方法是针对 Lisp 方言。编译到 Chez Scheme,例如,允许您利用其宏系统、运行时和编译器。Idris 2 和 Racket 使用 Chez Scheme 作为其主要后端目标。
这是应用语言的常见选择。您将代码编译为可移植的字节码,用于 虚拟机(VM)。VM 通常带有 垃圾回收、即时编译 和安全沙箱等功能。
Java 虚拟机(JVM)可能是最流行的。它是许多语言的目标,包括 Java、Kotlin、Scala、Groovy 和 Clojure。其主要竞争对手是 公共语言运行时,最初由 Microsoft 开发,目标语言包括 C#、F# 和 Visual Basic.NET。
另一个值得注意的 VM 是 BEAM,最初为 Erlang 构建。BEAM VM 不是为原始计算速度而设计的,而是为高并发性、容错性和可靠性而设计的。最近,新的语言,如 Elixir 和 Gleam,被创建来针对它。
最后,该类别还包括 MoarVM,它是 Parrot VM 的精神继承者,为 Raku(以前称为 Perl 6)语言而构建。
WebAssembly(Wasm)是一个相对较新的目标。它是一种专注于安全性和效率的可移植二进制指令格式。Wasm 得到了所有主要浏览器的支持,但不仅限于浏览器。WebAssembly 系统接口(WASI)标准为在非浏览器和非 JS 环境中运行 Wasm 提供了 API。Wasm 现在被许多语言作为目标,例如 Rust、C/C++、Go、Kotlin、Scala、Zig 和 Haskell。
元跟踪 和 元编译 框架是一个更复杂的类别。这些不是编译器后端的目标,而是使用它们通过指定解释器来构建自定义的 即时编译 编译器。
最著名的例子是 PyPy,它是使用 RPython 框架创建的 Python 实现。另一个这样的框架是 GraalVM/Truffle,它是来自 Oracle 的多语言 VM 和元跟踪框架。其主要功能是零成本互操作性:GraalJS、TruffleRuby 和 GraalPy 的代码都可以在同一个 VM 上运行,并且可以直接相互调用。
超越主流,您将发现一个非传统和古怪的编译器目标世界。开发人员选择它们是出于学术好奇心、艺术表达或测试可行的编译目标的界限。
Brainfuck:一种只有八个命令的古怪语言,Brainfuck 是 图灵完备 的,并且已经成为编译器的目标作为挑战。人们为 C、Haskell 和 Lambda 演算 编写了编译器。
Lambda 演算:Lambda 演算 是一种仅使用函数及其应用来表达计算的最小编程语言。它经常被用作教育编译器的目标,因为其简单性,以及与计算的基本性质的联系。Hell,Haskell 的一个子集,编译为 简单类型 Lambda 演算。
Lambda 演算:Lambda 演算是一种最小化的编程语言,它仅使用函数及其应用来表示计算。由于其简单性和与计算的基本性质的联系,它经常被用作教育编译器的目标语言。Hell,Haskell的一个子集,编译为简单类型的 Lambda 演算。
SKI 组合子:SKI 组合子演算比 Lambda 演算更加最小化。SKI 演算中的所有程序都可以仅由三个组合子组成:S、K 和 I。MicroHs 编译 Haskell 的一个子集为 SKI 演算。
JSFuck:你知道吗,你可以仅使用六个字符 []()!+ 来编写所有可能的 JavaScript 程序?好吧,现在你知道了。
Postscript:Postscript也是一个图灵完备的编程语言。你的下一个编译器可以以它为目标!
我将编写一个从 C++ 到 JSFuck 的编译器。