我如何维护语言引导程序?
How can I maintain a language boostrap?
我正在开发一个将 bootstrapped 的编译器,即编译器将从源代码编译自身。我正在实现一个精简的 C++ 版本的编译器作为垫脚石。我担心的是,随着时间的推移,真正的编译器将支持 C++ 实现不支持的功能,并且编译器源代码将利用这些功能。一旦发生这种情况,C++ 实现将无法使用,并且失去了从无到有的能力。
git 历史可以重复使用,从以前的状态开始,朝着最终结果努力。另一种选择是确保基于 C++ 的编译器始终能够编译 "real" 编译器,方法是根据需要扩展 C++ 版本,或者从不允许在真实编译器的源代码中使用不受支持的功能。这些都有各自的缺点。我想知道是否有其他技术适用于其他语言。
保持编程语言 bootstrap 能力的好方法是什么?
如有好奇,请查看 http://plange.tech
这取决于您的编译器的目标语言是什么。
一般建议是保留编译器的一些翻译变体。了解 Tombstone diagrams and partial evaluation(特别是二村预测)。
Ocaml 包含一个字节码变体(运行ning 在相当稳定的字节码虚拟机上)并且将 ocamlc
编译器的(可移植)字节码保存在其 git
或 svn
存储库(请参阅官方存储库中的 boot/ocamlc
)。
在我的GCC MELT(这是一种类 Lisp 语言,翻译成 C++ 适合作为 GCC 插件,遗憾的是我没有时间维护它),我将翻译和生成的 C++ 代码保存在 generated/
svn
存储库中的子目录。
许多编译器生成 C 代码(例如 bigloo, chicken, ...) are keeping the generated C code (even in the version control repository). C is so universal (and can be viewed as a nearly portable assembler) that it is often used as the target language of many experimental languages implementations. A lot of experimental compilers are targetting C。您可能会维护多个目标(其中一个是 C)。或者您也可以有一个简单的解释器(以便能够 运行你的引导编译器)。
显然(根据您的评论),您选择 LLVM 作为目标语言。然后你依赖于 LLVM 语言(规范)的稳定性。您最好将翻译后的表格保留为 LLVM assembly language(文本形式),例如在您的版本控制存储库中(因为翻译后的形式与您的源代码一样珍贵)。
如果 LLVM 汇编语言发展不兼容,你就会有麻烦了。当发生这种情况时,您可以将文本形式(这就是为什么文本形式更可取,因为它更好地转换)转换为更新的 LLVM 语法(假设它没有太大变化),或一些类似的语言,如低级 C 或 Gimple,或使用以前的 LLVM 代码生成器一段时间。顺便说一句,由于类似的原因,C 作为目标语言是成功的:它的发展大部分是兼容的,并且被广泛使用。
Go、D 和 Rust 为常见系统和架构保留二进制 ELF 可执行文件 (Linux/x86-64)。 IIRC 其中一些正在安装期间(从稳定 URL)下载编译器的先前二进制文件。
Bones Scheme 编译器直接生成 x86-64 汇编程序,因此它将其汇编代码与其源代码一起分发。
一些编译实现某种标准语言(的超集)(例如,SBCL,对于 Common Lisp)注意在多个平台上可编译(因此 SBCL 可以在 CLisp 上引导)。因此,核心 SBCL 编译器使用严格且可移植的 Common Lisp 进行编码。
您的增量引导想法是众所周知的方法。 J.Pitrat 的博客包含关于该想法的几个 entries。
实际上,您应该 "cold-bootstrap" 经常实施(例如,从版本控制存储库中的翻译变体开始),至少每周一次。 Bootstrap 失败是痛苦的错误。有时(当然是在每次发布时,可能更频繁)您会将新翻译的表格复制到您的版本控制存储库中。之后一定要 运行 进行广泛的测试。
可能 "cold-bootstrap" 过程会使用几个小时的 CPU,但您需要经常 运行 它(至少每月一次,可能更频繁),以确保您的实施处于正常状态。您甚至可以测试生成的翻译变体是否能够分几个阶段重新编译自身。顺便说一句,GCC 至少分 3 个阶段进行引导(为此需要几个小时的计算机),这是有充分理由的。
我正在开发一个将 bootstrapped 的编译器,即编译器将从源代码编译自身。我正在实现一个精简的 C++ 版本的编译器作为垫脚石。我担心的是,随着时间的推移,真正的编译器将支持 C++ 实现不支持的功能,并且编译器源代码将利用这些功能。一旦发生这种情况,C++ 实现将无法使用,并且失去了从无到有的能力。
git 历史可以重复使用,从以前的状态开始,朝着最终结果努力。另一种选择是确保基于 C++ 的编译器始终能够编译 "real" 编译器,方法是根据需要扩展 C++ 版本,或者从不允许在真实编译器的源代码中使用不受支持的功能。这些都有各自的缺点。我想知道是否有其他技术适用于其他语言。
保持编程语言 bootstrap 能力的好方法是什么?
如有好奇,请查看 http://plange.tech
这取决于您的编译器的目标语言是什么。
一般建议是保留编译器的一些翻译变体。了解 Tombstone diagrams and partial evaluation(特别是二村预测)。
Ocaml 包含一个字节码变体(运行ning 在相当稳定的字节码虚拟机上)并且将 ocamlc
编译器的(可移植)字节码保存在其 git
或 svn
存储库(请参阅官方存储库中的 boot/ocamlc
)。
在我的GCC MELT(这是一种类 Lisp 语言,翻译成 C++ 适合作为 GCC 插件,遗憾的是我没有时间维护它),我将翻译和生成的 C++ 代码保存在 generated/
svn
存储库中的子目录。
许多编译器生成 C 代码(例如 bigloo, chicken, ...) are keeping the generated C code (even in the version control repository). C is so universal (and can be viewed as a nearly portable assembler) that it is often used as the target language of many experimental languages implementations. A lot of experimental compilers are targetting C。您可能会维护多个目标(其中一个是 C)。或者您也可以有一个简单的解释器(以便能够 运行你的引导编译器)。
显然(根据您的评论),您选择 LLVM 作为目标语言。然后你依赖于 LLVM 语言(规范)的稳定性。您最好将翻译后的表格保留为 LLVM assembly language(文本形式),例如在您的版本控制存储库中(因为翻译后的形式与您的源代码一样珍贵)。
如果 LLVM 汇编语言发展不兼容,你就会有麻烦了。当发生这种情况时,您可以将文本形式(这就是为什么文本形式更可取,因为它更好地转换)转换为更新的 LLVM 语法(假设它没有太大变化),或一些类似的语言,如低级 C 或 Gimple,或使用以前的 LLVM 代码生成器一段时间。顺便说一句,由于类似的原因,C 作为目标语言是成功的:它的发展大部分是兼容的,并且被广泛使用。
Go、D 和 Rust 为常见系统和架构保留二进制 ELF 可执行文件 (Linux/x86-64)。 IIRC 其中一些正在安装期间(从稳定 URL)下载编译器的先前二进制文件。
Bones Scheme 编译器直接生成 x86-64 汇编程序,因此它将其汇编代码与其源代码一起分发。
一些编译实现某种标准语言(的超集)(例如,SBCL,对于 Common Lisp)注意在多个平台上可编译(因此 SBCL 可以在 CLisp 上引导)。因此,核心 SBCL 编译器使用严格且可移植的 Common Lisp 进行编码。
您的增量引导想法是众所周知的方法。 J.Pitrat 的博客包含关于该想法的几个 entries。
实际上,您应该 "cold-bootstrap" 经常实施(例如,从版本控制存储库中的翻译变体开始),至少每周一次。 Bootstrap 失败是痛苦的错误。有时(当然是在每次发布时,可能更频繁)您会将新翻译的表格复制到您的版本控制存储库中。之后一定要 运行 进行广泛的测试。
可能 "cold-bootstrap" 过程会使用几个小时的 CPU,但您需要经常 运行 它(至少每月一次,可能更频繁),以确保您的实施处于正常状态。您甚至可以测试生成的翻译变体是否能够分几个阶段重新编译自身。顺便说一句,GCC 至少分 3 个阶段进行引导(为此需要几个小时的计算机),这是有充分理由的。