Skip to content

Commit 9574787

Browse files
committed
Use layout information to detect transparent transmutes
1 parent 4d73126 commit 9574787

File tree

4 files changed

+111
-18
lines changed

4 files changed

+111
-18
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,24 +1420,23 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
14201420
}
14211421

14221422
// Aggregate-then-Transmute can just transmute the original field value,
1423-
// so long as the type is transparent over only that one single field.
1423+
// so long as the bytes of a value from only from a single field.
14241424
if let Transmute = kind
14251425
&& let Value::Aggregate(
14261426
AggregateTy::Def(aggregate_did, aggregate_args),
1427-
FIRST_VARIANT,
1427+
variant_idx,
14281428
field_values,
14291429
) = self.get(value)
1430-
&& let [single_field_value] = **field_values
1431-
&& let adt = self.tcx.adt_def(aggregate_did)
1432-
&& adt.is_struct()
1433-
&& adt.repr().transparent()
1430+
&& let aggregate_ty =
1431+
self.tcx.type_of(aggregate_did).instantiate(self.tcx, aggregate_args)
1432+
&& let Some((field_idx, field_ty)) =
1433+
self.value_is_all_in_one_field(aggregate_ty, *variant_idx)
14341434
{
1435-
let field_ty = adt.non_enum_variant().single_field().ty(self.tcx, aggregate_args);
14361435
from = field_ty;
1437-
value = single_field_value;
1436+
value = field_values[field_idx.as_usize()];
14381437
was_updated = true;
14391438
if field_ty == to {
1440-
return Some(single_field_value);
1439+
return Some(value);
14411440
}
14421441
}
14431442

@@ -1543,6 +1542,32 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
15431542
false
15441543
}
15451544
}
1545+
1546+
fn value_is_all_in_one_field(
1547+
&self,
1548+
ty: Ty<'tcx>,
1549+
variant: VariantIdx,
1550+
) -> Option<(FieldIdx, Ty<'tcx>)> {
1551+
if let Ok(layout) = self.ecx.layout_of(ty)
1552+
&& let abi::Variants::Single { index } = layout.variants
1553+
&& index == variant
1554+
&& let Some((field_idx, field_layout)) = layout.non_1zst_field(&self.ecx)
1555+
&& layout.size == field_layout.size
1556+
{
1557+
// We needed to check the variant to avoid trying to read the tag
1558+
// field from an enum where no fields have variants, since that tag
1559+
// field isn't in the `Aggregate` from which we're getting values.
1560+
Some((FieldIdx::from_usize(field_idx), field_layout.ty))
1561+
} else if let ty::Adt(adt, args) = ty.kind()
1562+
&& adt.is_struct()
1563+
&& adt.repr().transparent()
1564+
&& let [single_field] = adt.non_enum_variant().fields.raw.as_slice()
1565+
{
1566+
Some((FieldIdx::ZERO, single_field.ty(self.tcx, args)))
1567+
} else {
1568+
None
1569+
}
1570+
}
15461571
}
15471572

15481573
fn op_to_prop_const<'tcx>(

tests/mir-opt/gvn.aggregate_struct_then_transmute.GVN.panic-abort.diff

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414
let _10: ();
1515
let mut _11: u16;
1616
let mut _12: TypedId<std::string::String>;
17+
let mut _14: u16;
18+
let _15: ();
19+
let mut _16: u16;
20+
let mut _17: std::result::Result<aggregate_struct_then_transmute::Never, u16>;
1721
scope 1 {
1822
debug a => _2;
1923
let _7: TypedId<std::string::String>;
2024
scope 2 {
2125
debug b => _7;
26+
let _13: std::result::Result<aggregate_struct_then_transmute::Never, u16>;
27+
scope 3 {
28+
debug c => _13;
29+
}
2230
}
2331
}
2432

@@ -62,18 +70,43 @@
6270
- _12 = move _7;
6371
- _11 = move _12 as u16 (Transmute);
6472
+ _12 = copy _7;
65-
+ _11 = copy _7 as u16 (Transmute);
73+
+ _11 = copy _1;
6674
StorageDead(_12);
67-
_10 = opaque::<u16>(move _11) -> [return: bb2, unwind unreachable];
75+
- _10 = opaque::<u16>(move _11) -> [return: bb2, unwind unreachable];
76+
+ _10 = opaque::<u16>(copy _1) -> [return: bb2, unwind unreachable];
6877
}
6978

