diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 4249914346cd..62711ad92372 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -696,6 +696,28 @@ impl<'tcx> TerminatorKind<'tcx> { _ => None, } } + + /// Returns true if the terminator can write to memory. + pub fn can_write_to_memory(&self) -> bool { + match self { + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::Assert { .. } + | TerminatorKind::CoroutineDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Unreachable => false, + TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::TailCall { .. } + // Yield writes to the resume_arg place. + | TerminatorKind::Yield { .. } + | TerminatorKind::InlineAsm { .. } => true, + } + } } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 0d97571fa818..bdf53f45feac 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1926,13 +1926,8 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> { self.assign(local, opaque); } } - // Function calls and ASM may invalidate (nested) derefs. We must handle them carefully. - // Currently, only preserving derefs for trivial terminators like SwitchInt and Goto. - let safe_to_preserve_derefs = matches!( - terminator.kind, - TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. } - ); - if !safe_to_preserve_derefs { + // Terminators that can write to memory may invalidate (nested) derefs. + if terminator.kind.can_write_to_memory() { self.invalidate_derefs(); } self.super_terminator(terminator, location); diff --git a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.rs b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.rs index e82e7f2236f1..7b742b956ae5 100644 --- a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.rs +++ b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ compile-flags: -O #![crate_type = "lib"] @@ -6,6 +5,10 @@ // EMIT_MIR two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff // EMIT_MIR two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir pub fn two_unwrap_unchecked(v: &Option) -> i32 { + // CHECK-LABEL: fn two_unwrap_unchecked( + // CHECK: [[DEREF_V:_.*]] = copy (*_1); + // CHECK: [[V1V2:_.*]] = copy (([[DEREF_V]] as Some).0: i32); + // CHECK: _0 = Add(copy [[V1V2]], copy [[V1V2]]); let v1 = unsafe { v.unwrap_unchecked() }; let v2 = unsafe { v.unwrap_unchecked() }; v1 + v2 diff --git a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff index 3b96b4ebc791..5b063e6762e0 100644 --- a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff +++ b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.GVN.diff @@ -41,12 +41,15 @@ bb0: { - StorageLive(_2); +- StorageLive(_3); ++ nop; + nop; - StorageLive(_3); _3 = copy (*_1); - StorageLive(_8); +- StorageLive(_8); ++ nop; _8 = discriminant(_3); - switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1]; +- switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1]; ++ switchInt(copy _8) -> [0: bb2, 1: bb3, otherwise: bb1]; } bb1: { @@ -58,16 +61,21 @@ } bb3: { - _2 = move ((_3 as Some).0: i32); - StorageDead(_8); - StorageDead(_3); -- StorageLive(_4); +- _2 = move ((_3 as Some).0: i32); +- StorageDead(_8); +- StorageDead(_3); ++ _2 = copy ((_3 as Some).0: i32); + nop; ++ nop; + StorageLive(_4); StorageLive(_5); - _5 = copy (*_1); +- _5 = copy (*_1); ++ _5 = copy _3; StorageLive(_9); - _9 = discriminant(_5); - switchInt(move _9) -> [0: bb4, 1: bb5, otherwise: bb1]; +- _9 = discriminant(_5); +- switchInt(move _9) -> [0: bb4, 1: bb5, otherwise: bb1]; ++ _9 = copy _8; ++ switchInt(copy _8) -> [0: bb4, 1: bb5, otherwise: bb1]; } bb4: { @@ -75,20 +83,21 @@ } bb5: { - _4 = move ((_5 as Some).0: i32); +- _4 = move ((_5 as Some).0: i32); ++ _4 = copy _2; StorageDead(_9); StorageDead(_5); StorageLive(_6); _6 = copy _2; StorageLive(_7); - _7 = copy _4; +- _7 = copy _4; - _0 = Add(move _6, move _7); -+ _0 = Add(copy _2, copy _4); ++ _7 = copy _2; ++ _0 = Add(copy _2, copy _2); StorageDead(_7); StorageDead(_6); -- StorageDead(_4); + StorageDead(_4); - StorageDead(_2); -+ nop; + nop; return; } diff --git a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir index 67910b4f1784..b2b7f88d8534 100644 --- a/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/two_unwrap_unchecked.two_unwrap_unchecked.PreCodegen.after.mir @@ -5,15 +5,12 @@ fn two_unwrap_unchecked(_1: &Option) -> i32 { let mut _0: i32; let mut _2: std::option::Option; let _4: i32; - let mut _5: std::option::Option; scope 1 { debug v1 => _4; - let _7: i32; scope 2 { - debug v2 => _7; + debug v2 => _4; } scope 8 (inlined #[track_caller] Option::::unwrap_unchecked) { - let mut _6: isize; scope 9 { } scope 10 (inlined #[track_caller] unreachable_unchecked) { @@ -37,33 +34,18 @@ fn two_unwrap_unchecked(_1: &Option) -> i32 { } bb0: { - StorageLive(_2); _2 = copy (*_1); - StorageLive(_3); _3 = discriminant(_2); - switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb3]; + switchInt(copy _3) -> [0: bb2, 1: bb1, otherwise: bb2]; } bb1: { - _4 = move ((_2 as Some).0: i32); - StorageDead(_3); - StorageDead(_2); - StorageLive(_5); - _5 = copy (*_1); - StorageLive(_6); - _6 = discriminant(_5); - switchInt(move _6) -> [0: bb3, 1: bb2, otherwise: bb3]; - } - - bb2: { - _7 = move ((_5 as Some).0: i32); - StorageDead(_6); - StorageDead(_5); - _0 = Add(copy _4, copy _7); + _4 = copy ((_2 as Some).0: i32); + _0 = Add(copy _4, copy _4); return; } - bb3: { + bb2: { unreachable; } }