Skip to content

Commit aa1ee42

Browse files
committed
More GVN for PtrMetadata
`PtrMetadata` doesn't care about `*const`/`*mut`/`&`/`&mut`, so GVN away those casts in its argument. This includes updating MIR to allow calling PtrMetadata on references too, not just raw pointers. That means that `[T]::len` can be just `_0 = PtrMetadata(_1)`, for example.
1 parent 54242b1 commit aa1ee42

14 files changed

+306
-70
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
638638
(OperandValue::Immediate(llval), operand.layout)
639639
}
640640
mir::UnOp::PtrMetadata => {
641-
debug_assert!(operand.layout.ty.is_unsafe_ptr());
641+
debug_assert!(
642+
operand.layout.ty.is_unsafe_ptr() || operand.layout.ty.is_ref(),
643+
);
642644
let (_, meta) = operand.val.pointer_parts();
643645
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
644646
if let Some(meta) = meta {

compiler/rustc_const_eval/src/interpret/operator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
460460
let res = ScalarInt::truncate_from_uint(res, layout.size).0;
461461
Ok(ImmTy::from_scalar(res.into(), layout))
462462
}
463-
ty::RawPtr(..) => {
463+
ty::RawPtr(..) | ty::Ref(..) => {
464464
assert_eq!(un_op, PtrMetadata);
465465
let (_, meta) = val.to_scalar_and_meta();
466466
Ok(match meta {

compiler/rustc_middle/src/mir/syntax.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1434,10 +1434,12 @@ pub enum UnOp {
14341434
Not,
14351435
/// The `-` operator for negation
14361436
Neg,
1437-
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
1437+
/// Gets the metadata `M` from a `*const`/`*mut`/`&`/`&mut` to
1438+
/// `impl Pointee<Metadata = M>`.
14381439
///
14391440
/// For example, this will give a `()` from `*const i32`, a `usize` from
1440-
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
1441+
/// `&mut [u8]`, or a `ptr::DynMetadata<dyn Foo>` (internally a pointer)
1442+
/// from a `*mut dyn Foo`.
14411443
///
14421444
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
14431445
PtrMetadata,

compiler/rustc_mir_transform/src/gvn.rs

+63-2
Original file line numberDiff line numberDiff line change
@@ -840,8 +840,51 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
840840
}
841841
Value::BinaryOp(op, lhs, rhs)
842842
}
843-
Rvalue::UnaryOp(op, ref mut arg) => {
844-
let arg = self.simplify_operand(arg, location)?;
843+
Rvalue::UnaryOp(op, ref mut arg_op) => {
844+
let mut arg = self.simplify_operand(arg_op, location)?;
845+
846+
// PtrMetadata doesn't care about *const vs *mut vs & vs &mut,
847+
// so start by removing those distinctions so we can update the `Operand`
848+
if op == UnOp::PtrMetadata {
849+
let mut was_updated = false;
850+
loop {
851+
let value = self.get(arg);
852+
853+
// FIXME: remove MutToConst after #126308
854+
if let Value::Cast {
855+
kind:
856+
CastKind::PtrToPtr
857+
| CastKind::PointerCoercion(
858+
ty::adjustment::PointerCoercion::MutToConstPointer,
859+
),
860+
value: inner,
861+
from,
862+
to,
863+
} = value
864+
&& from.builtin_deref(true) == to.builtin_deref(true)
865+
{
866+
arg = *inner;
867+
was_updated = true;
868+
continue;
869+
}
870+
871+
if let Value::Address { place, kind: _, provenance: _ } = value
872+
&& let PlaceRef { local, projection: [PlaceElem::Deref] } =
873+
place.as_ref()
874+
&& let Some(local_index) = self.locals[local]
875+
{
876+
arg = local_index;
877+
was_updated = true;
878+
continue;
879+
}
880+
881+
if was_updated && let Some(op) = self.try_as_operand(arg, location) {
882+
*arg_op = op;
883+
}
884+
break;
885+
}
886+
}
887+
845888
if let Some(value) = self.simplify_unary(op, arg) {
846889
return Some(value);
847890
}
@@ -988,6 +1031,24 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
9881031
(UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
9891032
return Some(fields[1]);
9901033
}
1034+
// We have an unsizing cast, which assigns the length to fat pointer metadata.
1035+
(
1036+
UnOp::PtrMetadata,
1037+
Value::Cast {
1038+
kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
1039+
from,
1040+
to,
1041+
..
1042+
},
1043+
) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
1044+
&& let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
1045+
{
1046+
return self.insert_constant(Const::from_ty_const(
1047+
*len,
1048+
self.tcx.types.usize,
1049+
self.tcx,
1050+
));
1051+
}
9911052
_ => return None,
9921053
};
9931054

compiler/rustc_mir_transform/src/validate.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1116,12 +1116,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11161116
UnOp::PtrMetadata => {
11171117
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
11181118
// It would probably be fine to support this in earlier phases,
1119-
// but at the time of writing it's only ever introduced from intrinsic lowering,
1119+
// but at the time of writing it's only ever introduced from intrinsic lowering
1120+
// or other runtime-phase optimization passes,
11201121
// so earlier things can just `bug!` on it.
11211122
self.fail(location, "PtrMetadata should be in runtime MIR only");
11221123
}
11231124

1124-
check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..));
1125+
check_kinds!(
1126+
a,
1127+
"Cannot PtrMetadata non-pointer non-reference type {:?}",
1128+
ty::RawPtr(..) | ty::Ref(..)
1129+
);
11251130
}
11261131
}
11271132
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
- // MIR for `array_len` before GVN
2+
+ // MIR for `array_len` after GVN
3+
4+
fn array_len(_1: &mut [i32; 42]) -> usize {
5+
debug x => _1;
6+
let mut _0: usize;
7+
let _2: *mut [i32];
8+
let mut _3: *mut [i32; 42];
9+
let mut _4: *const [i32];
10+
let mut _5: *mut [i32];
11+
scope 1 {
12+
debug x => _2;
13+
}
14+
15+
bb0: {
16+
- StorageLive(_2);
17+
+ nop;
18+
StorageLive(_3);
19+
_3 = &raw mut (*_1);
20+
_2 = move _3 as *mut [i32] (PointerCoercion(Unsize));
21+
StorageDead(_3);
22+
StorageLive(_4);
23+
StorageLive(_5);
24+
_5 = _2;
25+
- _4 = move _5 as *const [i32] (PointerCoercion(MutToConstPointer));
26+
+ _4 = _2 as *const [i32] (PointerCoercion(MutToConstPointer));
27+
StorageDead(_5);
28+
- _0 = PtrMetadata(move _4);
29+
+ _0 = const 42_usize;
30+
StorageDead(_4);
31+
- StorageDead(_2);
32+
+ nop;
33+
return;
34+
}
35+
}
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
- // MIR for `array_len` before GVN
2+
+ // MIR for `array_len` after GVN
3+
4+
fn array_len(_1: &mut [i32; 42]) -> usize {
5+
debug x => _1;
6+
let mut _0: usize;
7+
let _2: *mut [i32];
8+
let mut _3: *mut [i32; 42];
9+
let mut _4: *const [i32];
10+
let mut _5: *mut [i32];
11+
scope 1 {
12+
debug x => _2;
13+
}
14+
15+
bb0: {
16+
- StorageLive(_2);
17+
+ nop;
18+
StorageLive(_3);
19+
_3 = &raw mut (*_1);
20+
_2 = move _3 as *mut [i32] (PointerCoercion(Unsize));
21+
StorageDead(_3);
22+
StorageLive(_4);
23+
StorageLive(_5);
24+
_5 = _2;
25+
- _4 = move _5 as *const [i32] (PointerCoercion(MutToConstPointer));
26+
+ _4 = _2 as *const [i32] (PointerCoercion(MutToConstPointer));
27+
StorageDead(_5);
28+
- _0 = PtrMetadata(move _4);
29+
+ _0 = const 42_usize;
30+
StorageDead(_4);
31+
- StorageDead(_2);
32+
+ nop;
33+
return;
34+
}
35+
}
36+

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

+9-9
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88
let mut _3: fn(u8) -> u8;
99
let _5: ();
1010
let mut _6: fn(u8) -> u8;
11-
let mut _9: {closure@$DIR/gvn.rs:612:19: 612:21};
11+
let mut _9: {closure@$DIR/gvn.rs:614:19: 614:21};
1212
let _10: ();
1313
let mut _11: fn();
14-
let mut _13: {closure@$DIR/gvn.rs:612:19: 612:21};
14+
let mut _13: {closure@$DIR/gvn.rs:614:19: 614:21};
1515
let _14: ();
1616
let mut _15: fn();
1717
scope 1 {
1818
debug f => _1;
1919
let _4: fn(u8) -> u8;
2020
scope 2 {
2121
debug g => _4;
22-
let _7: {closure@$DIR/gvn.rs:612:19: 612:21};
22+
let _7: {closure@$DIR/gvn.rs:614:19: 614:21};
2323
scope 3 {
2424
debug closure => _7;
2525
let _8: fn();
@@ -62,16 +62,16 @@
6262
StorageDead(_6);
6363
StorageDead(_5);
6464
- StorageLive(_7);
65-
- _7 = {closure@$DIR/gvn.rs:612:19: 612:21};
65+
- _7 = {closure@$DIR/gvn.rs:614:19: 614:21};
6666
- StorageLive(_8);
6767
+ nop;
68-
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
68+
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
6969
+ nop;
7070
StorageLive(_9);
7171
- _9 = _7;
7272
- _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe)));
73-
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
74-
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
73+
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
74+
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
7575
StorageDead(_9);
7676
StorageLive(_10);
7777
StorageLive(_11);
@@ -88,8 +88,8 @@
8888
StorageLive(_13);
8989
- _13 = _7;
9090
- _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe)));
91-
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
92-
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
91+
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
92+
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
9393
StorageDead(_13);
9494
StorageLive(_14);
9595
StorageLive(_15);

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

