diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bdf53f45feacb..247c949b70ec1 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -136,8 +136,8 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena); for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) { - let opaque = state.new_opaque(body.local_decls[local].ty); - state.assign(local, opaque); + let argument = state.insert_parameter(body.local_decls[local].ty); + state.assign(local, argument); } let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec(); @@ -204,8 +204,9 @@ enum AddressBase { enum Value<'a, 'tcx> { // Root values. /// Used to represent values we know nothing about. - /// The `usize` is a counter incremented by `new_opaque`. Opaque(VnOpaque), + /// Used to represent values we know nothing except that it is a function parameter. + Parameter(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, @@ -290,7 +291,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> { let value = value(VnOpaque); debug_assert!(match value { - Value::Opaque(_) | Value::Address { .. } => true, + Value::Opaque(_) | Value::Parameter(_) | Value::Address { .. } => true, Value::Constant { disambiguator, .. } => disambiguator.is_some(), _ => false, }); @@ -308,7 +309,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> { #[allow(rustc::pass_by_value)] // closures take `&VnIndex` fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) { debug_assert!(match value { - Value::Opaque(_) | Value::Address { .. } => false, + Value::Opaque(_) | Value::Parameter(_) | Value::Address { .. } => false, Value::Constant { disambiguator, .. } => disambiguator.is_none(), _ => true, }); @@ -420,6 +421,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { self.ecx.typing_env() } + fn insert_unique( + &mut self, + ty: Ty<'tcx>, + evaluated: bool, + value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>, + ) -> VnIndex { + let index = self.values.insert_unique(ty, value); + let _index = self.evaluated.push(if evaluated { Some(None) } else { None }); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index + } + #[instrument(level = "trace", skip(self), ret)] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex { let (index, new) = self.values.insert(ty, value); @@ -437,12 +452,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let index = self.values.insert_unique(ty, Value::Opaque); - let _index = self.evaluated.push(Some(None)); - debug_assert_eq!(index, _index); - let _index = self.rev_locals.push(SmallVec::new()); - debug_assert_eq!(index, _index); - index + self.insert_unique(ty, true, Value::Opaque) } /// Create a new `Value::Address` distinct from all the others. @@ -470,42 +480,30 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(())); let projection = self.arena.try_alloc_from_iter(projection).ok()?; - let index = self.values.insert_unique(ty, |provenance| Value::Address { + let index = self.insert_unique(ty, false, |provenance| Value::Address { base, projection, kind, provenance, }); - let _index = self.evaluated.push(None); - debug_assert_eq!(index, _index); - let _index = self.rev_locals.push(SmallVec::new()); - debug_assert_eq!(index, _index); Some(index) } #[instrument(level = "trace", skip(self), ret)] fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let (index, new) = if value.is_deterministic() { + if value.is_deterministic() { // The constant is deterministic, no need to disambiguate. let constant = Value::Constant { value, disambiguator: None }; - self.values.insert(value.ty(), constant) + self.insert(value.ty(), constant) } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant { + self.insert_unique(value.ty(), false, |disambiguator| Value::Constant { value, disambiguator: Some(disambiguator), - }); - (index, true) - }; - if new { - let _index = self.evaluated.push(None); - debug_assert_eq!(index, _index); - let _index = self.rev_locals.push(SmallVec::new()); - debug_assert_eq!(index, _index); + }) } - index } #[inline] @@ -540,13 +538,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { self.insert(ty, Value::Constant { value, disambiguator: None }) } + fn insert_parameter(&mut self, ty: Ty<'tcx>) -> VnIndex { + self.insert_unique(ty, true, Value::Parameter) + } + fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex { self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values))) } - fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex { + fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex, always_valid: bool) -> VnIndex { let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref)); - self.derefs.push(value); + // If the borrow lifetime is the whole body, we don't need to invalidate it. + if !always_valid { + self.derefs.push(value); + } value } @@ -569,7 +574,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let op = match self.get(value) { _ if ty.is_zst() => ImmTy::uninit(ty).into(), - Opaque(_) => return None, + Opaque(_) | Parameter(_) => return None, // Do not bother evaluating repeat expressions. This would uselessly consume memory. Repeat(..) => return None, @@ -817,15 +822,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { if let Some(Mutability::Not) = place_ty.ty.ref_mutability() && projection_ty.ty.is_freeze(self.tcx, self.typing_env()) { - if let Value::Address { base, projection, .. } = self.get(value) - && let Some(value) = self.dereference_address(base, projection) - { + if let Value::Address { base, projection, .. } = self.get(value) { + // Bail out if the address cannot be dereferenced. + let value = self.dereference_address(base, projection)?; return Some((projection_ty, value)); } - // An immutable borrow `_x` always points to the same value for the - // lifetime of the borrow, so we can merge all instances of `*_x`. - return Some((projection_ty, self.insert_deref(projection_ty.ty, value))); + if let Value::Parameter(_) | Value::Constant { .. } = self.get(value) { + // An immutable borrow `_x` that is an parameter or a constant always points to the same value for the + // lifetime of the borrow, so we can merge all instances of `*_x`. + // The lifetime here is unrelated to storage markers, but rather does not violate the Stacked Borrows rules. + return Some(( + projection_ty, + self.insert_deref(projection_ty.ty, value, true), + )); + } + + // FIXME: We could introduce new deref that the immutable borrow is not valid in the whole body, + // but we must invalidate it when out of the immutable borrow lifetime. + // Known related issues: + // - https://github.com/rust-lang/rust/issues/130853 (Fixed by invalidating) + // - https://github.com/rust-lang/rust/issues/132353 (Fixed by invalidating) + // - https://github.com/rust-lang/rust/issues/141038 (Not fixed well) + // - https://github.com/rust-lang/rust/issues/141251 (Fixed by #144477) + // - https://github.com/rust-lang/rust/issues/141313 + // - https://github.com/rust-lang/rust/pull/147607 (Fixed by invalidating) + if self.tcx.sess.opts.unstable_opts.unsound_mir_opts { + return Some(( + projection_ty, + self.insert_deref(projection_ty.ty, value, false), + )); + } + return None; } else { return None; } diff --git a/tests/mir-opt/const_prop/ref_deref_project.rs b/tests/mir-opt/const_prop/ref_deref_project.rs index 5a48a887f93d7..9363a6408c428 100644 --- a/tests/mir-opt/const_prop/ref_deref_project.rs +++ b/tests/mir-opt/const_prop/ref_deref_project.rs @@ -1,6 +1,6 @@ -// This does not currently propagate (#67862) //@ test-mir-pass: GVN +// Checks that GVN knows a is 5. // EMIT_MIR ref_deref_project.main.GVN.diff fn main() { // CHECK-LABEL: fn main( diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff index d6e81debccd81..0798b30392956 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff @@ -40,7 +40,7 @@ bb1: { - _1 = copy (*_2)[_6]; -+ _1 = const 2_u32; ++ _1 = copy (*_2)[1 of 2]; StorageDead(_6); StorageDead(_4); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff index 6713e531892ae..c0b3d4d321902 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff @@ -40,7 +40,7 @@ bb1: { - _1 = copy (*_2)[_6]; -+ _1 = const 2_u32; ++ _1 = copy (*_2)[1 of 2]; StorageDead(_6); StorageDead(_4); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff index d6e81debccd81..0798b30392956 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff @@ -40,7 +40,7 @@ bb1: { - _1 = copy (*_2)[_6]; -+ _1 = const 2_u32; ++ _1 = copy (*_2)[1 of 2]; StorageDead(_6); StorageDead(_4); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff index 6713e531892ae..c0b3d4d321902 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff @@ -40,7 +40,7 @@ bb1: { - _1 = copy (*_2)[_6]; -+ _1 = const 2_u32; ++ _1 = copy (*_2)[1 of 2]; StorageDead(_6); StorageDead(_4); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/slice_len.rs b/tests/mir-opt/const_prop/slice_len.rs index ebd3c9e792dca..59bb3b01329d2 100644 --- a/tests/mir-opt/const_prop/slice_len.rs +++ b/tests/mir-opt/const_prop/slice_len.rs @@ -3,12 +3,13 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH +// FIXME: GVN should be able to know `a` is 2; // EMIT_MIR slice_len.main.GVN.diff fn main() { // CHECK-LABEL: fn main( // CHECK: debug a => [[a:_.*]]; // CHECK: [[slice:_.*]] = copy {{.*}} as &[u32] (PointerCoercion(Unsize, AsCast)); // CHECK: assert(const true, - // CHECK: [[a]] = const 2_u32; + // CHECK: [[a]] = copy (*[[slice]])[1 of 2]; let a = (&[1u32, 2, 3] as &[u32])[1]; } diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff index 5d7343a15bbea..57771c1569e35 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff @@ -72,8 +72,7 @@ bb0: { StorageLive(_1); -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); - _4 = (); @@ -134,12 +133,10 @@ StorageDead(_4); _2 = &_3; _1 = &(*_2); -- StorageDead(_2); + StorageDead(_2); - StorageLive(_5); -- _10 = copy (*_1); -+ nop; + nop; -+ _10 = copy (*_2); + _10 = copy (*_1); _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff index 82a14a8b6ec99..9a354fc005ed5 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff @@ -37,8 +37,7 @@ bb0: { StorageLive(_1); -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); - _4 = (); @@ -51,12 +50,10 @@ StorageDead(_4); _2 = &_3; _1 = &(*_2); -- StorageDead(_2); + StorageDead(_2); - StorageLive(_5); -- _10 = copy (*_1); -+ nop; + nop; -+ _10 = copy (*_2); + _10 = copy (*_1); _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index ba5fe287b8e1c..bbf7b0b731d58 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -72,8 +72,7 @@ bb0: { StorageLive(_1); -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); - _4 = (); @@ -134,12 +133,10 @@ StorageDead(_4); _2 = &_3; _1 = &(*_2); -- StorageDead(_2); + StorageDead(_2); - StorageLive(_5); -- _10 = copy (*_1); -+ nop; + nop; -+ _10 = copy (*_2); + _10 = copy (*_1); _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff index 82a14a8b6ec99..9a354fc005ed5 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff @@ -37,8 +37,7 @@ bb0: { StorageLive(_1); -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); - _4 = (); @@ -51,12 +50,10 @@ StorageDead(_4); _2 = &_3; _1 = &(*_2); -- StorageDead(_2); + StorageDead(_2); - StorageLive(_5); -- _10 = copy (*_1); -+ nop; + nop; -+ _10 = copy (*_2); + _10 = copy (*_1); _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); diff --git a/tests/mir-opt/gvn.dereferences.GVN.panic-abort.diff b/tests/mir-opt/gvn.dereferences.GVN.panic-abort.diff index ecd7bdc433cd9..ade78830a5fc4 100644 --- a/tests/mir-opt/gvn.dereferences.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.dereferences.GVN.panic-abort.diff @@ -152,18 +152,23 @@ StorageDead(_28); StorageDead(_27); StorageLive(_29); - StorageLive(_30); +- StorageLive(_30); ++ nop; _30 = copy ((*_3).0: u32); - _29 = opaque::(move _30) -> [return: bb12, unwind unreachable]; +- _29 = opaque::(move _30) -> [return: bb12, unwind unreachable]; ++ _29 = opaque::(copy _30) -> [return: bb12, unwind unreachable]; } bb12: { - StorageDead(_30); +- StorageDead(_30); ++ nop; StorageDead(_29); StorageLive(_31); StorageLive(_32); - _32 = copy ((*_3).0: u32); - _31 = opaque::(move _32) -> [return: bb13, unwind unreachable]; +- _32 = copy ((*_3).0: u32); +- _31 = opaque::(move _32) -> [return: bb13, unwind unreachable]; ++ _32 = copy _30; ++ _31 = opaque::(copy _30) -> [return: bb13, unwind unreachable]; } bb13: { diff --git a/tests/mir-opt/gvn.dereferences.GVN.panic-unwind.diff b/tests/mir-opt/gvn.dereferences.GVN.panic-unwind.diff index bbca6bc3c754d..cacbd283f9c2f 100644 --- a/tests/mir-opt/gvn.dereferences.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.dereferences.GVN.panic-unwind.diff @@ -152,18 +152,23 @@ StorageDead(_28); StorageDead(_27); StorageLive(_29); - StorageLive(_30); +- StorageLive(_30); ++ nop; _30 = copy ((*_3).0: u32); - _29 = opaque::(move _30) -> [return: bb12, unwind continue]; +- _29 = opaque::(move _30) -> [return: bb12, unwind continue]; ++ _29 = opaque::(copy _30) -> [return: bb12, unwind continue]; } bb12: { - StorageDead(_30); +- StorageDead(_30); ++ nop; StorageDead(_29); StorageLive(_31); StorageLive(_32); - _32 = copy ((*_3).0: u32); - _31 = opaque::(move _32) -> [return: bb13, unwind continue]; +- _32 = copy ((*_3).0: u32); +- _31 = opaque::(move _32) -> [return: bb13, unwind continue]; ++ _32 = copy _30; ++ _31 = opaque::(copy _30) -> [return: bb13, unwind continue]; } bb13: { diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff index f3f631956374d..7f99b83d937dd 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _9: {closure@$DIR/gvn.rs:615:19: 615:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _13: {closure@$DIR/gvn.rs:615:19: 615:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:617:19: 617:21}; + let _7: {closure@$DIR/gvn.rs:615:19: 615:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:617:19: 617:21}; +- _7 = {closure@$DIR/gvn.rs:615:19: 615:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; + nop; StorageLive(_9); - _9 = copy _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = copy _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff index 029e736a97952..06dd0502f30f2 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _9: {closure@$DIR/gvn.rs:615:19: 615:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _13: {closure@$DIR/gvn.rs:615:19: 615:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:617:19: 617:21}; + let _7: {closure@$DIR/gvn.rs:615:19: 615:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:617:19: 617:21}; +- _7 = {closure@$DIR/gvn.rs:615:19: 615:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; + nop; StorageLive(_9); - _9 = copy _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = copy _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:615:19: 615:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 3c3241fefe22e..2ee0fb7a790d8 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -521,11 +521,9 @@ fn dereferences(t: &mut u32, u: &impl Copy, s: &S) { opaque(*u); opaque(*u); - // `*s` is not Copy, but `(*s).0` is, but we still cannot reuse. // CHECK: [[st10:_.*]] = copy ((*_3).0: u32); - // CHECK: opaque::(move [[st10]]) - // CHECK: [[st11:_.*]] = copy ((*_3).0: u32); - // CHECK: opaque::(move [[st11]]) + // CHECK: opaque::(copy [[st10]]) + // CHECK: opaque::(copy [[st10]]) opaque(s.0); opaque(s.0); } diff --git a/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff b/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff index eed8cb7d62e70..a44968a43e5dc 100644 --- a/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff +++ b/tests/mir-opt/gvn_copy_aggregate.all_copy_2.GVN.diff @@ -47,7 +47,7 @@ StorageLive(_7); _7 = copy _4; - _0 = AllCopy { a: move _5, b: move _6, c: move _7 }; -+ _0 = copy (*_8); ++ _0 = AllCopy { a: copy _2, b: copy _3, c: copy _4 }; StorageDead(_7); StorageDead(_6); StorageDead(_5); diff --git a/tests/mir-opt/gvn_copy_aggregate.deref_nonssa.GVN.diff b/tests/mir-opt/gvn_copy_aggregate.deref_nonssa.GVN.diff new file mode 100644 index 0000000000000..d9707e40c0bda --- /dev/null +++ b/tests/mir-opt/gvn_copy_aggregate.deref_nonssa.GVN.diff @@ -0,0 +1,48 @@ +- // MIR for `deref_nonssa` before GVN ++ // MIR for `deref_nonssa` after GVN + + fn deref_nonssa() -> Single { + let mut _0: Single; + let mut _1: Single; + let mut _4: Single; + let mut _5: u8; + scope 1 { + debug a => _1; + let _2: &Single; + scope 2 { + debug b => _2; + let _3: u8; + scope 3 { + debug c => _3; + } + } + } + + bb0: { + StorageLive(_1); +- _1 = Single(const 0_u8); ++ _1 = const Single(0_u8); + StorageLive(_2); + _2 = &_1; +- StorageLive(_3); ++ nop; + _3 = copy ((*_2).0: u8); + StorageLive(_4); +- _4 = Single(const 1_u8); +- _1 = move _4; ++ _4 = const Single(1_u8); ++ _1 = const Single(1_u8); + StorageDead(_4); + StorageLive(_5); + _5 = copy _3; +- _0 = Single(move _5); ++ _0 = Single(copy _3); + StorageDead(_5); +- StorageDead(_3); ++ nop; + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/gvn_copy_aggregate.rs b/tests/mir-opt/gvn_copy_aggregate.rs index c9473025a15f2..49fb5c7ff2bc6 100644 --- a/tests/mir-opt/gvn_copy_aggregate.rs +++ b/tests/mir-opt/gvn_copy_aggregate.rs @@ -24,13 +24,13 @@ fn all_copy(v: &AllCopy) -> AllCopy { AllCopy { a, b, c } } +// FIXME: This can be a dereference of the dereference of `v`. +// This requires that we know the dereference is in its valid liveness. // EMIT_MIR gvn_copy_aggregate.all_copy_2.GVN.diff fn all_copy_2(v: &&AllCopy) -> AllCopy { // CHECK-LABEL: fn all_copy_2( // CHECK: bb0: { - // CHECK-NOT: = AllCopy { {{.*}} }; - // CHECK: [[V1:_.*]] = copy (*_1); - // CHECK: _0 = copy (*[[V1]]); + // CHECK: _0 = AllCopy { {{.*}} }; let a = v.a; let b = v.b; let c = v.c; @@ -259,3 +259,19 @@ fn remove_storage_dead_from_index(f: fn() -> usize, v: [SameType; 5]) -> SameTyp } } } + +pub struct Single(u8); + +// EMIT_MIR gvn_copy_aggregate.deref_nonssa.GVN.diff +fn deref_nonssa() -> Single { + // CHECK-LABEL: fn deref_nonssa( + // CHECK: debug c => [[C:_.*]]; + // CHECK-NOT: _0 = copy (*_{{.*}}); + // CHECK: _0 = Single(copy [[C]]); + let mut a = Single(0); + let b = &a; + let c = (*b).0; + a = Single(1); + // GVN shouldn't replace `Single(c)` with `*b`. + Single(c) +} diff --git a/tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff b/tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff index 92e5ccabedf9e..5eaa78b893985 100644 --- a/tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff +++ b/tests/mir-opt/gvn_loop.loop_deref_mut.GVN.diff @@ -34,8 +34,7 @@ bb0: { StorageLive(_2); -- StorageLive(_3); -+ nop; + StorageLive(_3); StorageLive(_4); _4 = &(*_1); _3 = get::(move _4) -> [return: bb1, unwind unreachable]; @@ -44,12 +43,10 @@ bb1: { _2 = &(*_3); StorageDead(_4); -- StorageDead(_3); -+ nop; + StorageDead(_3); StorageLive(_5); _5 = const false; -- _8 = discriminant((*_2)); -+ _8 = discriminant((*_3)); + _8 = discriminant((*_2)); switchInt(move _8) -> [0: bb3, otherwise: bb2]; } @@ -59,9 +56,8 @@ bb3: { - StorageLive(_7); -- _7 = copy (((*_2) as V0).0: i32); + nop; -+ _7 = copy (((*_3) as V0).0: i32); + _7 = copy (((*_2) as V0).0: i32); StorageLive(_9); goto -> bb4; } diff --git a/tests/mir-opt/gvn_loop.rs b/tests/mir-opt/gvn_loop.rs index 6e9df55a968dc..4fa18adfcd42d 100644 --- a/tests/mir-opt/gvn_loop.rs +++ b/tests/mir-opt/gvn_loop.rs @@ -14,7 +14,8 @@ pub enum Value { fn loop_deref_mut(val: &mut Value) -> Value { // CHECK-LABEL: fn loop_deref_mut( // CHECK: [[VAL_REF:_.*]] = get::( - // CHECK: [[V:_.*]] = copy (((*[[VAL_REF]]) as V0).0: i32); + // CHECK: [[VAL_REF_2:_.*]] = &(*[[VAL_REF]]); + // CHECK: [[V:_.*]] = copy (((*[[VAL_REF_2]]) as V0).0: i32); // CEHCK-NOT: copy (*[[VAL_REF]]); // CHECK: [[RET:_*]] = Value::V0(copy [[V]]); // CEHCK-NOT: copy (*[[VAL_REF]]); diff --git a/tests/mir-opt/gvn_overlapping.copy_overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.copy_overlapping.GVN.diff new file mode 100644 index 0000000000000..7b7537bc9b40e --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.copy_overlapping.GVN.diff @@ -0,0 +1,18 @@ +- // MIR for `copy_overlapping` before GVN ++ // MIR for `copy_overlapping` after GVN + + fn copy_overlapping() -> () { + let mut _0: (); + let mut _1: Adt; + let mut _2: u32; + let mut _3: &Adt; + + bb0: { + ((_1 as variant#1).0: u32) = const 0_u32; + _3 = &_1; + _2 = copy (((*_3) as variant#1).0: u32); + _1 = Adt::Some(copy _2); + return; + } + } + diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs index f148a84356125..c6db2e87d81e6 100644 --- a/tests/mir-opt/gvn_overlapping.rs +++ b/tests/mir-opt/gvn_overlapping.rs @@ -5,7 +5,7 @@ use std::intrinsics::mir::*; // EMIT_MIR gvn_overlapping.overlapping.GVN.diff -/// Check that we do not create overlapping assignments. +// Check that we do not create overlapping assignments. #[custom_mir(dialect = "runtime")] fn overlapping(_17: Adt) { // CHECK-LABEL: fn overlapping( @@ -25,14 +25,39 @@ fn overlapping(_17: Adt) { } } +// EMIT_MIR gvn_overlapping.stable_projection_nonoverlapping.GVN.diff +// FIXME: We allow dereferences in the RHS if the LHS is a stable projection. +#[custom_mir(dialect = "runtime")] +fn stable_projection_nonoverlapping(_1: (Adt,)) { + // CHECK-LABEL: fn stable_projection_nonoverlapping( + // CHECK: let mut _2: *mut Adt; + // CHECK: let mut _4: &Adt; + // CHECK: (_5.0: Adt) = Adt::Some(copy {{.*}}); + mir! { + let _2: *mut Adt; + let _3: u32; + let _4: &Adt; + let _5: (Adt, ); + { + _2 = core::ptr::addr_of_mut!(_1.0); + _4 = &(*_2); + _3 = Field(Variant((*_4), 1), 0); + _5.0 = Adt::Some(_3); + Return() + } + } +} + // EMIT_MIR gvn_overlapping.stable_projection.GVN.diff -/// Check that we allow dereferences in the RHS if the LHS is a stable projection. +// This introduces copy overlapping if dereferencing `_2` or `_4`. #[custom_mir(dialect = "runtime")] fn stable_projection(_1: (Adt,)) { // CHECK-LABEL: fn stable_projection( // CHECK: let mut _2: *mut Adt; // CHECK: let mut _4: &Adt; - // CHECK: (_1.0: Adt) = copy (*_4); + // CHECK-NOT: (_1.0: Adt) = copy (*_2); + // CHECK: (_1.0: Adt) = Adt::Some(copy {{.*}}); + // CHECK-NOT: (_1.0: Adt) = copy (*_2); mir! { let _2: *mut Adt; let _3: u32; @@ -64,6 +89,26 @@ fn fields(_1: (Adt, Adt)) { } } +// EMIT_MIR gvn_overlapping.copy_overlapping.GVN.diff +#[custom_mir(dialect = "runtime")] +fn copy_overlapping() { + // CHECK-LABEL: fn copy_overlapping( + // CHECK-NOT: _1 = copy (*_{{.*}}); + // CHECK: _1 = Adt::Some(copy _2); + mir! { + let _1; + let _2; + let _3; + { + place!(Field(Variant(_1, 1), 0)) = 0u32; + _3 = &_1; + _2 = Field(Variant(*_3, 1), 0); + _1 = Adt::Some(_2); + Return() + } + } +} + fn main() { overlapping(Adt::Some(0)); } diff --git a/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff b/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff index 0883545659113..9bfb0ba2d584f 100644 --- a/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff +++ b/tests/mir-opt/gvn_overlapping.stable_projection.GVN.diff @@ -11,8 +11,7 @@ _2 = &raw mut (_1.0: Adt); _4 = &(*_2); _3 = copy (((*_4) as variant#1).0: u32); -- (_1.0: Adt) = Adt::Some(copy _3); -+ (_1.0: Adt) = copy (*_4); + (_1.0: Adt) = Adt::Some(copy _3); return; } } diff --git a/tests/mir-opt/gvn_overlapping.stable_projection_nonoverlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.stable_projection_nonoverlapping.GVN.diff new file mode 100644 index 0000000000000..c3acb57aa29da --- /dev/null +++ b/tests/mir-opt/gvn_overlapping.stable_projection_nonoverlapping.GVN.diff @@ -0,0 +1,19 @@ +- // MIR for `stable_projection_nonoverlapping` before GVN ++ // MIR for `stable_projection_nonoverlapping` after GVN + + fn stable_projection_nonoverlapping(_1: (Adt,)) -> () { + let mut _0: (); + let mut _2: *mut Adt; + let mut _3: u32; + let mut _4: &Adt; + let mut _5: (Adt,); + + bb0: { + _2 = &raw mut (_1.0: Adt); + _4 = &(*_2); + _3 = copy (((*_4) as variant#1).0: u32); + (_5.0: Adt) = Adt::Some(copy _3); + return; + } + } + diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff index 269af438e37ef..72d77368fca8c 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-abort.diff @@ -24,8 +24,10 @@ bb1: { StorageLive(_4); - _7 = copy (*_1); - _4 = copy (*_7); +- _7 = copy (*_1); +- _4 = copy (*_7); ++ _7 = copy _6; ++ _4 = copy (*_6); StorageLive(_5); _5 = copy _2; - _0 = Eq(move _4, move _5); diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff index 9ce17342a445c..5ff0b2211e7e9 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff @@ -24,8 +24,10 @@ bb1: { StorageLive(_4); - _7 = copy (*_1); - _4 = copy (*_7); +- _7 = copy (*_1); +- _4 = copy (*_7); ++ _7 = copy _6; ++ _4 = copy (*_6); StorageLive(_5); _5 = copy _2; - _0 = Eq(move _4, move _5); diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-abort.mir index 23b1c3f3f43ad..694108dcebde1 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-abort.mir @@ -6,8 +6,7 @@ fn src(_1: &&u8) -> bool { let mut _2: &u8; let _3: u8; let _4: (); - let mut _5: &u8; - let mut _6: u8; + let mut _5: u8; scope 1 { debug y => _3; } @@ -19,11 +18,10 @@ fn src(_1: &&u8) -> bool { } bb1: { - StorageLive(_6); - _5 = copy (*_1); - _6 = copy (*_5); - _0 = Eq(move _6, copy _3); - StorageDead(_6); + StorageLive(_5); + _5 = copy (*_2); + _0 = Eq(move _5, copy _3); + StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-unwind.mir index 4c01e9464bf49..355699021fe60 100644 --- a/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-unwind.mir @@ -6,8 +6,7 @@ fn src(_1: &&u8) -> bool { let mut _2: &u8; let _3: u8; let _4: (); - let mut _5: &u8; - let mut _6: u8; + let mut _5: u8; scope 1 { debug y => _3; } @@ -19,11 +18,10 @@ fn src(_1: &&u8) -> bool { } bb1: { - StorageLive(_6); - _5 = copy (*_1); - _6 = copy (*_5); - _0 = Eq(move _6, copy _3); - StorageDead(_6); + StorageLive(_5); + _5 = copy (*_2); + _0 = Eq(move _5, copy _3); + StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir index 2cab88182962f..26533d9cc18ea 100644 --- a/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_filter.variant_a-{closure#0}.PreCodegen.after.mir @@ -5,83 +5,89 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 let mut _3: &(usize, usize, usize, usize); let mut _6: bool; let mut _9: bool; - let mut _10: bool; - let _13: &usize; - let _14: &usize; + let mut _12: bool; let _15: &usize; let _16: &usize; - let mut _17: &&usize; - let mut _18: &&usize; + let _17: &usize; + let _18: &usize; let mut _19: &&usize; let mut _20: &&usize; let mut _21: &&usize; let mut _22: &&usize; let mut _23: &&usize; let mut _24: &&usize; + let mut _25: &&usize; + let mut _26: &&usize; scope 1 { - debug a => _13; - debug b => _14; - debug c => _15; - debug d => _16; + debug a => _15; + debug b => _16; + debug c => _17; + debug d => _18; scope 2 (inlined std::cmp::impls::::le) { - debug self => _17; - debug other => _18; + debug self => _19; + debug other => _20; scope 3 (inlined std::cmp::impls::::le) { - debug self => _13; - debug other => _15; + debug self => _15; + debug other => _17; let mut _4: usize; let mut _5: usize; } } scope 4 (inlined std::cmp::impls::::le) { - debug self => _19; - debug other => _20; + debug self => _21; + debug other => _22; scope 5 (inlined std::cmp::impls::::le) { - debug self => _16; - debug other => _14; + debug self => _18; + debug other => _16; let mut _7: usize; let mut _8: usize; } } scope 6 (inlined std::cmp::impls::::le) { - debug self => _21; - debug other => _22; + debug self => _23; + debug other => _24; scope 7 (inlined std::cmp::impls::::le) { - debug self => _15; - debug other => _13; + debug self => _17; + debug other => _15; + let mut _10: usize; + let mut _11: usize; } } scope 8 (inlined std::cmp::impls::::le) { - debug self => _23; - debug other => _24; + debug self => _25; + debug other => _26; scope 9 (inlined std::cmp::impls::::le) { - debug self => _14; - debug other => _16; - let mut _11: usize; - let mut _12: usize; + debug self => _16; + debug other => _18; + let mut _13: usize; + let mut _14: usize; } } } bb0: { _3 = copy (*_2); - // DBG: _13 = &((*_3).0: usize); - // DBG: _14 = &((*_3).1: usize); - // DBG: _15 = &((*_3).2: usize); - // DBG: _16 = &((*_3).3: usize); + // DBG: _15 = &((*_3).0: usize); + // DBG: _16 = &((*_3).1: usize); + // DBG: _17 = &((*_3).2: usize); + // DBG: _18 = &((*_3).3: usize); StorageLive(_6); - // DBG: _17 = &_13; - // DBG: _18 = &?; + // DBG: _19 = &_15; + // DBG: _20 = &?; + StorageLive(_4); _4 = copy ((*_3).0: usize); + StorageLive(_5); _5 = copy ((*_3).2: usize); - _6 = Le(copy _4, copy _5); + _6 = Le(move _4, move _5); + StorageDead(_5); + StorageDead(_4); switchInt(move _6) -> [0: bb2, otherwise: bb1]; } bb1: { StorageLive(_9); - // DBG: _19 = &_16; - // DBG: _20 = &?; + // DBG: _21 = &_18; + // DBG: _22 = &?; StorageLive(_7); _7 = copy ((*_3).3: usize); StorageLive(_8); @@ -93,11 +99,17 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb2: { + StorageLive(_12); + // DBG: _23 = &_17; + // DBG: _24 = &?; StorageLive(_10); - // DBG: _21 = &_15; - // DBG: _22 = &?; - _10 = Le(copy _5, copy _4); - switchInt(move _10) -> [0: bb3, otherwise: bb4]; + _10 = copy ((*_3).2: usize); + StorageLive(_11); + _11 = copy ((*_3).0: usize); + _12 = Le(move _10, move _11); + StorageDead(_11); + StorageDead(_10); + switchInt(move _12) -> [0: bb3, otherwise: bb4]; } bb3: { @@ -106,20 +118,20 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2 } bb4: { - // DBG: _23 = &_14; - // DBG: _24 = &?; - StorageLive(_11); - _11 = copy ((*_3).1: usize); - StorageLive(_12); - _12 = copy ((*_3).3: usize); - _0 = Le(move _11, move _12); - StorageDead(_12); - StorageDead(_11); + // DBG: _25 = &_16; + // DBG: _26 = &?; + StorageLive(_13); + _13 = copy ((*_3).1: usize); + StorageLive(_14); + _14 = copy ((*_3).3: usize); + _0 = Le(move _13, move _14); + StorageDead(_14); + StorageDead(_13); goto -> bb5; } bb5: { - StorageDead(_10); + StorageDead(_12); goto -> bb7; }