@@ -1366,57 +1366,108 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
1366
1366
return self . new_opaque ( ) ;
1367
1367
}
1368
1368
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 ;
1383
1384
}
1384
- }
1385
1385
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
+ }
1397
1400
}
1398
- }
1399
1401
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,
1405
1420
value : inner_value,
1406
1421
from : inner_from,
1407
1422
to : inner_to,
1408
1423
} = * 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 ;
1416
1467
}
1417
1468
}
1418
1469
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) {
1420
1471
* operand = op;
1421
1472
}
1422
1473
@@ -1438,6 +1489,54 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
1438
1489
false
1439
1490
}
1440
1491
}
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
+ }
1441
1540
}
1442
1541
1443
1542
fn op_to_prop_const < ' tcx > (
0 commit comments