Skip to content

Commit 7562d20

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 7562d20

14 files changed

+319
-78
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 3 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 4 additions & 2 deletions
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

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -840,12 +840,8 @@ 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)?;
845-
if let Some(value) = self.simplify_unary(op, arg) {
846-
return Some(value);
847-
}
848-
Value::UnaryOp(op, arg)
843+
Rvalue::UnaryOp(op, ref mut arg_op) => {
844+
return self.simplify_unary(op, arg_op, location);
849845
}
850846
Rvalue::Discriminant(ref mut place) => {
851847
let place = self.simplify_place_value(place, location)?;
@@ -975,8 +971,71 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
975971
}
976972

977973
#[instrument(level = "trace", skip(self), ret)]
978-
fn simplify_unary(&mut self, op: UnOp, value: VnIndex) -> Option<VnIndex> {
979-
let value = match (op, self.get(value)) {
974+
fn simplify_unary(
975+
&mut self,
976+
op: UnOp,
977+
arg_op: &mut Operand<'tcx>,
978+
location: Location,
979+
) -> Option<VnIndex> {
980+
let mut arg_index = self.simplify_operand(arg_op, location)?;
981+
982+
// PtrMetadata doesn't care about *const vs *mut vs & vs &mut,
983+
// so start by removing those distinctions so we can update the `Operand`
984+
if op == UnOp::PtrMetadata {
985+
let mut was_updated = false;
986+
loop {
987+
match self.get(arg_index) {
988+
// Pointer casts that preserve metadata, such as
989+
// `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`.
990+
// It's critical that this not eliminate cases like
991+
// `*const [T]` -> `*const T` which remove metadata.
992+
// We run on potentially-generic MIR, though, so unlike codegen
993+
// we can't always know exactly what the metadata are.
994+
// Thankfully, equality on `ptr_metadata_ty_or_tail` gives us
995+
// what we need: `Ok(meta_ty)` if the metadata is known, or
996+
// `Err(tail_ty)` if not. Matching metadata is ok, but if
997+
// that's not known, then matching tail types is also ok,
998+
// allowing things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`.
999+
// FIXME: remove MutToConst after #126308
1000+
Value::Cast {
1001+
kind:
1002+
CastKind::PtrToPtr
1003+
| CastKind::PointerCoercion(
1004+
ty::adjustment::PointerCoercion::MutToConstPointer,
1005+
),
1006+
value: inner,
1007+
from,
1008+
to,
1009+
} if from.builtin_deref(true)?.ptr_metadata_ty_or_tail(self.tcx, |t| t)
1010+
== to.builtin_deref(true)?.ptr_metadata_ty_or_tail(self.tcx, |t| t) =>
1011+
{
1012+
arg_index = *inner;
1013+
was_updated = true;
1014+
continue;
1015+
}
1016+
1017+
// `&mut *p`, `&raw *p`, etc don't change metadata.
1018+
Value::Address { place, kind: _, provenance: _ }
1019+
if let PlaceRef { local, projection: [PlaceElem::Deref] } =
1020+
place.as_ref()
1021+
&& let Some(local_index) = self.locals[local] =>
1022+
{
1023+
arg_index = local_index;
1024+
was_updated = true;
1025+
continue;
1026+
}
1027+
1028+
_ => {
1029+
if was_updated && let Some(op) = self.try_as_operand(arg_index, location) {
1030+
*arg_op = op;
1031+
}
1032+
break;
1033+
}
1034+
}
1035+
}
1036+
}
1037+
1038+
let value = match (op, self.get(arg_index)) {
9801039
(UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner),
9811040
(UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner),
9821041
(UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => {
@@ -988,9 +1047,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
9881047
(UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
9891048
return Some(fields[1]);
9901049
}
991-
_ => return None,
1050+
// We have an unsizing cast, which assigns the length to fat pointer metadata.
1051+
(
1052+
UnOp::PtrMetadata,
1053+
Value::Cast {
1054+
kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
1055+
from,
1056+
to,
1057+
..
1058+
},
1059+
) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
1060+
&& let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
1061+
{
1062+
return self.insert_constant(Const::from_ty_const(
1063+
*len,
1064+
self.tcx.types.usize,
1065+
self.tcx,
1066+
));
1067+
}
1068+
_ => Value::UnaryOp(op, arg_index),
9921069
};
993-
9941070
Some(self.insert(value))
9951071
}
9961072

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 7 additions & 2 deletions
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
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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: &[i32];
8+
let mut _3: &[i32; 42];
9+
let mut _4: *const [i32];
10+
scope 1 {
11+
debug x => _2;
12+
}
13+
14+
bb0: {
15+
- StorageLive(_2);
16+
+ nop;
17+
StorageLive(_3);
18+
_3 = &(*_1);
19+
_2 = move _3 as &[i32] (PointerCoercion(Unsize));
20+
StorageDead(_3);
21+
StorageLive(_4);
22+
_4 = &raw const (*_2);
23+
- _0 = PtrMetadata(move _4);
24+
+ _0 = const 42_usize;
25+
StorageDead(_4);
26+
- StorageDead(_2);
27+
+ nop;
28+
return;
29+
}
30+
}
31+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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: &[i32];
8+
let mut _3: &[i32; 42];
9+
let mut _4: *const [i32];
10+
scope 1 {
11+
debug x => _2;
12+
}
13+
14+
bb0: {
15+
- StorageLive(_2);
16+
+ nop;
17+
StorageLive(_3);
18+
_3 = &(*_1);
19+
_2 = move _3 as &[i32] (PointerCoercion(Unsize));
20+
StorageDead(_3);
21+
StorageLive(_4);
22+
_4 = &raw const (*_2);
23+
- _0 = PtrMetadata(move _4);
24+
+ _0 = const 42_usize;
25+
StorageDead(_4);
26+
- StorageDead(_2);
27+
+ nop;
28+
return;
29+
}
30+
}
31+

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

Lines changed: 9 additions & 9 deletions
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

Lines changed: 9 additions & 9 deletions
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);
Lines changed: 41 additions & 0 deletions
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)