解决 Rust 的前向进展保证

2020 年 3 月 19 日 · Mark Rousskov 代表编译器团队

Rust 中长期存在一个编译错误:程序未能实现前向进展。请注意,之前的链接是指 C++ 的定义;Rust 不是 C++,但目前 LLVM 会优化所有 LLVM IR,并假设缺乏前向进展是未定义行为。

还要注意,Rust 没有将缺乏前向进展定义为未定义行为,而 C++ 却这样定义。在编写 panic 处理程序和其他带有 loop {} 主体的代码时,“故意”遇到此编译错误是很常见的。一些用户还报告说,他们在递归代码中意外地遇到了这个错误,而这些递归代码恰好缺少基本情况。

最近,LLVM 添加了一个内在函数,它告诉优化器已经取得了前向进展。在 nightly Rust 中,你可以使用 -Zinsert-sideeffect 启用此功能,它将使用一些启发式方法在可能需要的地方插入它(目前,大大超出了最小集合)。

然而,最近默认启用此内在函数的尝试遇到了障碍:这样做会严重增加编译时间(3-30% 的回归)。运行时也会有一些影响;检查构建(不生成 LLVM IR 或运行 LLVM pass)的性能下降了 3-7%。

目前 rustc 中的实现非常激进地发出对副作用内在函数的调用;当然,在远远超出严格必要的情况下。然而,在不遗漏边缘情况的情况下,如何改进 rustc 所做的分析并没有真正的好主意:我们必须“像”LLVM 一样“出色”才能仅在必要时发出。

在上游 LLVM 中,关于是否以及如何调整 LLVM 的模型,以允许像 Rust 这样的语言的前端选择不使用前向进展保证的讨论已经持续了一段时间。似乎不太可能在短期内出现上游 LLVM 中的解决方案,允许我们选择退出。

然而,话虽如此,副作用本身很可能可以改进,至少可以避免过多的连续调用,正如这个 IR 在 LLVM 优化之后出现的那样所证明的。看起来这些改进也可能减少我们在 rustc 侧启用副作用时看到的编译时间冲击。话虽如此,这些改进有多简单尚不清楚。

我们很乐意听到关于如何解决这个问题的反馈和建议!请在此内部线程上留下反馈。