Skip to content

Commit b6b8361

Browse files
committed
Auto merge of #133324 - scottmcm:gvn-aggregate-transmute, r=cjgillot
[mir-opt] GVN some more transmute cases We already did `Transmute`-then-`PtrToPtr`; this adds the nearly-identical `PtrToPtr`-then-`Transmute`. It also adds `transmute(Foo(x))` → `transmute(x)`, when `Foo` is a single-field transparent type. That's useful for things like `NonNull { pointer: p }.as_ptr()`. It also detects when a `Transmute` is just an identity-for-the-value `PtrCast` between different raw pointer types, to help such things fold with other GVN passes. Found these as I was looking at <https://github.com/rust-lang/compiler-team/issues/807>-related changes. This also removes the questionably-useful "turn a transmute into a field projection" part of instsimplify (which I added ages ago without an obvious need for it) since that would just put back the field projections that MCP807 is trying to ban. r? mir-opt
2 parents 65d7296 + b421a56 commit b6b8361

File tree

32 files changed

+1842
-681
lines changed

32 files changed

+1842
-681
lines changed

Diff for: compiler/rustc_mir_transform/src/gvn.rs

+139-40
Original file line numberDiff line numberDiff line change
@@ -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

14431542
fn op_to_prop_const<'tcx>(

Diff for: compiler/rustc_mir_transform/src/instsimplify.rs

-23
Original file line numberDiff line numberDiff line change
@@ -173,29 +173,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
173173
*kind = CastKind::IntToInt;
174174
return;
175175
}
176-
177-
// Transmuting a transparent struct/union to a field's type is a projection
178-
if let ty::Adt(adt_def, args) = operand_ty.kind()
179-
&& adt_def.repr().transparent()
180-
&& (adt_def.is_struct() || adt_def.is_union())
181-
&& let Some(place) = operand.place()
182-
{
183-
let variant = adt_def.non_enum_variant();
184-
for (i, field) in variant.fields.iter_enumerated() {
185-
let field_ty = field.ty(self.tcx, args);
186-
if field_ty == *cast_ty {
187-
let place = place
188-
.project_deeper(&[ProjectionElem::Field(i, *cast_ty)], self.tcx);
189-
let operand = if operand.is_move() {
190-
Operand::Move(place)
191-
} else {
192-
Operand::Copy(place)
193-
};
194-
*rvalue = Rvalue::Use(operand);
195-
return;
196-
}
197-
}
198-
}
199176
}
200177
}
201178
}

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
StorageLive(_7);
5252
_7 = const 1_usize;
5353
_6 = const {0x1 as *mut [bool; 0]};
54-
StorageDead(_7);
5554
StorageLive(_11);
5655
StorageLive(_8);
5756
_8 = UbChecks();
@@ -79,6 +78,7 @@
7978
_11 = const {0x1 as *const [bool; 0]};
8079
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8180
StorageDead(_11);
81+
StorageDead(_7);
8282
StorageDead(_6);
8383
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8484
StorageDead(_5);

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
StorageLive(_7);
5252
_7 = const 1_usize;
5353
_6 = const {0x1 as *mut [bool; 0]};
54-
StorageDead(_7);
5554
StorageLive(_11);
5655
StorageLive(_8);
5756
_8 = UbChecks();
@@ -83,6 +82,7 @@
8382
_11 = const {0x1 as *const [bool; 0]};
8483
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8584
StorageDead(_11);
85+
StorageDead(_7);
8686
StorageDead(_6);
8787
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8888
StorageDead(_5);

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
StorageLive(_7);
5252
_7 = const 1_usize;
5353
_6 = const {0x1 as *mut [bool; 0]};
54-
StorageDead(_7);
5554
StorageLive(_11);
5655
StorageLive(_8);
5756
_8 = UbChecks();
@@ -79,6 +78,7 @@
7978
_11 = const {0x1 as *const [bool; 0]};
8079
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8180
StorageDead(_11);
81+
StorageDead(_7);
8282
StorageDead(_6);
8383
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8484
StorageDead(_5);

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
StorageLive(_7);
5252
_7 = const 1_usize;
5353
_6 = const {0x1 as *mut [bool; 0]};
54-
StorageDead(_7);
5554
StorageLive(_11);
5655
StorageLive(_8);
5756
_8 = UbChecks();
@@ -83,6 +82,7 @@
8382
_11 = const {0x1 as *const [bool; 0]};
8483
_5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8584
StorageDead(_11);
85+
StorageDead(_7);
8686
StorageDead(_6);
8787
_4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
8888
StorageDead(_5);

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
- _6 = copy _7 as *mut [bool; 0] (Transmute);
5454
+ _7 = const 1_usize;
5555
+ _6 = const {0x1 as *mut [bool; 0]};
56-
StorageDead(_7);
5756
StorageLive(_11);
5857
StorageLive(_8);
5958
_8 = UbChecks();
@@ -67,7 +66,7 @@
6766

