Skip to content

Commit 9088cd9

Browse files
committed
GVN away PtrToPtr-then-Transmute when possible
1 parent dd1e19e commit 9088cd9

6 files changed

+165
-35
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+43-13
Original file line numberDiff line numberDiff line change
@@ -978,12 +978,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
978978
// we can't always know exactly what the metadata are.
979979
// To allow things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`,
980980
// it's fine to get a projection as the type.
981-
// FIXME: Would it be worth trying to normalize, rather than
982-
// just accepting the projection? Or are the types in the
983-
// Cast realistically about as normalized as we can get anyway?
984981
Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
985-
if from.pointee_metadata_ty_or_projection(self.tcx)
986-
== to.pointee_metadata_ty_or_projection(self.tcx) =>
982+
if self.pointers_have_same_metadata(*from, *to) =>
987983
{
988984
arg_index = *inner;
989985
was_updated = true;
@@ -1054,7 +1050,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
10541050
rhs_operand: &mut Operand<'tcx>,
10551051
location: Location,
10561052
) -> Option<VnIndex> {
1057-
10581053
let lhs = self.simplify_operand(lhs_operand, location);
10591054
let rhs = self.simplify_operand(rhs_operand, location);
10601055
// Only short-circuit options after we called `simplify_operand`
@@ -1068,13 +1063,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
10681063
// types of both casts and the metadata all match.
10691064
if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op
10701065
&& lhs_ty.is_any_ptr()
1071-
&& let Value::Cast { kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, .. } =
1072-
self.get(lhs)
1073-
&& let Value::Cast { kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, .. } =
1074-
self.get(rhs)
1066+
&& let Value::Cast {
1067+
kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, ..
1068+
} = self.get(lhs)
1069+
&& let Value::Cast {
1070+
kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, ..
1071+
} = self.get(rhs)
10751072
&& lhs_from == rhs_from
1076-
&& lhs_from.pointee_metadata_ty_or_projection(self.tcx)
1077-
== lhs_ty.pointee_metadata_ty_or_projection(self.tcx)
1073+
&& self.pointers_have_same_metadata(*lhs_from, lhs_ty)
10781074
{
10791075
lhs = *lhs_value;
10801076
rhs = *rhs_value;
@@ -1254,14 +1250,33 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
12541250
}
12551251
}
12561252

1253+
// PtrToPtr-then-PtrToPtr can skip the intermediate step
12571254
if let PtrToPtr = kind
12581255
&& let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } =
12591256
*self.get(value)
12601257
&& let PtrToPtr = inner_kind
12611258
{
12621259
from = inner_from;
12631260
value = inner_value;
1264-
*kind = PtrToPtr;
1261+
was_updated = true;
1262+
if inner_from == to {
1263+
return Some(inner_value);
1264+
}
1265+
}
1266+
1267+
// PtrToPtr-then-Transmute can just transmute the original, so long as the
1268+
// PtrToPtr didn't change metadata (and thus the size of the pointer)
1269+
if let Transmute = kind
1270+
&& let Value::Cast {
1271+
kind: PtrToPtr,
1272+
value: inner_value,
1273+
from: inner_from,
1274+
to: inner_to,
1275+
} = *self.get(value)
1276+
&& self.pointers_have_same_metadata(inner_from, inner_to)
1277+
{
1278+
from = inner_from;
1279+
value = inner_value;
12651280
was_updated = true;
12661281
if inner_from == to {
12671282
return Some(inner_value);
@@ -1315,6 +1330,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
13151330
// Fallback: a symbolic `Len`.
13161331
Some(self.insert(Value::Len(inner)))
13171332
}
1333+
1334+
fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
1335+
let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
1336+
let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
1337+
if left_meta_ty == right_meta_ty {
1338+
true
1339+
} else if let Ok(left) =
1340+
self.tcx.try_normalize_erasing_regions(self.param_env, left_meta_ty)
1341+
&& let Ok(right) = self.tcx.try_normalize_erasing_regions(self.param_env, right_meta_ty)
1342+
{
1343+
left == right
1344+
} else {
1345+
false
1346+
}
1347+
}
13181348
}
13191349

