-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Fix MaybeUninit codegen using GVN #147827
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
Conversation
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Fix MaybeUninit codegen using GVN
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (e6fd12c): comparison URL. Overall result: ❌✅ regressions and improvements - please read the text belowBenchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @bors rollup=never Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary 0.9%, secondary 0.8%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -3.6%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 475.372s -> 474.527s (-0.18%) |
6d353c3 to
1a410f7
Compare
|
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt Some changes occurred to the CTFE / Miri interpreter cc @rust-lang/miri Some changes occurred to the CTFE machinery Some changes occurred in compiler/rustc_codegen_ssa |
|
rustbot has assigned @petrochenkov. Use |
|
r? scottmcm |
This comment has been minimized.
This comment has been minimized.
1a410f7 to
177e9fc
Compare
If you go in that direction, please make sure #137936 is fixed first. Otherwise such a transformation would expose the issue in Rust, as opposed to being merely limited to a custom MIR (probably?). |
I think once we move transmute to being union semantics we'll have more options here, can we can rewrite those repeats to |
| if let mir::Operand::Constant(const_op) = operand { | ||
| let val = self.eval_mir_constant(&const_op); | ||
| if val.all_bytes_uninit(self.cx.tcx()) { | ||
| return; |
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.
pondering: I wonder if there's any places that are intentionally writing uninit somewhere to tell LLVM that the value doesn't matter. It might be interesting to (outside of debug) write undef to the place for the situations where LLVM doesn't do the bad behaviour of copying a constant for that (such as *p = undef; for *p being Scalar or ScalarPair).
But I don't think that would need to be in this PR.
| } | ||
|
|
||
| // Printing [MaybeUninit<u8>::uninit(); N] or any other aggregate where all fields are uninit | ||
| // becomes very verbose. This special case makes the dump terse and clear. |
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.
👍
tests/codegen-llvm/uninit-consts.rs
Outdated
| pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> { | ||
| const M: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); | ||
| // CHECK: call void @llvm.memcpy.{{.+}}(ptr align 1 %_0, ptr align 1 {{.*}}[[FULLY_UNINIT]]{{.*}}, i{{(32|64)}} 10, i1 false) | ||
| // CHECK: ret void |
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.
fix: this updated test would still pass without the fix from the PR (since there was already a ret void in it), so you need to change the test somehow.
Perhaps something like
| // CHECK: ret void | |
| // returning uninit doesn't need to do anything to the return place at all | |
| // CHECK: start: | |
| // CHECK-NEXT: ret void |
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.
Done
Fix MaybeUninit codegen using GVN This is an alternative to #142837, based on #146355 (comment). The general approach I took here is to aggressively propagate anything that is entirely uninitialized. GVN generally takes the approach of only synthesizing small types, but we need to generate large consts to fix the codegen issue. I also added a special case to MIR dumps for this where now an entirely uninit const is printed as `const <uninit>`, because otherwise we end up with extremely verbose dumps of the new consts. After GVN though, we still end up with a lot of MIR that looks like this: ``` StorageLive(_1); _1 = const <uninit>; _2 = &raw mut _1; ``` Which will break tests/codegen-llvm/maybeuninit-rvo.rs with the naive lowering. I think the ideal fix here is to somehow omit these `_1 = const <uninit>` assignments that come directly after a StorageLive, but I'm not sure how to do that. For now at least, ignoring such assignments (even if they don't come right after a StorageLive) in codegen seems to work. Note that since GVN is based on synthesizing a `ConstValue` which has a defined layout, this scenario still gets deoptimized by LLVM. ```rust #![feature(rustc_attrs)] #![crate_type = "lib"] use std::mem::MaybeUninit; #[unsafe(no_mangle)] pub fn oof() -> [[MaybeUninit<u8>; 8]; 8] { #[rustc_no_mir_inline] pub fn inner<T: Copy>() -> [[MaybeUninit<T>; 8]; 8] { [[MaybeUninit::uninit(); 8]; 8] } inner() } ``` This case can be handled correctly if enough inlining has happened, or it could be handled by post-mono GVN. Synthesizing `UnevaluatedConst` or some other special kind of const seems dubious.
This comment has been minimized.
This comment has been minimized.
|
💔 Test failed - checks-actions |
…cottmcm Fix MaybeUninit codegen using GVN This is an alternative to rust-lang#142837, based on rust-lang#146355 (comment). The general approach I took here is to aggressively propagate anything that is entirely uninitialized. GVN generally takes the approach of only synthesizing small types, but we need to generate large consts to fix the codegen issue. I also added a special case to MIR dumps for this where now an entirely uninit const is printed as `const <uninit>`, because otherwise we end up with extremely verbose dumps of the new consts. After GVN though, we still end up with a lot of MIR that looks like this: ``` StorageLive(_1); _1 = const <uninit>; _2 = &raw mut _1; ``` Which will break tests/codegen-llvm/maybeuninit-rvo.rs with the naive lowering. I think the ideal fix here is to somehow omit these `_1 = const <uninit>` assignments that come directly after a StorageLive, but I'm not sure how to do that. For now at least, ignoring such assignments (even if they don't come right after a StorageLive) in codegen seems to work. Note that since GVN is based on synthesizing a `ConstValue` which has a defined layout, this scenario still gets deoptimized by LLVM. ```rust #![feature(rustc_attrs)] #![crate_type = "lib"] use std::mem::MaybeUninit; #[unsafe(no_mangle)] pub fn oof() -> [[MaybeUninit<u8>; 8]; 8] { #[rustc_no_mir_inline] pub fn inner<T: Copy>() -> [[MaybeUninit<T>; 8]; 8] { [[MaybeUninit::uninit(); 8]; 8] } inner() } ``` This case can be handled correctly if enough inlining has happened, or it could be handled by post-mono GVN. Synthesizing `UnevaluatedConst` or some other special kind of const seems dubious.
2d836b9 to
1a4852c
Compare
|
@bors r=scottmcm |
|
☀️ Test successful - checks-actions |
What is this?This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.Comparing 122cbd0 (parent) -> e9acbd9 (this PR) Test differencesShow 8 test diffsStage 1
Stage 2
Additionally, 4 doctest diffs were found. These are ignored, as they are noisy. Job group index
Test dashboardRun cargo run --manifest-path src/ci/citool/Cargo.toml -- \
test-dashboard e9acbd99d384280874129fb7fa0da9faeae0d051 --output-dir test-dashboardAnd then open Job duration changes
How to interpret the job duration changes?Job durations can vary a lot, based on the actual runner instance |
|
Finished benchmarking commit (e9acbd9): comparison URL. Overall result: ❌✅ regressions and improvements - please read the text belowOur benchmarks found a performance regression caused by this PR. Next Steps:
@rustbot label: +perf-regression Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -2.1%, secondary 2.6%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -4.1%, secondary -1.9%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary 0.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 471.242s -> 472.15s (0.19%) |
|
Tiny regressions on secondary benchmarks, but a large (and real) win on the @rustbot label: +perf-regression-triaged |
This is an alternative to #142837, based on #146355 (comment).
The general approach I took here is to aggressively propagate anything that is entirely uninitialized. GVN generally takes the approach of only synthesizing small types, but we need to generate large consts to fix the codegen issue.
I also added a special case to MIR dumps for this where now an entirely uninit const is printed as
const <uninit>, because otherwise we end up with extremely verbose dumps of the new consts.After GVN though, we still end up with a lot of MIR that looks like this:
Which will break tests/codegen-llvm/maybeuninit-rvo.rs with the naive lowering. I think the ideal fix here is to somehow omit these
_1 = const <uninit>assignments that come directly after a StorageLive, but I'm not sure how to do that. For now at least, ignoring such assignments (even if they don't come right after a StorageLive) in codegen seems to work.Note that since GVN is based on synthesizing a
ConstValuewhich has a defined layout, this scenario still gets deoptimized by LLVM.This case can be handled correctly if enough inlining has happened, or it could be handled by post-mono GVN. Synthesizing
UnevaluatedConstor some other special kind of const seems dubious.