about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform')
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs47
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs38
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs28
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs77
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs15
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lint.rs44
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs234
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs4
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs8
10 files changed, 97 insertions, 402 deletions
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 4603c695ded..08316aaed3b 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1340,14 +1340,13 @@ fn create_cases<'tcx>(
                     }
                 }
 
-                if operation == Operation::Resume {
+                if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() {
                     // Move the resume argument to the destination place of the `Yield` terminator
-                    let resume_arg = CTX_ARG;
                     statements.push(Statement::new(
                         source_info,
                         StatementKind::Assign(Box::new((
                             point.resume_arg,
-                            Rvalue::Use(Operand::Move(resume_arg.into())),
+                            Rvalue::Use(Operand::Move(CTX_ARG.into())),
                         ))),
                     ));
                 }
@@ -1439,7 +1438,10 @@ fn check_field_tys_sized<'tcx>(
 }
 
 impl<'tcx> crate::MirPass<'tcx> for StateTransform {
+    #[instrument(level = "debug", skip(self, tcx, body), ret)]
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        debug!(def_id = ?body.source.def_id());
+
         let Some(old_yield_ty) = body.yield_ty() else {
             // This only applies to coroutines
             return;
@@ -1518,31 +1520,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
             cleanup_async_drops(body);
         }
 
-        // We also replace the resume argument and insert an `Assign`.
-        // This is needed because the resume argument `_2` might be live across a `yield`, in which
-        // case there is no `Assign` to it that the transform can turn into a store to the coroutine
-        // state. After the yield the slot in the coroutine state would then be uninitialized.
-        let resume_local = CTX_ARG;
-        let resume_ty = body.local_decls[resume_local].ty;
-        let old_resume_local = replace_local(resume_local, resume_ty, body, tcx);
-
-        // When first entering the coroutine, move the resume argument into its old local
-        // (which is now a generator interior).
-        let source_info = SourceInfo::outermost(body.span);
-        let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements;
-        stmts.insert(
-            0,
-            Statement::new(
-                source_info,
-                StatementKind::Assign(Box::new((
-                    old_resume_local.into(),
-                    Rvalue::Use(Operand::Move(resume_local.into())),
-                ))),
-            ),
-        );
-
         let always_live_locals = always_storage_live_locals(body);
-
         let movable = coroutine_kind.movability() == hir::Movability::Movable;
         let liveness_info =
             locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
@@ -1583,6 +1561,21 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
         };
         transform.visit_body(body);
 
+        // MIR parameters are not explicitly assigned-to when entering the MIR body.
+        // If we want to save their values inside the coroutine state, we need to do so explicitly.
+        let source_info = SourceInfo::outermost(body.span);
+        let args_iter = body.args_iter();
+        body.basic_blocks.as_mut()[START_BLOCK].statements.splice(
+            0..0,
+            args_iter.filter_map(|local| {
+                let (ty, variant_index, idx) = transform.remap[local]?;
+                let lhs = transform.make_field(variant_index, idx, ty);
+                let rhs = Rvalue::Use(Operand::Move(local.into()));
+                let assign = StatementKind::Assign(Box::new((lhs, rhs)));
+                Some(Statement::new(source_info, assign))
+            }),
+        );
+
         // Update our MIR struct to reflect the changes we've made
         body.arg_count = 2; // self, resume arg
         body.spread_arg = None;
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index fe53de31f75..5c984984d3c 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -412,18 +412,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
         state: &mut State<FlatSet<Scalar>>,
     ) -> ValueOrPlace<FlatSet<Scalar>> {
         let val = match rvalue {
-            Rvalue::Len(place) => {
-                let place_ty = place.ty(self.local_decls, self.tcx);
-                if let ty::Array(_, len) = place_ty.ty.kind() {
-                    Const::Ty(self.tcx.types.usize, *len)
-                        .try_eval_scalar(self.tcx, self.typing_env)
-                        .map_or(FlatSet::Top, FlatSet::Elem)
-                } else if let [ProjectionElem::Deref] = place.projection[..] {
-                    state.get_len(place.local.into(), &self.map)
-                } else {
-                    FlatSet::Top
-                }
-            }
             Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
                 let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
                     return ValueOrPlace::Value(FlatSet::Top);
@@ -465,15 +453,23 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
                 let (val, _overflow) = self.binary_op(state, *op, left, right);
                 val
             }
-            Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
-                FlatSet::Elem(value) => self
-                    .ecx
-                    .unary_op(*op, &value)
-                    .discard_err()
-                    .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)),
-                FlatSet::Bottom => FlatSet::Bottom,
-                FlatSet::Top => FlatSet::Top,
-            },
+            Rvalue::UnaryOp(op, operand) => {
+                if let UnOp::PtrMetadata = op
+                    && let Some(place) = operand.place()
+                    && let Some(len) = self.map.find_len(place.as_ref())
+                {
+                    return ValueOrPlace::Place(len);
+                }
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(value) => self
+                        .ecx
+                        .unary_op(*op, &value)
+                        .discard_err()
+                        .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
             Rvalue::NullaryOp(null_op, ty) => {
                 let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
                     return ValueOrPlace::Value(FlatSet::Top);
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index c57483a6811..74c22ff10c1 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -141,7 +141,7 @@ use rustc_data_structures::union_find::UnionFind;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_index::interval::SparseIntervalMatrix;
 use rustc_index::{IndexVec, newtype_index};
-use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals};
@@ -153,15 +153,7 @@ pub(super) struct DestinationPropagation;
 
 impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        // For now, only run at MIR opt level 3. Two things need to be changed before this can be
-        // turned on by default:
-        //  1. Because of the overeager removal of storage statements, this can cause stack space
-        //     regressions. This opt is not the place to fix this though, it's a more general
-        //     problem in MIR.
-        //  2. Despite being an overall perf improvement, this still causes a 30% regression in
-        //     keccak. We can temporarily fix this by bounding function size, but in the long term
-        //     we should fix this by being smarter about invalidating analysis results.
-        sess.mir_opt_level() >= 3
+        sess.mir_opt_level() >= 2
     }
 
     #[tracing::instrument(level = "trace", skip(self, tcx, body))]
