-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Distinguish between library and lang UB in assert_unsafe_precondition #121662
Conversation
Some changes occurred to the CTFE / Miri engine cc @rust-lang/miri Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt This PR changes MIR cc @oli-obk, @RalfJung, @JakobDegen, @davidtwco, @celinval, @vakaras This PR changes Stable MIR cc @oli-obk, @celinval, @spastorino, @ouz-a Some changes occurred in compiler/rustc_codegen_cranelift cc @bjorn3 |
This comment has been minimized.
This comment has been minimized.
0cdb9c6
to
a64a002
Compare
This comment has been minimized.
This comment has been minimized.
a64a002
to
ea23c97
Compare
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
ea23c97
to
b268228
Compare
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…=<try> Distinguish between library and lang UB in assert_unsafe_precondition As described in rust-lang#121583 (comment), `assert_unsafe_precondition` now explicitly distinguishes between language UB (conditions we explicitly optimize on) and library UB (things we document you shouldn't do, and maybe some library internals assume you don't do). `debug_assert_nounwind` was originally added to avoid the "only at runtime" aspect of `assert_unsafe_precondition`. Since then the difference between the macros has gotten muddied. This totally revamps the situation. Now _all_ preconditions shall be checked with `assert_unsafe_precondition`. If you have a precondition that's only checkable at runtime, do a `const_eval_select` hack, as done in this PR. r? RalfJung
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (c492ab1): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @bors rollup=never Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 649.3s -> 650.779s (0.23%) |
b268228
to
f8d0380
Compare
library/core/src/intrinsics.rs
Outdated
/// implement debug assertions that are included in the precompiled standard library, but can | ||
/// be optimized out by builds that monomorphize the standard library code with debug | ||
/// Whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` | ||
/// in codegen, and `true` in const-eval/Miri. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe const-eval/Miri should be consistent with runtime here? Every single time we made Miri diverge from that people were caught off-guard by it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does const-eval usually behave differently when you pass --release
? Granted it's just UB checking... but still I expect const
to always work the same regardless of the flags I pass. Even if that's wrong, it feel more like it's part of the type system than the runtime part of Rust.
I'll swap this to work the same because I think it's sufficiently less surprising for Miri and kind of a wash in const-eval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does const-eval usually behave differently when you pass --release?
const-eval usually honors #[cfg(debug_assertions)]
/cfg!(debug_assertions)
the same way everything else does.
So, yes. It feels strange to do anything different here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still open, I think library UB checks during const-eval and Miri should match runtime behavior.
☀️ Test successful - checks-actions |
Finished benchmarking commit (768408a): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 647.204s -> 645.087s (-0.33%) |
Relevant upstream changes: rust-lang/rust#120675: An intrinsic `Symbol` is now wrapped in a `IntrinsicDef` struct, so the relevant part of the code needed to be updated. rust-lang/rust#121464: The second argument of the `create_wrapper_file` function changed from a vector to a string. rust-lang/rust#121662: `NullOp::DebugAssertions` was renamed to `NullOp::UbCheck` and it now has data (currently unused by Kani) rust-lang/rust#121728: Introduces `F16` and `F128`, so needed to add stubs for them rust-lang/rust#121969: `parse_sess` was renamed to `psess`, so updated the relevant code. rust-lang/rust#122059: The `is_val_statically_known` intrinsic is now used in some `core::fmt` code, so had to handle it in (codegen'ed to false). rust-lang/rust#122233: This added a new `retag_box_to_raw` intrinsic. This is an operation that is primarily relevant for stacked borrows. For Kani, we just return the pointer. Resolves #3057
Visiting for weekly rustc-perf triage
|
#122282 might solve part of it? Primary benchmarks are to cranelift-codegen opt-full (-2.03%), cargo opt-full (-0.90%) and clap opt-full (-1.35%). See https://perf.rust-lang.org/compare.html?start=cdb775cab50311de54ccf3a07b331cc56b0da436&end=acc30d0cafd012430f7d0d83addb6fea4c7b2bbf&stat=instructions:u. |
The changes above are chaotic, small, about-same-magnitude changes, and almost all benchmarks that were changed are Perhaps that's not the usual kind of perf report you're used to seeing in perf triage. I think most other kinds of changes produce perf reports where it is educational to look into each benchmark, because the fact that one crate was regressed or not means that there is a sort of code pattern that is now pessimized or handled better. I have certainly done such careful case-by-case analysis myself on many PRs and found it helpful. I firmly believe that such analysis is not useful in these perf reports that are primarily MIR inliner chaos (not noise!!). We could make rather subtle tweaks to the relevant standard library code, and completely change the set of benchmarks that get better or worse. And in addition, for PRs like this where the perf reports are MIR inliner chaos, one should not expect the pre-merge perf report to look the same as the post-merge one. PRs that land between those try builds will provide the perturbations to cause a different chaotic outcome. The exact same logic applies to #122282. It contains some regressions. That PR is good because it improves our ability to optimize MIR, and not because "improvements outweigh regressions". That suggests that the regressions in such PRs could be recouped by sufficient re-engineering of that change, but I believe they cannot because they reflect imprecision in the MIR inliner's cost model, as opposed to anything about the changes in the PR. The same logic applies to any improvements that are reported in a PR that adds code to the standard library; such improvements reflect that the MIR inliner previously made wrong decisions and now happens (by sheer luck) to in a few cases make better ones. If anyone wants to try to do case-by-case analysis of what changed in the generated code or analyze the MIR inliner's decisions they are welcome to attempt it. I will not be doing so. We already know what the next step is to improve the compile-time overhead of these checks, and that is #122282 as @DianQK indicated. |
Binary size results looking good though, which is an expected result of this change |
No. I think you're making exactly the mistake I tried to warn about above:
In all cases the relevant code is behind an I do not understand exactly why the MIR inliner tends to do inlinings that LLVM doesn't, but I've done a fair bit of analysis in that area before and it does. For some reason, a lot of drop-related symbols appear or disappear from binaries when inlining is tweaked. |
Thanks for the explanations @saethlin , I really appreciate it, and I totally buy the explanation you have provided here. These are really good thoughts. I am wondering if there's any place less transient we could put them, e.g. in some sort of guide/document that would help people doing the rustc-perf triage with the kinds of things they should be considering. @rustbot label: +perf-regression-triaged |
Not just perf triage, we should have a manual for doing perf report analysis in general. |
…=RalfJung Distinguish between library and lang UB in assert_unsafe_precondition As described in rust-lang#121583 (comment), `assert_unsafe_precondition` now explicitly distinguishes between language UB (conditions we explicitly optimize on) and library UB (things we document you shouldn't do, and maybe some library internals assume you don't do). `debug_assert_nounwind` was originally added to avoid the "only at runtime" aspect of `assert_unsafe_precondition`. Since then the difference between the macros has gotten muddied. This totally revamps the situation. Now _all_ preconditions shall be checked with `assert_unsafe_precondition`. If you have a precondition that's only checkable at runtime, do a `const_eval_select` hack, as done in this PR. r? RalfJung
…=RalfJung Distinguish between library and lang UB in assert_unsafe_precondition As described in rust-lang#121583 (comment), `assert_unsafe_precondition` now explicitly distinguishes between language UB (conditions we explicitly optimize on) and library UB (things we document you shouldn't do, and maybe some library internals assume you don't do). `debug_assert_nounwind` was originally added to avoid the "only at runtime" aspect of `assert_unsafe_precondition`. Since then the difference between the macros has gotten muddied. This totally revamps the situation. Now _all_ preconditions shall be checked with `assert_unsafe_precondition`. If you have a precondition that's only checkable at runtime, do a `const_eval_select` hack, as done in this PR. r? RalfJung
I think I stumbled onto why this is causing "chaotic, small, about-same-magnitude changes" by "perturbing the MIR inliner's decisions" in #123174: the newly introduced branch is blocking MIR inlining of |
As described in #121583 (comment),
assert_unsafe_precondition
now explicitly distinguishes between language UB (conditions we explicitly optimize on) and library UB (things we document you shouldn't do, and maybe some library internals assume you don't do).debug_assert_nounwind
was originally added to avoid the "only at runtime" aspect ofassert_unsafe_precondition
. Since then the difference between the macros has gotten muddied. This totally revamps the situation.Now all preconditions shall be checked with
assert_unsafe_precondition
. If you have a precondition that's only checkable at runtime, do aconst_eval_select
hack, as done in this PR.r? RalfJung