diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 517bc61e5eb02..61f251ba7c606 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1494,6 +1494,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } + #[instrument(level = "trace", skip(self), ret)] fn simplify_cast( &mut self, initial_kind: &mut CastKind, @@ -1549,6 +1550,42 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } + // Field-Access-then-Transmute can just transmute the original value, + // so long as the bytes of a value from only from a single field. + if let Transmute = kind + && let Value::Projection(field_value, ProjectionElem::Field(field_idx, ())) = + self.get(value) + { + if let Value::Projection( + downcast_value, + ProjectionElem::Downcast(_, _variant_idx), + ) = self.get(field_value) + { + let downcast_ty = self.ty(downcast_value); + if let Ok(downcast_layout) = self.ecx.layout_of(downcast_ty) + && let Ok(projected_layout) = self.ecx.layout_of(from) + && downcast_layout.size == projected_layout.size + { + from = downcast_ty; + value = downcast_value; + was_updated_this_iteration = true; + if projected_layout.ty == to { + return Some(value); + } + } + } else if let Some((f_idx, field_ty)) = + self.value_is_all_in_one_field(self.ty(field_value), FIRST_VARIANT) + { + assert_eq!(field_idx, f_idx, "{from} -> {field_ty}"); + from = self.ty(field_value); + value = field_value; + was_updated_this_iteration = true; + if field_ty == to { + return Some(value); + } + } + } + // Aggregate-then-Transmute can just transmute the original field value, // so long as the bytes of a value from only from a single field. if let Transmute = kind diff --git a/tests/mir-opt/const_prop/transmute.option_field.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.option_field.GVN.32bit.diff new file mode 100644 index 0000000000000..2a1af7787d4e9 --- /dev/null +++ b/tests/mir-opt/const_prop/transmute.option_field.GVN.32bit.diff @@ -0,0 +1,42 @@ +- // MIR for `option_field` before GVN ++ // MIR for `option_field` after GVN + + fn option_field(_1: Option>) -> *const () { + debug x => _1; + let mut _0: *const (); + let mut _2: isize; + let mut _4: std::ptr::NonNull<()>; + scope 1 { + debug x => _3; + let _3: std::ptr::NonNull<()>; + } + + bb0: { + _2 = discriminant(_1); + switchInt(move _2) -> [1: bb1, otherwise: bb2]; + } + + bb1: { +- StorageLive(_3); ++ nop; + _3 = copy ((_1 as Some).0: std::ptr::NonNull<()>); + StorageLive(_4); + _4 = copy _3; +- _0 = move _4 as *const () (Transmute); ++ _0 = copy _1 as *const () (Transmute); + StorageDead(_4); +- StorageDead(_3); ++ nop; + goto -> bb3; + } + + bb2: { + _0 = const 0_usize as *const () (PointerWithExposedProvenance); + goto -> bb3; + } + + bb3: { + return; + } + } + diff --git a/tests/mir-opt/const_prop/transmute.option_field.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.option_field.GVN.64bit.diff new file mode 100644 index 0000000000000..2a1af7787d4e9 --- /dev/null +++ b/tests/mir-opt/const_prop/transmute.option_field.GVN.64bit.diff @@ -0,0 +1,42 @@ +- // MIR for `option_field` before GVN ++ // MIR for `option_field` after GVN + + fn option_field(_1: Option>) -> *const () { + debug x => _1; + let mut _0: *const (); + let mut _2: isize; + let mut _4: std::ptr::NonNull<()>; + scope 1 { + debug x => _3; + let _3: std::ptr::NonNull<()>; + } + + bb0: { + _2 = discriminant(_1); + switchInt(move _2) -> [1: bb1, otherwise: bb2]; + } + + bb1: { +- StorageLive(_3); ++ nop; + _3 = copy ((_1 as Some).0: std::ptr::NonNull<()>); + StorageLive(_4); + _4 = copy _3; +- _0 = move _4 as *const () (Transmute); ++ _0 = copy _1 as *const () (Transmute); + StorageDead(_4); +- StorageDead(_3); ++ nop; + goto -> bb3; + } + + bb2: { + _0 = const 0_usize as *const () (PointerWithExposedProvenance); + goto -> bb3; + } + + bb3: { + return; + } + } + diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index ad971a64370e9..cf4244860832f 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -84,4 +84,12 @@ pub unsafe fn unreachable_box() -> ! { match *x {} } +// EMIT_MIR transmute.option_field.GVN.diff +pub unsafe fn option_field(x: Option>) -> *const () { + // CHECK-LABEL: fn option_field( + // CHECK: _3 = copy ((_1 as Some).0: std::ptr::NonNull<()>) + // CHECK: _0 = copy _1 as *const () (Transmute) + if let Some(x) = x { unsafe { transmute(x) } } else { 0 as *const () } +} + enum Never {} diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff index bd24af602c88c..40c2758cd4178 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff @@ -14,7 +14,7 @@ - _1 = const 1_usize as std::boxed::Box (Transmute); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} }} as *const Never (Transmute); ++ _2 = const std::boxed::Box::(std::ptr::Unique:: {{ pointer: std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} }}, _marker: std::marker::PhantomData:: }}, std::alloc::Global) as *const Never (Transmute); unreachable; } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff index bd24af602c88c..40c2758cd4178 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff @@ -14,7 +14,7 @@ - _1 = const 1_usize as std::boxed::Box (Transmute); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} }} as *const Never (Transmute); ++ _2 = const std::boxed::Box::(std::ptr::Unique:: {{ pointer: std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} }}, _marker: std::marker::PhantomData:: }}, std::alloc::Global) as *const Never (Transmute); unreachable; } } 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 25f323061c6e8..f65072ca387ce 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 @@ -74,7 +74,8 @@ - StorageLive(_5); + nop; _10 = copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); +- _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); ++ _11 = copy _10 as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); + nop; 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 9a354fc005ed5..312918a35f147 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 @@ -54,7 +54,8 @@ - StorageLive(_5); + nop; _10 = copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); +- _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); ++ _11 = copy _10 as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); + nop; 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 32fe7e2fdeb60..d64f95ad18f20 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 @@ -74,7 +74,8 @@ - StorageLive(_5); + nop; _10 = copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); +- _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); ++ _11 = copy _10 as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); + nop; 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 9a354fc005ed5..312918a35f147 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 @@ -54,7 +54,8 @@ - StorageLive(_5); + nop; _10 = copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); +- _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); ++ _11 = copy _10 as *const () (Transmute); _5 = &raw const (*_11); - StorageLive(_6); + nop; diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff index b48e6fc56f430..d684c5ea0af5a 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff @@ -69,7 +69,8 @@ StorageLive(_3); StorageLive(_4); StorageLive(_5); - StorageLive(_6); +- StorageLive(_6); ++ nop; StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable]; @@ -93,11 +94,14 @@ } bb6: { - _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); +- _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); ++ _5 = copy ((_6 as Ok).0: std::ptr::NonNull<[u8]>); StorageDead(_14); StorageDead(_10); - StorageDead(_6); - _4 = copy _5 as *mut [u8] (Transmute); +- StorageDead(_6); +- _4 = copy _5 as *mut [u8] (Transmute); ++ nop; ++ _4 = copy _6 as *mut [u8] (Transmute); StorageDead(_5); _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index 18fc4ac0d87f1..625163059c677 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -69,7 +69,8 @@ StorageLive(_3); StorageLive(_4); StorageLive(_5); - StorageLive(_6); +- StorageLive(_6); ++ nop; StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable]; @@ -93,11 +94,14 @@ } bb6: { - _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); +- _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); ++ _5 = copy ((_6 as Ok).0: std::ptr::NonNull<[u8]>); StorageDead(_14); StorageDead(_10); - StorageDead(_6); - _4 = copy _5 as *mut [u8] (Transmute); +- StorageDead(_6); +- _4 = copy _5 as *mut [u8] (Transmute); ++ nop; ++ _4 = copy _6 as *mut [u8] (Transmute); StorageDead(_5); _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4);