解决 Rust 的前向进度保证问题

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

Rust 中存在一个长期存在的错误编译问题:程序没有实现 前向进度。请注意,上面的链接指向 C++ 的定义;Rust 不是 C++,但当前 LLVM 在优化所有 LLVM IR 时,都假设缺乏前向进度是未定义行为。

另请注意,Rust 并未将缺乏前向进度定义为未定义行为,而 C++ 则不然。在编写 panic handler 和其他带有 loop {} 主体的此类代码时,“有意地”遇到此错误编译问题是尤其常见的。一些用户还报告说,他们在意外缺乏基本情况的递归代码中无意中遇到了此错误。

最近,LLVM 添加了一个 intrinsic,用于告知优化器已取得前向进度。在 nightly 版 Rust 中,您可以使用 -Zinsert-sideeffect 来启用此功能,该功能会使用一些启发式方法将其插入到可能需要的地方(目前,远远超出了所需的最小集合)。

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

rustc 中当前的实现非常激进地发出对 side effect intrinsic 的调用;肯定比严格必要的情况多得多。然而,目前没有真正好的想法如何在不遗漏边缘情况的情况下改进 rustc 的分析:我们必须像 LLVM 一样“好”,才能仅在必要时发出调用。

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

然而,话虽如此,side effect 本身很可能可以改进,至少可以避免过多的连续调用,如 LLVM 优化后出现的 IR 所示。这些改进似乎也有可能减少我们在 rustc 端启用 side effect 时看到的编译时间增加。话虽如此,这些改进有多简单尚不清楚。

我们很想听听关于如何解决这个问题的反馈和建议!请在这个内部论坛主题上留下反馈。