You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unreachable code optimization failure when matching on Rust enum #77812
#[derive(Copy,Clone,Eq,PartialEq)]pubenumVariant{Zero,One,Two,}#[inline]fnunreachable(){println!("Impossible");}extern{fnexf1();fnexf2();}#[no_mangle]pubstaticmutGLOBAL:Variant = Variant::Zero;pubunsafefntest(){let g = GLOBAL;if g != Variant::Zero{match g {Variant::One => exf1(),Variant::Two => exf2(),Variant::Zero => unreachable(),}}}
the unreachable() branch is not removed. Adding any #[repr(n)] but not #[repr(C)] to Variant or making reachable branches call only one of external functions allows the optimization.
There's no such issue with a similar C example using clang so it's likely something on Rust side.
That clearly has sufficient information to know that bb4 is unreachable, but it's not taking advantage of it.
Curiosity: why is rustc insisting on zexting to i64 for the discriminant comparison? That's something rust is doing; it's there even in -O0https://rust.godbolt.org/z/a1nxzx
It gets even weirder if you put Variant::Zero as the first pattern in the match, notice in particular the two consecutive jehttps://rust.godbolt.org/z/3K933z
defaults to the smallest type that will fit instead of isize. (I think this would be backwards compatible, you can still as-cast to any integer type, I'm not sure if the underlying discriminant type is exposed anywhere.)
FYI, #74215 has some discussion about the trade-offs of changing DiscriminantKind::Discriminant to be the smallest possible integer type.
I've also found that a not insignificant amount of hashing in the compiler is being done on discriminant values, so there is a potential compile time improvement in doing this too, though I don't think that should be a primary motivation for the change.
Hmm, there's nothing that would force that type and the HIR->MIR desugaring to use the same internal construct value, though, right? Like we could change the MIR Rvalue::Discriminant to return the same width as it's actually stored, and the discriminant_value intrinsic to [sz]ext to whatever DiscriminantKind wants?
That way LLVM would just see a switch on the loaded value, not an extended one. (So this might still happen if the if g != Variant::Zero { check were replaced with mem::Discriminant one, but that would be a different problem.)
Activity
scottmcm commentedon Oct 11, 2020
This is weird; LLVM has plenty enough information to be able to do this from what's emitted.
The IR it currently produces: https://rust.godbolt.org/z/GWacEc
That clearly has sufficient information to know that
bb4is unreachable, but it's not taking advantage of it.Curiosity: why is rustc insisting on
zexting toi64for the discriminant comparison? That's something rust is doing; it's there even in-O0https://rust.godbolt.org/z/a1nxzxSkiFire13 commentedon Oct 11, 2020
It gets even weirder if you put
Variant::Zeroas the first pattern in the match, notice in particular the two consecutivejehttps://rust.godbolt.org/z/3K933zerikdesjardins commentedon Oct 11, 2020
This looks like the same problem as #73031. LLVM can't see through the
zextin some cases.rustc extends to
i64because the default discriminant type isisize, even though the memory representation is usually smaller.Although this can be fixed in LLVM, it may be worthwhile to try making the discr type the same as the memory repr, i.e. do this
rust/compiler/rustc_middle/src/ty/layout.rs
Lines 74 to 78 in 8cc82ee
rust/compiler/rustc_middle/src/ty/mod.rs
Lines 2290 to 2292 in 8cc82ee
isize. (I think this would be backwards compatible, you can stillas-cast to any integer type, I'm not sure if the underlying discriminant type is exposed anywhere.)scottmcm commentedon Oct 12, 2020
So is the fix here to codegen it using
<T as DiscriminantKind>::Discriminantinstead ofi64?erikdesjardins commentedon Oct 12, 2020
<T as DiscriminantKind>::Discriminantisi64, which is the problem: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=3f5f5c4a34d07152653c173a7e3407e4tgnottingham commentedon Oct 12, 2020
FYI, #74215 has some discussion about the trade-offs of changing
DiscriminantKind::Discriminantto be the smallest possible integer type.I've also found that a not insignificant amount of hashing in the compiler is being done on discriminant values, so there is a potential compile time improvement in doing this too, though I don't think that should be a primary motivation for the change.
scottmcm commentedon Oct 12, 2020
Hmm, there's nothing that would force that type and the HIR->MIR desugaring to use the same internal construct value, though, right? Like we could change the MIR
Rvalue::Discriminantto return the same width as it's actually stored, and thediscriminant_valueintrinsic to[sz]extto whateverDiscriminantKindwants?That way LLVM would just see a switch on the loaded value, not an extended one. (So this might still happen if the
if g != Variant::Zero {check were replaced withmem::Discriminantone, but that would be a different problem.)nikic commentedon Dec 12, 2020
The case is eliminated with LLVM 12 by IPSCCP, probably as a result of https://reviews.llvm.org/D84270: https://llvm.godbolt.org/z/nbfnzc
I would have expected CVP to handle this already before, but it doesn't due to https://github.com/llvm/llvm-project/blob/7beee561e23db56c7e24462a9870a3ba58a9b2b3/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp#L333-L336.
nikic commentedon Dec 12, 2020
The CVP issue is addressed by llvm/llvm-project@afbb6d9. Assigning this to myself to re-check this after the LLVM 12 upgrade.
MSxDOS commentedon Mar 5, 2021
AFAICT, the issue has been successfully obliterated by #81451 🎉
Rollup merge of rust-lang#82874 - erikdesjardins:cgtests, r=nagisa