@@ -1366,110 +1366,112 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
1366
1366
return self . new_opaque ( ) ;
1367
1367
}
1368
1368
1369
- let mut was_updated = false ;
1370
-
1371
- // Transmuting `*const T` <=> `*mut T` is just a pointer cast,
1372
- // which we might be able to merge with other ones later.
1373
- if let Transmute = kind
1374
- && let ty:: RawPtr ( from_pointee, _) = from. kind ( )
1375
- && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1376
- && from_pointee == to_pointee
1377
- {
1378
- * kind = PtrToPtr ;
1379
- was_updated = true ;
1380
- }
1381
-
1382
- // If a cast just casts away the metadata again, then we can get it by
1383
- // casting the original thin pointer passed to `from_raw_parts`
1384
- if let PtrToPtr = kind
1385
- && let Value :: Aggregate ( AggregateTy :: RawPtr { data_pointer_ty, .. } , _, fields) =
1386
- self . get ( value)
1387
- && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1388
- && to_pointee. is_sized ( self . tcx , self . typing_env ( ) )
1389
- {
1390
- from = * data_pointer_ty;
1391
- value = fields[ 0 ] ;
1392
- was_updated = true ;
1393
- if * data_pointer_ty == to {
1394
- return Some ( fields[ 0 ] ) ;
1369
+ let mut was_ever_updated = false ;
1370
+ loop {
1371
+ let mut was_updated_this_iteration = false ;
1372
+
1373
+ // Transmuting `*const T` <=> `*mut T` is just a pointer cast,
1374
+ // which we might be able to merge with other ones later.
1375
+ if let Transmute = kind
1376
+ && let ty:: RawPtr ( from_pointee, _) = from. kind ( )
1377
+ && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1378
+ && from_pointee == to_pointee
1379
+ {
1380
+ * kind = PtrToPtr ;
1381
+ was_updated_this_iteration = true ;
1395
1382
}
1396
- }
1397
1383
1398
- // PtrToPtr-then-PtrToPtr can skip the intermediate step
1399
- if let PtrToPtr = kind
1400
- && let Value :: Cast { kind : inner_kind, value : inner_value, from : inner_from, to : _ } =
1401
- * self . get ( value)
1402
- && let PtrToPtr = inner_kind
1403
- {
1404
- from = inner_from;
1405
- value = inner_value;
1406
- was_updated = true ;
1407
- if inner_from == to {
1408
- return Some ( inner_value) ;
1384
+ // If a cast just casts away the metadata again, then we can get it by
1385
+ // casting the original thin pointer passed to `from_raw_parts`
1386
+ if let PtrToPtr = kind
1387
+ && let Value :: Aggregate ( AggregateTy :: RawPtr { data_pointer_ty, .. } , _, fields) =
1388
+ self . get ( value)
1389
+ && let ty:: RawPtr ( to_pointee, _) = to. kind ( )
1390
+ && to_pointee. is_sized ( self . tcx , self . typing_env ( ) )
1391
+ {
1392
+ from = * data_pointer_ty;
1393
+ value = fields[ 0 ] ;
1394
+ was_updated_this_iteration = true ;
1395
+ if * data_pointer_ty == to {
1396
+ return Some ( fields[ 0 ] ) ;
1397
+ }
1409
1398
}
1410
- }
1411
1399
1412
- // Aggregate-then-Transmute can just transmute the original field value,
1413
- // so long as the bytes of a value from only from a single field.
1414
- if let Transmute = kind
1415
- && let Value :: Aggregate (
1416
- AggregateTy :: Def ( aggregate_did, aggregate_args) ,
1417
- variant_idx,
1418
- field_values,
1419
- ) = self . get ( value)
1420
- && let aggregate_ty =
1421
- self . tcx . type_of ( aggregate_did) . instantiate ( self . tcx , aggregate_args)
1422
- && let Some ( ( field_idx, field_ty) ) =
1423
- self . value_is_all_in_one_field ( aggregate_ty, * variant_idx)
1424
- {
1425
- from = field_ty;
1426
- value = field_values[ field_idx. as_usize ( ) ] ;
1427
- was_updated = true ;
1428
- if field_ty == to {
1429
- return Some ( value) ;
1400
+ // Aggregate-then-Transmute can just transmute the original field value,
1401
+ // so long as the bytes of a value from only from a single field.
1402
+ if let Transmute = kind
1403
+ && let Value :: Aggregate (
1404
+ AggregateTy :: Def ( aggregate_did, aggregate_args) ,
1405
+ variant_idx,
1406
+ field_values,
1407
+ ) = self . get ( value)
1408
+ && let aggregate_ty =
1409
+ self . tcx . type_of ( aggregate_did) . instantiate ( self . tcx , aggregate_args)
1410
+ && let Some ( ( field_idx, field_ty) ) =
1411
+ self . value_is_all_in_one_field ( aggregate_ty, * variant_idx)
1412
+ {
1413
+ from = field_ty;
1414
+ value = field_values[ field_idx. as_usize ( ) ] ;
1415
+ was_updated_this_iteration = true ;
1416
+ if field_ty == to {
1417
+ return Some ( value) ;
1418
+ }
1430
1419
}
1431
- }
1432
1420
1433
- // PtrToPtr-then-Transmute can just transmute the original, so long as the
1434
- // PtrToPtr didn't change metadata (and thus the size of the pointer)
1435
- if let Transmute = kind
1436
- && let Value :: Cast {
1437
- kind : PtrToPtr ,
1421
+ // Various cast-then-cast cases can be simplified.
1422
+ if let Value :: Cast {
1423
+ kind : inner_kind,
1438
1424
value : inner_value,
1439
1425
from : inner_from,
1440
1426
to : inner_to,
1441
1427
} = * self . get ( value)
1442
- && self . pointers_have_same_metadata ( inner_from, inner_to)
1443
- {
1444
- from = inner_from;
1445
- value = inner_value;
1446
- was_updated = true ;
1447
- if inner_from == to {
1448
- return Some ( inner_value) ;
1428
+ {
1429
+ let new_kind = match ( inner_kind, * kind) {
1430
+ // Even if there's a narrowing cast in here that's fine, because
1431
+ // things like `*mut [i32] -> *mut i32 -> *const i32` and
1432
+ // `*mut [i32] -> *const [i32] -> *const i32` can skip the middle in MIR.
1433
+ ( PtrToPtr , PtrToPtr ) => Some ( PtrToPtr ) ,
1434
+ // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity:
1435
+ // `*const T -> *mut T -> NonNull<T>` is fine, but we need to check for narrowing
1436
+ // to skip things like `*const [i32] -> *const i32 -> NonNull<T>`.
1437
+ ( PtrToPtr , Transmute )
1438
+ if self . pointers_have_same_metadata ( inner_from, inner_to) =>
1439
+ {
1440
+ Some ( Transmute )
1441
+ }
1442
+ // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different
1443
+ // variables for their metadata, and thus this can't merge with the previous arm.
1444
+ ( Transmute , PtrToPtr ) if self . pointers_have_same_metadata ( from, to) => {
1445
+ Some ( Transmute )
1446
+ }
1447
+ // If would be legal to always do this, but we don't want to hide information
1448
+ // from the backend that it'd otherwise be able to use for optimizations.
1449
+ ( Transmute , Transmute )
1450
+ if !self . type_may_have_niche_of_interest_to_backend ( inner_to) =>
1451
+ {
1452
+ Some ( Transmute )
1453
+ }
1454
+ _ => None ,
1455
+ } ;
1456
+ if let Some ( new_kind) = new_kind {
1457
+ * kind = new_kind;
1458
+ from = inner_from;
1459
+ value = inner_value;
1460
+ was_updated_this_iteration = true ;
1461
+ if inner_from == to {
1462
+ return Some ( inner_value) ;
1463
+ }
1464
+ }
1449
1465
}
1450
- }
1451
1466
1452
- // Transmute-then-PtrToPtr can just transmute the original, so long as the
1453
- // PtrToPtr won't change metadata (and thus the size of the pointer)
1454
- if let PtrToPtr = kind
1455
- && let Value :: Cast {
1456
- kind : Transmute ,
1457
- value : inner_value,
1458
- from : inner_from,
1459
- to : _inner_to,
1460
- } = * self . get ( value)
1461
- && self . pointers_have_same_metadata ( from, to)
1462
- {
1463
- * kind = Transmute ;
1464
- from = inner_from;
1465
- value = inner_value;
1466
- was_updated = true ;
1467
- if inner_from == to {
1468
- return Some ( inner_value) ;
1467
+ if was_updated_this_iteration {
1468
+ was_ever_updated = true ;
1469
+ } else {
1470
+ break ;
1469
1471
}
1470
1472
}
1471
1473
1472
- if was_updated && let Some ( op) = self . try_as_operand ( value, location) {
1474
+ if was_ever_updated && let Some ( op) = self . try_as_operand ( value, location) {
1473
1475
* operand = op;
1474
1476
}
1475
1477
@@ -1492,6 +1494,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
1492
1494
}
1493
1495
}
1494
1496
1497
+ /// Returns `false` if we know for sure that this type has no interesting niche,
1498
+ /// and thus we can skip transmuting through it without worrying.
1499
+ ///
1500
+ /// The backend will emit `assume`s when transmuting between types with niches,
1501
+ /// so we want to preserve `i32 -> char -> u32` so that that data is around,
1502
+ /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`.
1503
+ fn type_may_have_niche_of_interest_to_backend ( & self , ty : Ty < ' tcx > ) -> bool {
1504
+ let Ok ( layout) = self . ecx . layout_of ( ty) else {
1505
+ // If it's too generic or something, then assume it might be interesting later.
1506
+ return true ;
1507
+ } ;
1508
+
1509
+ match layout. backend_repr {
1510
+ BackendRepr :: Uninhabited => true ,
1511
+ BackendRepr :: Scalar ( a) => !a. is_always_valid ( & self . ecx ) ,
1512
+ BackendRepr :: ScalarPair ( a, b) => {
1513
+ !a. is_always_valid ( & self . ecx ) || !b. is_always_valid ( & self . ecx )
1514
+ }
1515
+ BackendRepr :: Vector { .. } | BackendRepr :: Memory { .. } => false ,
1516
+ }
1517
+ }
1518
+
1495
1519
fn value_is_all_in_one_field (
1496
1520
& self ,
1497
1521
ty : Ty < ' tcx > ,
0 commit comments