|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Resolving Rust's forward progress guarantees" |
| 4 | +author: Mark Rousskov |
| 5 | +description: "Should side-effect be the fix?" |
| 6 | +team: the compiler team <https://www.rust-lang.org/governance/teams/compiler> |
| 7 | +--- |
| 8 | + |
| 9 | +There has been a longstanding miscompilation in Rust: programs that do not make |
| 10 | +[forward progress]. Note that the previous link is to the C++ definition; Rust |
| 11 | +is not C++, but currently LLVM optimizes all LLVM IR with the assumption that a |
| 12 | +lack of forward progress is undefined behavior. |
| 13 | + |
| 14 | +Note also that Rust does not define a lack of forward progress as [undefined |
| 15 | +behavior], while C++ does. It is particularly common to encounter the |
| 16 | +miscompilation "intentionally" when writing panic handlers and other such code |
| 17 | +with a body of `loop {}`. Some users also report that they've unintentionally |
| 18 | +hit this bug in recursive code which accidentally lacks a base case. |
| 19 | + |
| 20 | +Somewhat recently, LLVM added an intrinsic which tells the optimizer that |
| 21 | +forward progress has been made. On nightly Rust, you can enable this with |
| 22 | +`-Zinsert-sideeffect`, which will use some heuristics to insert it where it's |
| 23 | +possibly needed (currently, massively overshooting the minimal set). |
| 24 | + |
| 25 | +However, recent attempts to enable this intrinsic by default hit a snag: it's |
| 26 | +very expensive on compile times to do so ([3-30% regressions][compile-time |
| 27 | +regressions]). There is some runtime effect as well; check builds (which do not |
| 28 | +generate LLVM IR or run LLVM passes) regressed by up to 3-7%. |
| 29 | + |
| 30 | +The current implementation in rustc emits calls to the side effect intrinsic |
| 31 | +very aggressively; certainly in way more cases than is strictly necessary. |
| 32 | +However, there's not really any good ideas on how to improve the analysis rustc |
| 33 | +does without missing edge cases: we'd have to be "as good" as LLVM to emit only |
| 34 | +when necessary. |
| 35 | + |
| 36 | +Upstream, in LLVM, discussion has been ongoing for some time around whether, and |
| 37 | +how to, adjust LLVM's model to permit frontends for languages like Rust to |
| 38 | +opt-out of the forward progress guarantees. It seems unlikely that a solution |
| 39 | +will materialize in upstream LLVM that allows us to opt-out in the short term. |
| 40 | + |
| 41 | +However, having said that, side effect itself is likely improvable to at least |
| 42 | +avoid the excessive consecutive calls, as demonstrated by this [IR][IR-test] |
| 43 | +that occur after LLVM optimizations. It seems plausible that those improvements |
| 44 | +may also reduce the compile time hit that we see when enabling side effect on |
| 45 | +the rustc side. Having said that, how simple these improvements are is unclear. |
| 46 | + |
| 47 | +We would love to hear feedback and suggestions on how to resolve this problem! |
| 48 | +Please leave feedback on this internals thread (link to be filled in right |
| 49 | +before merging). |
| 50 | + |
| 51 | +[IR-test]: https://gist.github.com/nikic/7e521def71d106c345a255e464b18d3f |
| 52 | +[compile-time regressions]: https://perf.rust-lang.org/compare.html?start=66b0c97070f422cb82baaaafc79ee94cab4396c5&end=548b5e75afd6bad696920dfdb69c9812ce0488f1 |
| 53 | +[forward progress]: https://en.cppreference.com/w/cpp/language/memory_model#Forward_progress |
| 54 | +[undefined behavior]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#undefined-behavior |
0 commit comments