7079
bb2: {
7180
StorageDead(_11);
7281
StorageDead(_10);
82+
- StorageLive(_13);
83+
+ nop;
84+
StorageLive(_14);
85+
_14 = copy _1;
86+
- _13 = Result::<Never, u16>::Err(move _14);
87+
+ _13 = Result::<Never, u16>::Err(copy _1);
88+
StorageDead(_14);
89+
StorageLive(_15);
90+
StorageLive(_16);
91+
StorageLive(_17);
92+
- _17 = move _13;
93+
- _16 = move _17 as u16 (Transmute);
94+
+ _17 = copy _13;
95+
+ _16 = copy _1;
96+
StorageDead(_17);
97+
- _15 = opaque::<u16>(move _16) -> [return: bb3, unwind unreachable];
98+
+ _15 = opaque::<u16>(copy _1) -> [return: bb3, unwind unreachable];
99+
}
100+
101+
bb3: {
102+
StorageDead(_16);
103+
StorageDead(_15);
73104
_0 = const ();
105+
- StorageDead(_13);
74106
- StorageDead(_7);
75107
- StorageDead(_2);
76108
+ nop;
109+
+ nop;
77110
+ nop;
78111
return;
79112
}

tests/mir-opt/gvn.aggregate_struct_then_transmute.GVN.panic-unwind.diff

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414
let _10: ();
1515
let mut _11: u16;
1616
let mut _12: TypedId<std::string::String>;
17+
let mut _14: u16;
18+
let _15: ();
19+
let mut _16: u16;
20+
let mut _17: std::result::Result<aggregate_struct_then_transmute::Never, u16>;
1721
scope 1 {
1822
debug a => _2;
1923
let _7: TypedId<std::string::String>;
2024
scope 2 {
2125
debug b => _7;
26+
let _13: std::result::Result<aggregate_struct_then_transmute::Never, u16>;
27+
scope 3 {
28+
debug c => _13;
29+
}
2230
}
2331
}
2432

@@ -62,18 +70,43 @@
6270
- _12 = move _7;
6371
- _11 = move _12 as u16 (Transmute);
6472
+ _12 = copy _7;
65-
+ _11 = copy _7 as u16 (Transmute);
73+
+ _11 = copy _1;
6674
StorageDead(_12);
67-
_10 = opaque::<u16>(move _11) -> [return: bb2, unwind continue];
75+
- _10 = opaque::<u16>(move _11) -> [return: bb2, unwind continue];
76+
+ _10 = opaque::<u16>(copy _1) -> [return: bb2, unwind continue];
6877
}
6978

7079
bb2: {
7180
StorageDead(_11);
7281
StorageDead(_10);
82+
- StorageLive(_13);
83+
+ nop;
84+
StorageLive(_14);
85+
_14 = copy _1;
86+
- _13 = Result::<Never, u16>::Err(move _14);
87+
+ _13 = Result::<Never, u16>::Err(copy _1);
88+
StorageDead(_14);
89+
StorageLive(_15);
90+
StorageLive(_16);
91+
StorageLive(_17);
92+
- _17 = move _13;
93+
- _16 = move _17 as u16 (Transmute);
94+
+ _17 = copy _13;
95+
+ _16 = copy _1;
96+
StorageDead(_17);
97+
- _15 = opaque::<u16>(move _16) -> [return: bb3, unwind continue];
98+
+ _15 = opaque::<u16>(copy _1) -> [return: bb3, unwind continue];
99+
}
100+
101+
bb3: {
102+
StorageDead(_16);
103+
StorageDead(_15);
73104
_0 = const ();
105+
- StorageDead(_13);
74106
- StorageDead(_7);
75107
- StorageDead(_2);
76108
+ nop;
109+
+ nop;
77110
+ nop;
78111
return;
79112
}

tests/mir-opt/gvn.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -919,13 +919,15 @@ unsafe fn aggregate_struct_then_transmute(id: u16) {
919919
let a = MyId(id);
920920
opaque(std::intrinsics::transmute::<_, u16>(a));
921921

922-
// GVN can't do this yet because it doesn't know which field is the ZST,
923-
// but future changes might enable it.
924-
// CHECK: [[AGG:_.+]] = TypedId::<String>(copy _1, const PhantomData::<String>);
925-
// CHECK: [[INT:_.+]] = copy [[AGG]] as u16 (Transmute);
926-
// CHECK: opaque::<u16>(move [[INT]])
922+
// CHECK: opaque::<u16>(copy _1)
927923
let b = TypedId::<String>(id, PhantomData);
928924
opaque(std::intrinsics::transmute::<_, u16>(b));
925+
926+
// CHECK: opaque::<u16>(copy _1)
927+
let c = Err::<Never, u16>(id);
928+
opaque(std::intrinsics::transmute::<_, u16>(c));
929+
930+
enum Never {}
929931
}
930932

931933
// Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast.

0 commit comments

Comments
 (0)