13201350
fn op_to_prop_const<'tcx>(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
- // MIR for `cast_pointer_then_transmute` before GVN
2+
+ // MIR for `cast_pointer_then_transmute` after GVN
3+
4+
fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () {
5+
debug thin => _1;
6+
debug fat => _2;
7+
let mut _0: ();
8+
let _3: usize;
9+
let mut _4: *const ();
10+
let mut _5: *mut u32;
11+
let mut _7: *const ();
12+
let mut _8: *mut [u8];
13+
scope 1 {
14+
debug thin_addr => _3;
15+
let _6: usize;
16+
scope 2 {
17+
debug fat_addr => _6;
18+
}
19+
}
20+
21+
bb0: {
22+
StorageLive(_3);
23+
StorageLive(_4);
24+
StorageLive(_5);
25+
_5 = _1;
26+
- _4 = move _5 as *const () (PtrToPtr);
27+
+ _4 = _1 as *const () (PtrToPtr);
28+
StorageDead(_5);
29+
- _3 = move _4 as usize (Transmute);
30+
+ _3 = _1 as usize (Transmute);
31+
StorageDead(_4);
32+
StorageLive(_6);
33+
StorageLive(_7);
34+
StorageLive(_8);
35+
_8 = _2;
36+
- _7 = move _8 as *const () (PtrToPtr);
37+
+ _7 = _2 as *const () (PtrToPtr);
38+
StorageDead(_8);
39+
_6 = move _7 as usize (Transmute);
40+
StorageDead(_7);
41+
_0 = const ();
42+
StorageDead(_6);
43+
StorageDead(_3);
44+
return;
45+
}
46+
}
47+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
- // MIR for `cast_pointer_then_transmute` before GVN
2+
+ // MIR for `cast_pointer_then_transmute` after GVN
3+
4+
fn cast_pointer_then_transmute(_1: *mut u32, _2: *mut [u8]) -> () {
5+
debug thin => _1;
6+
debug fat => _2;
7+
let mut _0: ();
8+
let _3: usize;
9+
let mut _4: *const ();
10+
let mut _5: *mut u32;
11+
let mut _7: *const ();
12+
let mut _8: *mut [u8];
13+
scope 1 {
14+
debug thin_addr => _3;
15+
let _6: usize;
16+
scope 2 {
17+
debug fat_addr => _6;
18+
}
19+
}
20+
21+
bb0: {
22+
StorageLive(_3);
23+
StorageLive(_4);
24+
StorageLive(_5);
25+
_5 = _1;
26+
- _4 = move _5 as *const () (PtrToPtr);
27+
+ _4 = _1 as *const () (PtrToPtr);
28+
StorageDead(_5);
29+
- _3 = move _4 as usize (Transmute);
30+
+ _3 = _1 as usize (Transmute);
31+
StorageDead(_4);
32+
StorageLive(_6);
33+
StorageLive(_7);
34+
StorageLive(_8);
35+
_8 = _2;
36+
- _7 = move _8 as *const () (PtrToPtr);
37+
+ _7 = _2 as *const () (PtrToPtr);
38+
StorageDead(_8);
39+
_6 = move _7 as usize (Transmute);
40+
StorageDead(_7);
41+
_0 = const ();
42+
StorageDead(_6);
43+
StorageDead(_3);
44+
return;
45+
}
46+
}
47+

tests/mir-opt/gvn.rs

+14
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,19 @@ fn cast_pointer_eq(p1: *mut u8, p2: *mut u32, p3: *mut u32, p4: *mut [u32]) {
913913
// CHECK: _0 = const ();
914914
}
915915

