@@ -1366,57 +1366,108 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
13661366 return self . new_opaque ( ) ;
13671367 }
13681368
1369- let mut was_updated = false ;
1370-
1371- // If that cast just casts away the metadata again,
1372- if let PtrToPtr = kind
1373- && let Value :: Aggregate ( AggregateTy :: RawPtr { data_pointer_ty, .. } , _, fields) =
1374- self . get ( value)
1375- && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1376- && to_pointee. is_sized ( self . tcx , self . typing_env ( ) )
1377- {
1378- from = * data_pointer_ty;
1379- value = fields[ 0 ] ;
1380- was_updated = true ;
1381- if * data_pointer_ty == to {
1382- return Some ( fields[ 0 ] ) ;
1369+ let mut was_ever_updated = false ;
1370+ loop {
1371+ let mut was_updated_this_iteration = false ;
1372+
1373+ // Transmuting between raw pointers is just a pointer cast so long as
1374+ // they have the same metadata type (like `*const i32` <=> `*mut u64`
1375+ // or `*mut [i32]` <=> `*const [u64]`), including the common special
1376+ // case of `*const T` <=> `*mut T`.
1377+ if let Transmute = kind
1378+ && from. is_unsafe_ptr ( )
1379+ && to. is_unsafe_ptr ( )
1380+ && self . pointers_have_same_metadata ( from, to)
1381+ {
1382+ * kind = PtrToPtr ;
1383+ was_updated_this_iteration = true ;
13831384 }
1384- }
13851385
1386- // PtrToPtr-then-PtrToPtr can skip the intermediate step
1387- if let PtrToPtr = kind
1388- && let Value :: Cast { kind : inner_kind, value : inner_value, from : inner_from, to : _ } =
1389- * self . get ( value)
1390- && let PtrToPtr = inner_kind
1391- {
1392- from = inner_from;
1393- value = inner_value;
1394- was_updated = true ;
1395- if inner_from == to {
1396- return Some ( inner_value) ;
1386+ // If a cast just casts away the metadata again, then we can get it by
1387+ // casting the original thin pointer passed to `from_raw_parts`
1388+ if let PtrToPtr = kind
1389+ && let Value :: Aggregate ( AggregateTy :: RawPtr { data_pointer_ty, .. } , _, fields) =
1390+ self . get ( value)
1391+ && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1392+ && to_pointee. is_sized ( self . tcx , self . typing_env ( ) )
1393+ {
1394+ from = * data_pointer_ty;
1395+ value = fields[ 0 ] ;
1396+ was_updated_this_iteration = true ;
1397+ if * data_pointer_ty == to {
1398+ return Some ( fields[ 0 ] ) ;
1399+ }
13971400 }
1398- }
13991401
1400- // PtrToPtr-then-Transmute can just transmute the original, so long as the
1401- // PtrToPtr didn't change metadata (and thus the size of the pointer)
1402- if let Transmute = kind
1403- && let Value :: Cast {
1404- kind : PtrToPtr ,
1402+ // Aggregate-then-Transmute can just transmute the original field value,
1403+ // so long as the bytes of a value from only from a single field.
1404+ if let Transmute = kind
1405+ && let Value :: Aggregate ( _aggregate_ty, variant_idx, field_values) = self . get ( value)
1406+ && let Some ( ( field_idx, field_ty) ) =
1407+ self . value_is_all_in_one_field ( from, * variant_idx)
1408+ {
1409+ from = field_ty;
1410+ value = field_values[ field_idx. as_usize ( ) ] ;
1411+ was_updated_this_iteration = true ;
1412+ if field_ty == to {
1413+ return Some ( value) ;
1414+ }
1415+ }
1416+
1417+ // Various cast-then-cast cases can be simplified.
1418+ if let Value :: Cast {
1419+ kind : inner_kind,
14051420 value : inner_value,
14061421 from : inner_from,
14071422 to : inner_to,
14081423 } = * self . get ( value)
1409- && self . pointers_have_same_metadata ( inner_from, inner_to)
1410- {
1411- from = inner_from;
1412- value = inner_value;
1413- was_updated = true ;
1414- if inner_from == to {
1415- return Some ( inner_value) ;
1424+ {
1425+ let new_kind = match ( inner_kind, * kind) {
1426+ // Even if there's a narrowing cast in here that's fine, because
1427+ // things like `*mut [i32] -> *mut i32 -> *const i32` and
1428+ // `*mut [i32] -> *const [i32] -> *const i32` can skip the middle in MIR.
1429+ ( PtrToPtr , PtrToPtr ) => Some ( PtrToPtr ) ,
1430+ // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity:
1431+ // `*const T -> *mut T -> NonNull<T>` is fine, but we need to check for narrowing
1432+ // to skip things like `*const [i32] -> *const i32 -> NonNull<T>`.
1433+ ( PtrToPtr , Transmute )
1434+ if self . pointers_have_same_metadata ( inner_from, inner_to) =>
1435+ {
1436+ Some ( Transmute )
1437+ }
1438+ // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different
1439+ // variables for their metadata, and thus this can't merge with the previous arm.
1440+ ( Transmute , PtrToPtr ) if self . pointers_have_same_metadata ( from, to) => {
1441+ Some ( Transmute )
1442+ }
1443+ // If would be legal to always do this, but we don't want to hide information
1444+ // from the backend that it'd otherwise be able to use for optimizations.
1445+ ( Transmute , Transmute )
1446+ if !self . type_may_have_niche_of_interest_to_backend ( inner_to) =>
1447+ {
1448+ Some ( Transmute )
1449+ }
1450+ _ => None ,
1451+ } ;
1452+ if let Some ( new_kind) = new_kind {
1453+ * kind = new_kind;
1454+ from = inner_from;
1455+ value = inner_value;
1456+ was_updated_this_iteration = true ;
1457+ if inner_from == to {
1458+ return Some ( inner_value) ;
1459+ }
1460+ }
1461+ }
1462+
1463+ if was_updated_this_iteration {
1464+ was_ever_updated = true ;
1465+ } else {
1466+ break ;
14161467 }
14171468 }
14181469
1419- if was_updated && let Some ( op) = self . try_as_operand ( value, location) {
1470+ if was_ever_updated && let Some ( op) = self . try_as_operand ( value, location) {
14201471 * operand = op;
14211472 }
14221473
@@ -1438,6 +1489,54 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
14381489 false
14391490 }
14401491 }
1492+
1493+ /// Returns `false` if we know for sure that this type has no interesting niche,
1494+ /// and thus we can skip transmuting through it without worrying.
1495+ ///
1496+ /// The backend will emit `assume`s when transmuting between types with niches,
1497+ /// so we want to preserve `i32 -> char -> u32` so that that data is around,
1498+ /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`.
1499+ fn type_may_have_niche_of_interest_to_backend ( & self , ty : Ty < ' tcx > ) -> bool {
1500+ let Ok ( layout) = self . ecx . layout_of ( ty) else {
1501+ // If it's too generic or something, then assume it might be interesting later.
1502+ return true ;
1503+ } ;
1504+
1505+ match layout. backend_repr {
1506+ BackendRepr :: Uninhabited => true ,
1507+ BackendRepr :: Scalar ( a) => !a. is_always_valid ( & self . ecx ) ,
1508+ BackendRepr :: ScalarPair ( a, b) => {
1509+ !a. is_always_valid ( & self . ecx ) || !b. is_always_valid ( & self . ecx )
1510+ }
1511+ BackendRepr :: Vector { .. } | BackendRepr :: Memory { .. } => false ,
1512+ }
1513+ }
1514+
1515+ fn value_is_all_in_one_field (
1516+ & self ,
1517+ ty : Ty < ' tcx > ,
1518+ variant : VariantIdx ,
1519+ ) -> Option < ( FieldIdx , Ty < ' tcx > ) > {
1520+ if let Ok ( layout) = self . ecx . layout_of ( ty)
1521+ && let abi:: Variants :: Single { index } = layout. variants
1522+ && index == variant
1523+ && let Some ( ( field_idx, field_layout) ) = layout. non_1zst_field ( & self . ecx )
1524+ && layout. size == field_layout. size
1525+ {
1526+ // We needed to check the variant to avoid trying to read the tag
1527+ // field from an enum where no fields have variants, since that tag
1528+ // field isn't in the `Aggregate` from which we're getting values.
1529+ Some ( ( FieldIdx :: from_usize ( field_idx) , field_layout. ty ) )
1530+ } else if let ty:: Adt ( adt, args) = ty. kind ( )
1531+ && adt. is_struct ( )
1532+ && adt. repr ( ) . transparent ( )
1533+ && let [ single_field] = adt. non_enum_variant ( ) . fields . raw . as_slice ( )
1534+ {
1535+ Some ( ( FieldIdx :: ZERO , single_field. ty ( self . tcx , args) ) )
1536+ } else {
1537+ None
1538+ }
1539+ }
14411540}
14421541
14431542fn op_to_prop_const < ' tcx > (
0 commit comments