+9-9
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88
let mut _3: fn(u8) -> u8;
99
let _5: ();
1010
let mut _6: fn(u8) -> u8;
11-
let mut _9: {closure@$DIR/gvn.rs:612:19: 612:21};
11+
let mut _9: {closure@$DIR/gvn.rs:614:19: 614:21};
1212
let _10: ();
1313
let mut _11: fn();
14-
let mut _13: {closure@$DIR/gvn.rs:612:19: 612:21};
14+
let mut _13: {closure@$DIR/gvn.rs:614:19: 614:21};
1515
let _14: ();
1616
let mut _15: fn();
1717
scope 1 {
1818
debug f => _1;
1919
let _4: fn(u8) -> u8;
2020
scope 2 {
2121
debug g => _4;
22-
let _7: {closure@$DIR/gvn.rs:612:19: 612:21};
22+
let _7: {closure@$DIR/gvn.rs:614:19: 614:21};
2323
scope 3 {
2424
debug closure => _7;
2525
let _8: fn();
@@ -62,16 +62,16 @@
6262
StorageDead(_6);
6363
StorageDead(_5);
6464
- StorageLive(_7);
65-
- _7 = {closure@$DIR/gvn.rs:612:19: 612:21};
65+
- _7 = {closure@$DIR/gvn.rs:614:19: 614:21};
6666
- StorageLive(_8);
6767
+ nop;
68-
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
68+
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
6969
+ nop;
7070
StorageLive(_9);
7171
- _9 = _7;
7272
- _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe)));
73-
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
74-
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
73+
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
74+
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
7575
StorageDead(_9);
7676
StorageLive(_10);
7777
StorageLive(_11);
@@ -88,8 +88,8 @@
8888
StorageLive(_13);
8989
- _13 = _7;
9090
- _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe)));
91-
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21};
92-
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:612:19: 612:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
91+
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21};
92+
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:614:19: 614:21} as fn() (PointerCoercion(ClosureFnPointer(Safe)));
9393
StorageDead(_13);
9494
StorageLive(_14);
9595
StorageLive(_15);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
- // MIR for `manual_slice_mut_len` before GVN
2+
+ // MIR for `manual_slice_mut_len` after GVN
3+
4+
fn manual_slice_mut_len(_1: &mut [i32]) -> usize {
5+
debug x => _1;
6+
let mut _0: usize;
7+
let _2: *mut [i32];
8+
let mut _4: *mut [i32];
9+
let mut _5: *const [i32];
10+
scope 1 {
11+
debug x => _2;
12+
let _3: *const [i32];
13+
scope 2 {
14+
debug x => _3;
15+
}
16+
}
17+
18+
bb0: {
19+
- StorageLive(_2);
20+
+ nop;
21+
_2 = &raw mut (*_1);
22+
- StorageLive(_3);
23+
+ nop;
24+
StorageLive(_4);
25+
_4 = _2;
26+
- _3 = move _4 as *const [i32] (PointerCoercion(MutToConstPointer));
27+
+ _3 = _2 as *const [i32] (PointerCoercion(MutToConstPointer));
28+
StorageDead(_4);
29+
StorageLive(_5);
30+
_5 = _3;
31+
- _0 = PtrMetadata(move _5);
32+
+ _0 = PtrMetadata(_1);
33+
StorageDead(_5);
34+
- StorageDead(_3);
35+
- StorageDead(_2);
36+
+ nop;
37+
+ nop;
38+
return;
39+
}
40+
}
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
- // MIR for `manual_slice_mut_len` before GVN
2+
+ // MIR for `manual_slice_mut_len` after GVN
3+
4+
fn manual_slice_mut_len(_1: &mut [i32]) -> usize {
5+
debug x => _1;
6+
let mut _0: usize;
7+
let _2: *mut [i32];
8+
let mut _4: *mut [i32];
9+
let mut _5: *const [i32];
10+
scope 1 {
11+
debug x => _2;
12+
let _3: *const [i32];
13+
scope 2 {
14+
debug x => _3;
15+
}
16+
}
17+
18+
bb0: {
19+
- StorageLive(_2);
20+
+ nop;
21+
_2 = &raw mut (*_1);
22+
- StorageLive(_3);
23+
+ nop;
24+
StorageLive(_4);
25+
_4 = _2;
26+
- _3 = move _4 as *const [i32] (PointerCoercion(MutToConstPointer));
27+
+ _3 = _2 as *const [i32] (PointerCoercion(MutToConstPointer));
28+
StorageDead(_4);
29+
StorageLive(_5);
30+
_5 = _3;
31+
- _0 = PtrMetadata(move _5);
32+
+ _0 = PtrMetadata(_1);
33+
StorageDead(_5);
34+
- StorageDead(_3);
35+
- StorageDead(_2);
36+
+ nop;
37+
+ nop;
38+
return;
39+
}
40+
}
41+

0 commit comments

Comments
 (0)