@@ -511,22 +503,6 @@ impl TwoStepIndex {
     }
 }
 
-struct VisitPlacesWith<F>(F);
-
-impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F>
-where
-    F: FnMut(Place<'tcx>, PlaceContext),
-{
-    fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) {
-        (self.0)(local.into(), ctxt);
-    }
-
-    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) {
-        (self.0)(*place, ctxt);
-        self.visit_projection(place.as_ref(), ctxt, location);
-    }
-}
-
 /// Add points depending on the result of the given dataflow analysis.
 fn save_as_intervals<'tcx>(
     elements: &DenseLocationMap,
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index bf6aa800d20..30e68a3f650 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -200,8 +200,6 @@ enum Value<'tcx> {
     Projection(VnIndex, ProjectionElem<VnIndex, ()>),
     /// Discriminant of the given value.
     Discriminant(VnIndex),
-    /// Length of an array or slice.
-    Len(VnIndex),
 
     // Operations.
     NullaryOp(NullOp<'tcx>, Ty<'tcx>),
@@ -477,11 +475,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
                 discr_value.into()
             }
-            Len(slice) => {
-                let slice = self.evaluated[slice].as_ref()?;
-                let len = slice.len(&self.ecx).discard_err()?;
-                ImmTy::from_uint(len, ty).into()
-            }
             NullaryOp(null_op, arg_ty) => {
                 let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
                 if let NullOp::SizeOf | NullOp::AlignOf = null_op
@@ -841,7 +834,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             }
 
             // Operations.
-            Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
             Rvalue::Cast(ref mut kind, ref mut value, to) => {
                 return self.simplify_cast(kind, value, to, location);
             }
@@ -1049,7 +1041,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         if op == UnOp::PtrMetadata {
             let mut was_updated = false;
             loop {
-                match self.get(arg_index) {
+                arg_index = match self.get(arg_index) {
                     // Pointer casts that preserve metadata, such as
                     // `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`.
                     // It's critical that this not eliminate cases like
@@ -1061,9 +1053,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     Value::Cast { kind: CastKind::PtrToPtr, value: inner }
                         if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) =>
                     {
-                        arg_index = *inner;
-                        was_updated = true;
-                        continue;
+                        *inner
+                    }
+
+                    // We have an unsizing cast, which assigns the length to wide pointer metadata.
+                    Value::Cast {
+                        kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _),
+                        value: from,
+                    } if let Some(from) = self.ty(*from).builtin_deref(true)
+                        && let ty::Array(_, len) = from.kind()
+                        && let Some(to) = self.ty(arg_index).builtin_deref(true)
+                        && let ty::Slice(..) = to.kind() =>
+                    {
+                        return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
                     }
 
                     // `&mut *p`, `&raw *p`, etc don't change metadata.
@@ -1072,18 +1074,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                             place.as_ref()
                             && let Some(local_index) = self.locals[local] =>
                     {
-                        arg_index = local_index;
-                        was_updated = true;
-                        continue;
+                        local_index
                     }
 
-                    _ => {
-                        if was_updated && let Some(op) = self.try_as_operand(arg_index, location) {
-                            *arg_op = op;
-                        }
-                        break;
-                    }
-                }
+                    _ => break,
+                };
+                was_updated = true;
+            }
+
+            if was_updated && let Some(op) = self.try_as_operand(arg_index, location) {
+                *arg_op = op;
             }
         }
 