916+
// Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast.
917+
unsafe fn cast_pointer_then_transmute(thin: *mut u32, fat: *mut [u8]) {
918+
// CHECK-LABEL: fn cast_pointer_then_transmute
919+
920+
// CHECK: [[UNUSED:_.+]] = _1 as *const () (PtrToPtr);
921+
// CHECK: = _1 as usize (Transmute);
922+
let thin_addr: usize = std::intrinsics::transmute(thin as *const ());
923+
924+
// CHECK: [[TEMP2:_.+]] = _2 as *const () (PtrToPtr);
925+
// CHECK: = move [[TEMP2]] as usize (Transmute);
926+
let fat_addr: usize = std::intrinsics::transmute(fat as *const ());
927+
}
928+
916929
fn main() {
917930
subexpression_elimination(2, 4, 5);
918931
wrap_unwrap(5);
@@ -981,3 +994,4 @@ fn identity<T>(x: T) -> T {
981994
// EMIT_MIR gvn.array_len.GVN.diff
982995
// EMIT_MIR gvn.generic_cast_metadata.GVN.diff
983996
// EMIT_MIR gvn.cast_pointer_eq.GVN.diff
997+
// EMIT_MIR gvn.cast_pointer_then_transmute.GVN.diff

tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-abort.mir

+7-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
99
let mut _8: *const T;
1010
scope 2 {
1111
let _4: std::ptr::NonNull<T>;
12-
let _10: usize;
12+
let _9: usize;
1313
scope 3 {
1414
}
1515
scope 4 {
@@ -24,7 +24,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
2424
}
2525
}
2626
scope 5 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
27-
let mut _9: *const ();
2827
scope 6 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
2928
}
3029
}
@@ -34,7 +33,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
3433
}
3534

3635
bb0: {
37-
StorageLive(_10);
36+
StorageLive(_9);
37+
StorageLive(_8);
3838
StorageLive(_4);
3939
switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
4040
}
@@ -61,20 +61,16 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
6161
}
6262

6363
bb2: {
64-
StorageLive(_8);
6564
_8 = ((*_1).1: *const T);
66-
StorageLive(_9);
67-
_9 = _8 as *const () (PtrToPtr);
68-
_10 = move _9 as usize (Transmute);
69-
StorageDead(_9);
70-
StorageDead(_8);
71-
_0 = Eq(_10, const 0_usize);
65+
_9 = _8 as usize (Transmute);
66+
_0 = Eq(_9, const 0_usize);
7267
goto -> bb3;
7368
}
7469

7570
bb3: {
7671
StorageDead(_4);
77-
StorageDead(_10);
72+
StorageDead(_8);
73+
StorageDead(_9);
7874
return;
7975
}
8076
}

tests/mir-opt/pre-codegen/slice_iter.slice_iter_generic_is_empty.PreCodegen.after.panic-unwind.mir

+7-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
99
let mut _8: *const T;
1010
scope 2 {
1111
let _4: std::ptr::NonNull<T>;
12-
let _10: usize;
12+
let _9: usize;
1313
scope 3 {
1414
}
1515
scope 4 {
@@ -24,7 +24,6 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
2424
}
2525
}
2626
scope 5 (inlined std::ptr::const_ptr::<impl *const T>::addr) {
27-
let mut _9: *const ();
2827
scope 6 (inlined std::ptr::const_ptr::<impl *const T>::cast::<()>) {
2928
}
3029
}
@@ -34,7 +33,8 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
3433
}
3534

3635
bb0: {
37-
StorageLive(_10);
36+
StorageLive(_9);
37+
StorageLive(_8);
3838
StorageLive(_4);
3939
switchInt(const <T as std::mem::SizedTypeProperties>::IS_ZST) -> [0: bb1, otherwise: bb2];
4040
}
@@ -61,20 +61,16 @@ fn slice_iter_generic_is_empty(_1: &std::slice::Iter<'_, T>) -> bool {
6161
}
6262

6363
bb2: {
64-
StorageLive(_8);
6564
_8 = ((*_1).1: *const T);
66-
StorageLive(_9);
67-
_9 = _8 as *const () (PtrToPtr);
68-
_10 = move _9 as usize (Transmute);
69-
StorageDead(_9);
70-
StorageDead(_8);
71-
_0 = Eq(_10, const 0_usize);
65+
_9 = _8 as usize (Transmute);
66+
_0 = Eq(_9, const 0_usize);
7267
goto -> bb3;
7368
}
7469

7570
bb3: {
7671
StorageDead(_4);
77-
StorageDead(_10);
72+
StorageDead(_8);
73+
StorageDead(_9);
7874
return;
7975
}
8076
}

0 commit comments

Comments
 (0)