Skip to content

Commit e32ea48

Browse files
committed
Auto merge of #126541 - scottmcm:more-ptr-metadata-gvn, r=cjgillot
More ptr metadata gvn There's basically 3 parts to this PR. 1. Allow references as arguments to `UnOp::PtrMetadata` This is a MIR semantics addition, so r? mir Rather than just raw pointers, also allow references to be passed to `PtrMetadata`. That means the length of a slice can be just `PtrMetadata(_1)` instead of also needing a ref-to-pointer statement (`_2 = &raw *_1` + `PtrMetadata(_2)`). AFAIK there should be no provenance or tagging implications of looking at the *metadata* of a pointer, and the code in the backends actually already supported it (other than a debug assert, given that they don't care about ptr vs reference, really), so we might as well allow it. 2. Simplify the argument to `PtrMetadata` in GVN Because the specific kind of pointer-like thing isn't that important, GVN can simplify all those details away. Things like `*const`-to-`*mut` casts and `&mut`-to-`&` reborrows are irrelevant, and skipping them lets it see more interesting things. cc `@cjgillot` Notably, unsizing casts for arrays. GVN supported that for `Len`, and now it sees it for `PtrMetadata` as well, allowing `PtrMetadata(pointer)` to become a constant if that pointer came from an array-to-slice unsizing, even through a bunch of other possible steps. 3. Replace `NormalizeArrayLen` with GVN The `NormalizeArrayLen` pass hasn't been running even in optimized builds for well over a year, and it turns out that GVN -- which *is* on in optimized builds -- can do everything it was trying to do. So the code for the pass is deleted, but the tests are kept, just changed to the different pass. As part of this, `LowerSliceLen` was changed to emit `PtrMetadata(_1)` instead of `Len(*_1)`, a small step on the road to eventually eliminating `Rvalue::Len`.
2 parents 4e6de37 + 55d1337 commit e32ea48

File tree

51 files changed

+622
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+622
-323
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
639639
(OperandValue::Immediate(llval), operand.layout)
640640
}
641641
mir::UnOp::PtrMetadata => {
642-
debug_assert!(operand.layout.ty.is_unsafe_ptr());
642+
debug_assert!(
643+
operand.layout.ty.is_unsafe_ptr() || operand.layout.ty.is_ref(),
644+
);
643645
let (_, meta) = operand.val.pointer_parts();
644646
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
645647
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
@@ -1446,10 +1446,12 @@ pub enum UnOp {
14461446
Not,
14471447
/// The `-` operator for negation
14481448
Neg,
1449-
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
1449+
/// Gets the metadata `M` from a `*const`/`*mut`/`&`/`&mut` to
1450+
/// `impl Pointee<Metadata = M>`.
14501451
///
14511452
/// For example, this will give a `()` from `*const i32`, a `usize` from
1452-
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
1453+
/// `&mut [u8]`, or a `ptr::DynMetadata<dyn Foo>` (internally a pointer)
1454+
/// from a `*mut dyn Foo`.
14531455
///
14541456
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
14551457
PtrMetadata,

compiler/rustc_mir_build/src/build/custom/parse/instruction.rs

+4
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
193193
let source = self.parse_operand(args[0])?;
194194
Ok(Rvalue::Cast(CastKind::Transmute, source, expr.ty))
195195
},
196+
@call(mir_cast_ptr_to_ptr, args) => {
197+
let source = self.parse_operand(args[0])?;
198+
Ok(Rvalue::Cast(CastKind::PtrToPtr, source, expr.ty))
199+
},
196200
@call(mir_checked, args) => {
197201
parse_by_kind!(self, args[0], _, "binary op",
198202
ExprKind::Binary { op, lhs, rhs } => {

compiler/rustc_mir_transform/src/gvn.rs

+105-29
Original file line numberDiff line numberDiff line change
@@ -836,12 +836,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
836836
}
837837
Value::BinaryOp(op, lhs, rhs)
838838
}
839-
Rvalue::UnaryOp(op, ref mut arg) => {
840-
let arg = self.simplify_operand(arg, location)?;
841-
if let Some(value) = self.simplify_unary(op, arg) {
842-
return Some(value);
843-
}
844-
Value::UnaryOp(op, arg)
839+
Rvalue::UnaryOp(op, ref mut arg_op) => {
840+
return self.simplify_unary(op, arg_op, location);
845841
}
846842
Rvalue::Discriminant(ref mut place) => {
847843
let place = self.simplify_place_value(place, location)?;
@@ -949,13 +945,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
949945
was_updated = true;
950946
}
951947

952-
if was_updated {
953-
if let Some(const_) = self.try_as_constant(fields[0]) {
954-
field_ops[FieldIdx::ZERO] = Operand::Constant(Box::new(const_));
955-
} else if let Some(local) = self.try_as_local(fields[0], location) {
956-
field_ops[FieldIdx::ZERO] = Operand::Copy(Place::from(local));
957-
self.reused_locals.insert(local);
958-
}
948+
if was_updated && let Some(op) = self.try_as_operand(fields[0], location) {
949+
field_ops[FieldIdx::ZERO] = op;
959950
}
960951
}
961952

@@ -965,11 +956,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
965956
let first = fields[0];
966957
if fields.iter().all(|&v| v == first) {
967958
let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
968-
if let Some(const_) = self.try_as_constant(first) {
969-
*rvalue = Rvalue::Repeat(Operand::Constant(Box::new(const_)), len);
970-
} else if let Some(local) = self.try_as_local(first, location) {
971-
*rvalue = Rvalue::Repeat(Operand::Copy(local.into()), len);
972-
self.reused_locals.insert(local);
959+
if let Some(op) = self.try_as_operand(first, location) {
960+
*rvalue = Rvalue::Repeat(op, len);
973961
}
974962
return Some(self.insert(Value::Repeat(first, len)));
975963
}
@@ -979,8 +967,71 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
979967
}
980968

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

@@ -1174,13 +1242,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
11741242
}
11751243
}
11761244

1177-
if was_updated {
1178-
if let Some(const_) = self.try_as_constant(value) {
1179-
*operand = Operand::Constant(Box::new(const_));
1180-
} else if let Some(local) = self.try_as_local(value, location) {
1181-
*operand = Operand::Copy(local.into());
1182-
self.reused_locals.insert(local);
1183-
}
1245+
if was_updated && let Some(op) = self.try_as_operand(value, location) {
1246+
*operand = op;
11841247
}
11851248

11861249
Some(self.insert(Value::Cast { kind: *kind, value, from, to }))
@@ -1296,6 +1359,19 @@ fn op_to_prop_const<'tcx>(
12961359
}
12971360

12981361
impl<'tcx> VnState<'_, 'tcx> {
1362+
/// If either [`Self::try_as_constant`] as [`Self::try_as_local`] succeeds,
1363+
/// returns that result as an [`Operand`].
1364+
fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1365+
if let Some(const_) = self.try_as_constant(index) {
1366+
Some(Operand::Constant(Box::new(const_)))
1367+
} else if let Some(local) = self.try_as_local(index, location) {
1368+
self.reused_locals.insert(local);
1369+
Some(Operand::Copy(local.into()))
1370+
} else {
1371+
None
1372+
}
1373+
}
1374+
12991375
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
13001376
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
13011377
// This was already constant in MIR, do not change it.