@@ -1407,39 +1407,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         Some(self.insert(to, Value::Cast { kind, value }))
     }
 
-    fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
-        // Trivial case: we are fetching a statically known length.
-        let place_ty = place.ty(self.local_decls, self.tcx).ty;
-        if let ty::Array(_, len) = place_ty.kind() {
-            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
-        }
-
-        let mut inner = self.simplify_place_value(place, location)?;
-
-        // The length information is stored in the wide pointer.
-        // Reborrowing copies length information from one pointer to the other.
-        while let Value::Address { place: borrowed, .. } = self.get(inner)
-            && let [PlaceElem::Deref] = borrowed.projection[..]
-            && let Some(borrowed) = self.locals[borrowed.local]
-        {
-            inner = borrowed;
-        }
-
-        // We have an unsizing cast, which assigns the length to wide pointer metadata.
-        if let Value::Cast { kind, value: from } = self.get(inner)
-            && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
-            && let Some(from) = self.ty(*from).builtin_deref(true)
-            && let ty::Array(_, len) = from.kind()
-            && let Some(to) = self.ty(inner).builtin_deref(true)
-            && let ty::Slice(..) = to.kind()
-        {
-            return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
-        }
-
-        // Fallback: a symbolic `Len`.
-        Some(self.insert(self.tcx.types.usize, Value::Len(inner)))
-    }
-
     fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
         let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
         let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 481c7941909..aaacc5866a2 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -441,7 +441,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             | Rvalue::Use(..)
             | Rvalue::CopyForDeref(..)
             | Rvalue::Repeat(..)
-            | Rvalue::Len(..)
             | Rvalue::Cast(..)
             | Rvalue::ShallowInitBox(..)
             | Rvalue::Discriminant(..)
@@ -604,20 +603,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                 return None;
             }
 