6867
bb2: {
6968
StorageLive(_10);
70-
- _10 = copy _6 as *mut () (PtrToPtr);
69+
- _10 = copy _7 as *mut () (Transmute);
7170
- _9 = NonNull::<T>::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable];
7271
+ _10 = const {0x1 as *mut ()};
7372
+ _9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable];
@@ -80,11 +79,12 @@
8079

8180
bb4: {
8281
StorageDead(_8);
83-
- _11 = copy _6 as *const [bool; 0] (PtrToPtr);
82+
- _11 = copy _7 as *const [bool; 0] (Transmute);
8483
- _5 = NonNull::<[bool; 0]> { pointer: copy _11 };
8584
+ _11 = const {0x1 as *const [bool; 0]};
8685
+ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8786
StorageDead(_11);
87+
StorageDead(_7);
8888
StorageDead(_6);
8989
- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
9090
+ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
- _6 = copy _7 as *mut [bool; 0] (Transmute);
5454
+ _7 = const 1_usize;
5555
+ _6 = const {0x1 as *mut [bool; 0]};
56-
StorageDead(_7);
5756
StorageLive(_11);
5857
StorageLive(_8);
5958
_8 = UbChecks();
@@ -71,7 +70,7 @@
7170

7271
bb3: {
7372
StorageLive(_10);
74-
- _10 = copy _6 as *mut () (PtrToPtr);
73+
- _10 = copy _7 as *mut () (Transmute);
7574
- _9 = NonNull::<T>::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable];
7675
+ _10 = const {0x1 as *mut ()};
7776
+ _9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable];
@@ -84,11 +83,12 @@
8483

8584
bb5: {
8685
StorageDead(_8);
87-
- _11 = copy _6 as *const [bool; 0] (PtrToPtr);
86+
- _11 = copy _7 as *const [bool; 0] (Transmute);
8887
- _5 = NonNull::<[bool; 0]> { pointer: copy _11 };
8988
+ _11 = const {0x1 as *const [bool; 0]};
9089
+ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
9190
StorageDead(_11);
91+
StorageDead(_7);
9292
StorageDead(_6);
9393
- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
9494
+ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};

Diff for: tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
- _6 = copy _7 as *mut [bool; 0] (Transmute);
5454
+ _7 = const 1_usize;
5555
+ _6 = const {0x1 as *mut [bool; 0]};
56-
StorageDead(_7);
5756
StorageLive(_11);
5857
StorageLive(_8);
5958
_8 = UbChecks();
@@ -67,7 +66,7 @@
6766

6867
bb2: {
6968
StorageLive(_10);
70-
- _10 = copy _6 as *mut () (PtrToPtr);
69+
- _10 = copy _7 as *mut () (Transmute);
7170
- _9 = NonNull::<T>::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable];
7271
+ _10 = const {0x1 as *mut ()};
7372
+ _9 = NonNull::<T>::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable];
@@ -80,11 +79,12 @@
8079

8180
bb4: {
8281
StorageDead(_8);
83-
- _11 = copy _6 as *const [bool; 0] (PtrToPtr);
82+
- _11 = copy _7 as *const [bool; 0] (Transmute);
8483
- _5 = NonNull::<[bool; 0]> { pointer: copy _11 };
8584
+ _11 = const {0x1 as *const [bool; 0]};
8685
+ _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
8786
StorageDead(_11);
87+
StorageDead(_7);
8888
StorageDead(_6);
8989
- _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
9090
+ _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};

0 commit comments

Comments
 (0)