compiler/rustc_mir_transform/src/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ mod lower_slice_len;
8888
mod match_branches;
8989
mod mentioned_items;
9090
mod multiple_return_terminators;
91-
mod normalize_array_len;
9291
mod nrvo;
9392
mod prettify;
9493
mod promote_consts;
@@ -581,9 +580,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
581580
&o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching),
582581
// Inlining may have introduced a lot of redundant code and a large move pattern.
583582
// Now, we need to shrink the generated MIR.
584-
585-
// Has to run after `slice::len` lowering
586-
&normalize_array_len::NormalizeArrayLen,
587583
&ref_prop::ReferencePropagation,
588584
&sroa::ScalarReplacementOfAggregates,
589585
&match_branches::MatchBranchSimplification,

compiler/rustc_mir_transform/src/lower_slice_len.rs

+8-16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
//! This pass lowers calls to core::slice::len to just Len op.
1+
//! This pass lowers calls to core::slice::len to just PtrMetadata op.
22
//! It should run before inlining!
33
44
use rustc_hir::def_id::DefId;
5-
use rustc_index::IndexSlice;
65
use rustc_middle::mir::*;
7-
use rustc_middle::ty::{self, TyCtxt};
6+
use rustc_middle::ty::TyCtxt;
87

98
pub struct LowerSliceLenCalls;
109

@@ -29,16 +28,11 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
2928
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
3029
for block in basic_blocks {
3130
// lower `<[_]>::len` calls
32-
lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id);
31+
lower_slice_len_call(block, slice_len_fn_item_def_id);
3332
}
3433
}
3534

36-
fn lower_slice_len_call<'tcx>(
37-
tcx: TyCtxt<'tcx>,
38-
block: &mut BasicBlockData<'tcx>,
39-
local_decls: &IndexSlice<Local, LocalDecl<'tcx>>,
40-
slice_len_fn_item_def_id: DefId,
41-
) {
35+
fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_item_def_id: DefId) {
4236
let terminator = block.terminator();
4337
if let TerminatorKind::Call {
4438
func,
@@ -50,19 +44,17 @@ fn lower_slice_len_call<'tcx>(
5044
} = &terminator.kind
5145
// some heuristics for fast rejection
5246
&& let [arg] = &args[..]
53-
&& let Some(arg) = arg.node.place()
54-
&& let ty::FnDef(fn_def_id, _) = func.ty(local_decls, tcx).kind()
55-
&& *fn_def_id == slice_len_fn_item_def_id
47+
&& let Some((fn_def_id, _)) = func.const_fn_def()
48+
&& fn_def_id == slice_len_fn_item_def_id
5649
{
5750
// perform modifications from something like:
5851
// _5 = core::slice::<impl [u8]>::len(move _6) -> bb1
5952
// into:
60-
// _5 = Len(*_6)
53+
// _5 = PtrMetadata(move _6)
6154
// goto bb1
6255

6356
// make new RValue for Len
64-
let deref_arg = tcx.mk_place_deref(arg);
65-
let r_value = Rvalue::Len(deref_arg);
57+
let r_value = Rvalue::UnaryOp(UnOp::PtrMetadata, arg.node.clone());
6658
let len_statement_kind = StatementKind::Assign(Box::new((*destination, r_value)));
6759
let add_statement =
6860
Statement { kind: len_statement_kind, source_info: terminator.source_info };

0 commit comments

Comments
 (0)