-            Len(place) => {
-                let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind()
-                {
-                    n.try_to_target_usize(self.tcx)?
-                } else {
-                    match self.get_const(place)? {
-                        Value::Immediate(src) => src.len(&self.ecx).discard_err()?,
-                        Value::Aggregate { fields, .. } => fields.len() as u64,
-                        Value::Uninit => return None,
-                    }
-                };
-                ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into()
-            }
-
             Ref(..) | RawPtr(..) => return None,
 
             NullaryOp(ref null_op, ty) => {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 1663dfa744f..9ff7e0b5500 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -156,7 +156,6 @@ declare_passes! {
     mod match_branches : MatchBranchSimplification;
     mod mentioned_items : MentionedItems;
     mod multiple_return_terminators : MultipleReturnTerminators;
-    mod nrvo : RenameReturnPlace;
     mod post_drop_elaboration : CheckLiveDrops;
     mod prettify : ReorderBasicBlocks, ReorderLocals;
     mod promote_consts : PromoteTemps;
@@ -715,7 +714,6 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
             &jump_threading::JumpThreading,
             &early_otherwise_branch::EarlyOtherwiseBranch,
             &simplify_comparison_integral::SimplifyComparisonIntegral,
-            &dest_prop::DestinationPropagation,
             &o1(simplify_branches::SimplifyConstCondition::Final),
             &o1(remove_noop_landing_pads::RemoveNoopLandingPads),
             &o1(simplify::SimplifyCfg::Final),
@@ -723,7 +721,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
             &strip_debuginfo::StripDebugInfo,
             &copy_prop::CopyProp,
             &dead_store_elimination::DeadStoreElimination::Final,
-            &nrvo::RenameReturnPlace,
+            &dest_prop::DestinationPropagation,
             &simplify::SimplifyLocals::Final,
             &multiple_return_terminators::MultipleReturnTerminators,
             &large_enums::EnumSizeOpt { discrepancy: 128 },
diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs
index f472c7cb493..2ab49645dc4 100644
--- a/compiler/rustc_mir_transform/src/lint.rs
+++ b/compiler/rustc_mir_transform/src/lint.rs
@@ -6,7 +6,7 @@ use std::borrow::Cow;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_index::bit_set::DenseBitSet;
-use rustc_middle::mir::visit::{PlaceContext, Visitor};
+use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals};
@@ -79,15 +79,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         match &statement.kind {
             StatementKind::Assign(box (dest, rvalue)) => {
-                if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
-                    // The sides of an assignment must not alias. Currently this just checks whether
-                    // the places are identical.
-                    if dest == src {
-                        self.fail(
-                            location,
-                            "encountered `Assign` statement with overlapping memory",
-                        );
-                    }
+                let forbid_aliasing = match rvalue {
+                    Rvalue::Use(..)
+                    | Rvalue::CopyForDeref(..)
+                    | Rvalue::Repeat(..)
+                    | Rvalue::Aggregate(..)
+                    | Rvalue::Cast(..)
+                    | Rvalue::ShallowInitBox(..)
+                    | Rvalue::WrapUnsafeBinder(..) => true,
+                    Rvalue::ThreadLocalRef(..)
+                    | Rvalue::NullaryOp(..)
+                    | Rvalue::UnaryOp(..)
+                    | Rvalue::BinaryOp(..)
+                    | Rvalue::Ref(..)
+                    | Rvalue::RawPtr(..)
+                    | Rvalue::Discriminant(..) => false,
+                };
+                // The sides of an assignment must not alias.
+                if forbid_aliasing {
+                    VisitPlacesWith(|src: Place<'tcx>, _| {
+                        if *dest == src
+                            || (dest.local == src.local
+                                && !dest.is_indirect()
+                                && !src.is_indirect())
+                        {
+                            self.fail(
+                                location,
+                                format!(
+                                    "encountered `{statement:?}` statement with overlapping memory"
+                                ),
+                            );
+                        }
+                    })
+                    .visit_rvalue(rvalue, location);
                 }
             }
             StatementKind::StorageLive(local) => {
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
deleted file mode 100644
index 965002aae04..00000000000
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-//! See the docs for [`RenameReturnPlace`].
-
-use rustc_hir::Mutability;
-use rustc_index::bit_set::DenseBitSet;
-use rustc_middle::bug;
-use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{self, BasicBlock, Local, Location};
-use rustc_middle::ty::TyCtxt;
-use tracing::{debug, trace};
-
-/// This pass looks for MIR that always copies the same local into the return place and eliminates
-/// the copy by renaming all uses of that local to `_0`.
-///
-/// This allows LLVM to perform an optimization similar to the named return value optimization
-/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the
-/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by
-/// value like so:
-///
-/// ```rust
-/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
-///     let mut buf = [0; 1024];
-///     init(&mut buf);
-///     buf
-/// }
-/// ```
-///
-/// For now, this pass is very simple and only capable of eliminating a single copy. A more general
-/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and
-/// [#71003], could yield even more benefits.
-///
-/// [#47954]: https://github.com/rust-lang/rust/pull/47954
-/// [#71003]: https://github.com/rust-lang/rust/pull/71003
-pub(super) struct RenameReturnPlace;
-
-impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace {
-    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        // unsound: #111005
-        sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts
-    }
-
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
-        let def_id = body.source.def_id();
-        let Some(returned_local) = local_eligible_for_nrvo(body) else {
-            debug!("`{:?}` was ineligible for NRVO", def_id);
-            return;
-        };
-
-        debug!(
-            "`{:?}` was eligible for NRVO, making {:?} the return place",
-            def_id, returned_local
-        );
-
-        RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body_preserves_cfg(body);
-
-        // Clean up the `NOP`s we inserted for statements made useless by our renaming.
-        for block_data in body.basic_blocks.as_mut_preserves_cfg() {
-            block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop);
-        }
-
-        // Overwrite the debuginfo of `_0` with that of the renamed local.
-        let (renamed_decl, ret_decl) =
-            body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE);
-
-        // Sometimes, the return place is assigned a local of a different but coercible type, for
-        // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means
-        // its type may no longer match the return type of its function. This doesn't cause a
-        // problem in codegen because these two types are layout-compatible, but may be unexpected.
-        debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty);
-        ret_decl.clone_from(renamed_decl);
-
-        // The return place is always mutable.
-        ret_decl.mutability = Mutability::Mut;
-    }
-
-    fn is_required(&self) -> bool {
-        false
-    }
-}
-
-/// MIR that is eligible for the NRVO must fulfill two conditions:
-///   1. The return place must not be read prior to the `Return` terminator.
-///   2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the
-///      only definition of the return place reaching the `Return` terminator.
-///
-/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned
-/// to the return place along all possible paths through the control-flow graph.
-fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option<Local> {
-    if IsReturnPlaceRead::run(body) {
-        return None;
-    }
-
-    let mut copied_to_return_place = None;
-    for block in body.basic_blocks.indices() {
-        // Look for blocks with a `Return` terminator.
-        if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) {
-            continue;
-        }
-
-        // Look for an assignment of a single local to the return place prior to the `Return`.
-        let returned_local = find_local_assigned_to_return_place(block, body)?;
-        match body.local_kind(returned_local) {
-            // FIXME: Can we do this for arguments as well?
-            mir::LocalKind::Arg => return None,
-
-            mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"),
-            mir::LocalKind::Temp => {}
-        }
-
-        // If multiple different locals are copied to the return place. We can't pick a
-        // single one to rename.
-        if copied_to_return_place.is_some_and(|old| old != returned_local) {
-            return None;
-        }
-
-        copied_to_return_place = Some(returned_local);
-    }
-
-    copied_to_return_place
-}
-
-fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option<Local> {
-    let mut block = start;
-    let mut seen = DenseBitSet::new_empty(body.basic_blocks.len());
-
-    // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
-    while seen.insert(block) {
-        trace!("Looking for assignments to `_0` in {:?}", block);
-
-        let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place);
-        if local.is_some() {
-            return local;
-        }
-
-        match body.basic_blocks.predecessors()[block].as_slice() {
-            &[pred] => block = pred,
-            _ => return None,
-        }
-    }
-
-    None
-}
-
-// If this statement is an assignment of an unprojected local to the return place,
-// return that local.
-fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option<Local> {
-    if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind {
-        if lhs.as_local() == Some(mir::RETURN_PLACE) {
-            if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs {
-                return rhs.as_local();
-            }
-        }
-    }
-
-    None
-}
-
-struct RenameToReturnPlace<'tcx> {
-    to_rename: Local,
-    tcx: TyCtxt<'tcx>,
-}
-
-/// Replaces all uses of `self.to_rename` with `_0`.
-impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) {
-        // Remove assignments of the local being replaced to the return place, since it is now the
-        // return place:
-        //     _0 = _1
-        if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) {
-            stmt.kind = mir::StatementKind::Nop;
-            return;
-        }
-
-        // Remove storage annotations for the local being replaced:
-        //     StorageLive(_1)
-        if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) =
-            stmt.kind
-        {
-            if local == self.to_rename {
-                stmt.kind = mir::StatementKind::Nop;
-                return;
-            }
-        }
-
-        self.super_statement(stmt, loc)
-    }
-
-    fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) {
-        // Ignore the implicit "use" of the return place in a `Return` statement.
-        if let mir::TerminatorKind::Return = terminator.kind {
-            return;
-        }
-
-        self.super_terminator(terminator, loc);
-    }
-
-    fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) {
-        if *l == mir::RETURN_PLACE {
-            assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo));
-        } else if *l == self.to_rename {
-            *l = mir::RETURN_PLACE;
-        }
-    }
-}
-
-struct IsReturnPlaceRead(bool);
-
-impl IsReturnPlaceRead {
-    fn run(body: &mir::Body<'_>) -> bool {
-        let mut vis = IsReturnPlaceRead(false);
-        vis.visit_body(body);
-        vis.0
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead {
-    fn visit_local(&mut self, l: Local, ctxt: PlaceContext, _: Location) {
-        if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() {
-            self.0 = true;
-        }
-    }
-
-    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) {
-        // Ignore the implicit "use" of the return place in a `Return` statement.
-        if let mir::TerminatorKind::Return = terminator.kind {
-            return;
-        }
-
-        self.super_terminator(terminator, loc);
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 9ea2eb4f25d..a0b0c8c990f 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -437,9 +437,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                 self.validate_operand(op)?
             }
 
-            Rvalue::Discriminant(place) | Rvalue::Len(place) => {
-                self.validate_place(place.as_ref())?
-            }
+            Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?,
 
             Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),
 
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index 99e4782e470..c8a9a88dc3f 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -1064,14 +1064,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 }
             }
             Rvalue::Ref(..) => {}
-            Rvalue::Len(p) => {
-                let pty = p.ty(&self.body.local_decls, self.tcx).ty;
-                check_kinds!(
-                    pty,
-                    "Cannot compute length of non-array type {:?}",
-                    ty::Array(..) | ty::Slice(..)
-                );
-            }
             Rvalue::BinaryOp(op, vals) => {
                 use BinOp::*;
                 let a = vals.0.ty(&self.body.local_decls, self.tcx);