about summary refs log tree commit diff
path: root/compiler/rustc_mir/src/transform
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir/src/transform')
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/mod.rs12
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/ops.rs38
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/validation.rs20
-rw-r--r--compiler/rustc_mir/src/transform/check_unsafety.rs3
-rw-r--r--compiler/rustc_mir/src/transform/copy_prop.rs384
-rw-r--r--compiler/rustc_mir/src/transform/inline.rs10
-rw-r--r--compiler/rustc_mir/src/transform/instcombine.rs5
-rw-r--r--compiler/rustc_mir/src/transform/instrument_coverage.rs12
-rw-r--r--compiler/rustc_mir/src/transform/match_branches.rs29
-rw-r--r--compiler/rustc_mir/src/transform/mod.rs4
-rw-r--r--compiler/rustc_mir/src/transform/promote_consts.rs34
-rw-r--r--compiler/rustc_mir/src/transform/simplify_branches.rs7
-rw-r--r--compiler/rustc_mir/src/transform/validate.rs26
13 files changed, 118 insertions, 466 deletions
diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs
index 33815ceba62..ba7bea4ac54 100644
--- a/compiler/rustc_mir/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs
@@ -74,12 +74,18 @@ impl ConstCx<'mir, 'tcx> {
 
 /// Returns `true` if this `DefId` points to one of the official `panic` lang items.
 pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
-    Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn()
+    Some(def_id) == tcx.lang_items().panic_fn()
+        || Some(def_id) == tcx.lang_items().panic_str()
+        || Some(def_id) == tcx.lang_items().begin_panic_fn()
 }
 
-pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
+pub fn rustc_allow_const_fn_unstable(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    feature_gate: Symbol,
+) -> bool {
     let attrs = tcx.get_attrs(def_id);
-    attr::allow_internal_unstable(&tcx.sess, attrs)
+    attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs)
         .map_or(false, |mut features| features.any(|name| name == feature_gate))
 }
 
diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs
index 63b20c7c027..bd51136b8db 100644
--- a/compiler/rustc_mir/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs
@@ -224,7 +224,8 @@ impl NonConstOp for CellBorrow {
 }
 
 #[derive(Debug)]
-pub struct MutBorrow;
+pub struct MutBorrow(pub hir::BorrowKind);
+
 impl NonConstOp for MutBorrow {
     fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
         // Forbid everywhere except in const fn with a feature gate
@@ -236,22 +237,28 @@ impl NonConstOp for MutBorrow {
     }
 
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        let raw = match self.0 {
+            hir::BorrowKind::Raw => "raw ",
+            hir::BorrowKind::Ref => "",
+        };
+
         let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_mut_refs,
                 span,
-                &format!("mutable references are not allowed in {}s", ccx.const_kind()),
+                &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
             )
         } else {
             let mut err = struct_span_err!(
                 ccx.tcx.sess,
                 span,
                 E0764,
-                "mutable references are not allowed in {}s",
+                "{}mutable references are not allowed in {}s",
+                raw,
                 ccx.const_kind(),
             );
-            err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
+            err.span_label(span, format!("`&{}mut` is only allowed in `const fn`", raw));
             err
         };
         if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
@@ -270,29 +277,6 @@ impl NonConstOp for MutBorrow {
     }
 }
 
-// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
-#[derive(Debug)]
-pub struct MutAddressOf;
-impl NonConstOp for MutAddressOf {
-    fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
-        // Forbid everywhere except in const fn with a feature gate
-        if ccx.const_kind() == hir::ConstContext::ConstFn {
-            Status::Unstable(sym::const_mut_refs)
-        } else {
-            Status::Forbidden
-        }
-    }
-
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
-        feature_err(
-            &ccx.tcx.sess.parse_sess,
-            sym::const_mut_refs,
-            span,
-            &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()),
-        )
-    }
-}
-
 #[derive(Debug)]
 pub struct MutDeref;
 impl NonConstOp for MutDeref {
diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs
index 587b5b38128..4139b544998 100644
--- a/compiler/rustc_mir/src/transform/check_consts/validation.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs
@@ -292,7 +292,11 @@ impl Validator<'mir, 'tcx> {
 
             Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
                 let unstable_in_stable = self.ccx.is_const_stable_const_fn()
-                    && !super::allow_internal_unstable(self.tcx, self.def_id().to_def_id(), gate);
+                    && !super::rustc_allow_const_fn_unstable(
+                        self.tcx,
+                        self.def_id().to_def_id(),
+                        gate,
+                    );
                 if unstable_in_stable {
                     emit_unstable_in_stable_error(self.ccx, span, gate);
                 }
@@ -525,14 +529,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
 
                 if !is_allowed {
                     if let BorrowKind::Mut { .. } = kind {
-                        self.check_op(ops::MutBorrow);
+                        self.check_op(ops::MutBorrow(hir::BorrowKind::Ref));
                     } else {
                         self.check_op(ops::CellBorrow);
                     }
                 }
             }
 
-            Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf),
+            Rvalue::AddressOf(Mutability::Mut, _) => {
+                self.check_op(ops::MutBorrow(hir::BorrowKind::Raw))
+            }
 
             Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
             | Rvalue::AddressOf(Mutability::Not, ref place) => {
@@ -805,7 +811,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
                     }
 
                     // Calling an unstable function *always* requires that the corresponding gate
-                    // be enabled, even if the function has `#[allow_internal_unstable(the_gate)]`.
+                    // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
                     if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) {
                         self.check_op(ops::FnCallUnstable(callee, Some(gate)));
                         return;
@@ -819,7 +825,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
 
                     // Otherwise, we are something const-stable calling a const-unstable fn.
 
-                    if super::allow_internal_unstable(tcx, caller, gate) {
+                    if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
                         return;
                     }
 
@@ -965,8 +971,8 @@ fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol
         )
         .span_suggestion(
             attr_span,
-            "otherwise `#[allow_internal_unstable]` can be used to bypass stability checks",
-            format!("#[allow_internal_unstable({})]\n", gate),
+            "otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks",
+            format!("#[rustc_allow_const_fn_unstable({})]\n", gate),
             Applicability::MaybeIncorrect,
         )
         .emit();
diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs
index 7309a4129e4..3d68b862df2 100644
--- a/compiler/rustc_mir/src/transform/check_unsafety.rs
+++ b/compiler/rustc_mir/src/transform/check_unsafety.rs
@@ -204,6 +204,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             if let [] = proj_base {
                 let decl = &self.body.local_decls[place.local];
                 if decl.internal {
+                    // If the projection root is an artifical local that we introduced when
+                    // desugaring `static`, give a more specific error message
+                    // (avoid the general "raw pointer" clause below, that would only be confusing).
                     if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
                         if self.tcx.is_mutable_static(def_id) {
                             self.require_unsafe(
diff --git a/compiler/rustc_mir/src/transform/copy_prop.rs b/compiler/rustc_mir/src/transform/copy_prop.rs
deleted file mode 100644
index 4f44bb7b204..00000000000
--- a/compiler/rustc_mir/src/transform/copy_prop.rs
+++ /dev/null
@@ -1,384 +0,0 @@
-//! Trivial copy propagation pass.
-//!
-//! This uses def-use analysis to remove values that have exactly one def and one use, which must
-//! be an assignment.
-//!
-//! To give an example, we look for patterns that look like:
-//!
-//!     DEST = SRC
-//!     ...
-//!     USE(DEST)
-//!
-//! where `DEST` and `SRC` are both locals of some form. We replace that with:
-//!
-//!     NOP
-//!     ...
-//!     USE(SRC)
-//!
-//! The assignment `DEST = SRC` must be (a) the only mutation of `DEST` and (b) the only
-//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
-//! future.
-
-use crate::transform::MirPass;
-use crate::util::def_use::DefUseAnalysis;
-use rustc_middle::mir::visit::MutVisitor;
-use rustc_middle::mir::{
-    Body, Constant, Local, LocalKind, Location, Operand, Place, Rvalue, StatementKind,
-};
-use rustc_middle::ty::TyCtxt;
-
-pub struct CopyPropagation;
-
-impl<'tcx> MirPass<'tcx> for CopyPropagation {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        let opts = &tcx.sess.opts.debugging_opts;
-        // We only run when the MIR optimization level is > 1.
-        // This avoids a slow pass, and messing up debug info.
-        // FIXME(76740): This optimization is buggy and can cause unsoundness.
-        if opts.mir_opt_level <= 1 || !opts.unsound_mir_opts {
-            return;
-        }
-
-        let mut def_use_analysis = DefUseAnalysis::new(body);
-        loop {
-            def_use_analysis.analyze(body);
-
-            if eliminate_self_assignments(body, &def_use_analysis) {
-                def_use_analysis.analyze(body);
-            }
-
-            let mut changed = false;
-            for dest_local in body.local_decls.indices() {
-                debug!("considering destination local: {:?}", dest_local);
-
-                let action;
-                let location;
-                {
-                    // The destination must have exactly one def.
-                    let dest_use_info = def_use_analysis.local_info(dest_local);
-                    let dest_def_count = dest_use_info.def_count_not_including_drop();
-                    if dest_def_count == 0 {
-                        debug!("  Can't copy-propagate local: dest {:?} undefined", dest_local);
-                        continue;
-                    }
-                    if dest_def_count > 1 {
-                        debug!(
-                            "  Can't copy-propagate local: dest {:?} defined {} times",
-                            dest_local,
-                            dest_use_info.def_count()
-                        );
-                        continue;
-                    }
-                    if dest_use_info.use_count() == 0 {
-                        debug!("  Can't copy-propagate local: dest {:?} unused", dest_local);
-                        continue;
-                    }
-                    // Conservatively gives up if the dest is an argument,
-                    // because there may be uses of the original argument value.
-                    // Also gives up on the return place, as we cannot propagate into its implicit
-                    // use by `return`.
-                    if matches!(
-                        body.local_kind(dest_local),
-                        LocalKind::Arg | LocalKind::ReturnPointer
-                    ) {
-                        debug!("  Can't copy-propagate local: dest {:?} (argument)", dest_local);
-                        continue;
-                    }
-                    let dest_place_def = dest_use_info.defs_not_including_drop().next().unwrap();
-                    location = dest_place_def.location;
-
-                    let basic_block = &body[location.block];
-                    let statement_index = location.statement_index;
-                    let statement = match basic_block.statements.get(statement_index) {
-                        Some(statement) => statement,
-                        None => {
-                            debug!("  Can't copy-propagate local: used in terminator");
-                            continue;
-                        }
-                    };
-
-                    // That use of the source must be an assignment.
-                    match &statement.kind {
-                        StatementKind::Assign(box (place, Rvalue::Use(operand))) => {
-                            if let Some(local) = place.as_local() {
-                                if local == dest_local {
-                                    let maybe_action = match operand {
-                                        Operand::Copy(src_place) | Operand::Move(src_place) => {
-                                            Action::local_copy(&body, &def_use_analysis, *src_place)
-                                        }
-                                        Operand::Constant(ref src_constant) => {
-                                            Action::constant(src_constant)
-                                        }
-                                    };
-                                    match maybe_action {
-                                        Some(this_action) => action = this_action,
-                                        None => continue,
-                                    }
-                                } else {
-                                    debug!(
-                                        "  Can't copy-propagate local: source use is not an \
-                                    assignment"
-                                    );
-                                    continue;
-                                }
-                            } else {
-                                debug!(
-                                    "  Can't copy-propagate local: source use is not an \
-                                    assignment"
-                                );
-                                continue;
-                            }
-                        }
-                        _ => {
-                            debug!(
-                                "  Can't copy-propagate local: source use is not an \
-                                    assignment"
-                            );
-                            continue;
-                        }
-                    }
-                }
-
-                changed =
-                    action.perform(body, &def_use_analysis, dest_local, location, tcx) || changed;
-                // FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
-                // regenerating the chains.
-                break;
-            }
-            if !changed {
-                break;
-            }
-        }
-    }
-}
-
-fn eliminate_self_assignments(body: &mut Body<'_>, def_use_analysis: &DefUseAnalysis) -> bool {
-    let mut changed = false;
-
-    for dest_local in body.local_decls.indices() {
-        let dest_use_info = def_use_analysis.local_info(dest_local);
-
-        for def in dest_use_info.defs_not_including_drop() {
-            let location = def.location;
-            if let Some(stmt) = body[location.block].statements.get(location.statement_index) {
-                match &stmt.kind {
-                    StatementKind::Assign(box (
-                        place,
-                        Rvalue::Use(Operand::Copy(src_place) | Operand::Move(src_place)),
-                    )) => {
-                        if let (Some(local), Some(src_local)) =
-                            (place.as_local(), src_place.as_local())
-                        {
-                            if local == dest_local && dest_local == src_local {
-                            } else {
-                                continue;
-                            }
-                        } else {
-                            continue;
-                        }
-                    }
-                    _ => {
-                        continue;
-                    }
-                }
-            } else {
-                continue;
-            }
-            debug!("deleting a self-assignment for {:?}", dest_local);
-            body.make_statement_nop(location);
-            changed = true;
-        }
-    }
-
-    changed
-}
-
-enum Action<'tcx> {
-    PropagateLocalCopy(Local),
-    PropagateConstant(Constant<'tcx>),
-}
-
-impl<'tcx> Action<'tcx> {
-    fn local_copy(
-        body: &Body<'tcx>,
-        def_use_analysis: &DefUseAnalysis,
-        src_place: Place<'tcx>,
-    ) -> Option<Action<'tcx>> {
-        // The source must be a local.
-        let src_local = if let Some(local) = src_place.as_local() {
-            local
-        } else {
-            debug!("  Can't copy-propagate local: source is not a local");
-            return None;
-        };
-
-        // We're trying to copy propagate a local.
-        // There must be exactly one use of the source used in a statement (not in a terminator).
-        let src_use_info = def_use_analysis.local_info(src_local);
-        let src_use_count = src_use_info.use_count();
-        if src_use_count == 0 {
-            debug!("  Can't copy-propagate local: no uses");
-            return None;
-        }
-        if src_use_count != 1 {
-            debug!("  Can't copy-propagate local: {} uses", src_use_info.use_count());
-            return None;
-        }
-
-        // Verify that the source doesn't change in between. This is done conservatively for now,
-        // by ensuring that the source has exactly one mutation. The goal is to prevent things
-        // like:
-        //
-        //     DEST = SRC;
-        //     SRC = X;
-        //     USE(DEST);
-        //
-        // From being misoptimized into:
-        //
-        //     SRC = X;
-        //     USE(SRC);
-        let src_def_count = src_use_info.def_count_not_including_drop();
-        // allow function arguments to be propagated
-        let is_arg = body.local_kind(src_local) == LocalKind::Arg;
-        if (is_arg && src_def_count != 0) || (!is_arg && src_def_count != 1) {
-            debug!(
-                "  Can't copy-propagate local: {} defs of src{}",
-                src_def_count,
-                if is_arg { " (argument)" } else { "" },
-            );
-            return None;
-        }
-
-        Some(Action::PropagateLocalCopy(src_local))
-    }
-
-    fn constant(src_constant: &Constant<'tcx>) -> Option<Action<'tcx>> {
-        Some(Action::PropagateConstant(*src_constant))
-    }
-
-    fn perform(
-        self,
-        body: &mut Body<'tcx>,
-        def_use_analysis: &DefUseAnalysis,
-        dest_local: Local,
-        location: Location,
-        tcx: TyCtxt<'tcx>,
-    ) -> bool {
-        match self {
-            Action::PropagateLocalCopy(src_local) => {
-                // Eliminate the destination and the assignment.
-                //
-                // First, remove all markers.
-                //
-                // FIXME(pcwalton): Don't do this. Merge live ranges instead.
-                debug!("  Replacing all uses of {:?} with {:?} (local)", dest_local, src_local);
-                for place_use in &def_use_analysis.local_info(dest_local).defs_and_uses {
-                    if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
-                    }
-                }
-                for place_use in &def_use_analysis.local_info(src_local).defs_and_uses {
-                    if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
-                    }
-                }
-
-                // Replace all uses of the destination local with the source local.
-                def_use_analysis.replace_all_defs_and_uses_with(dest_local, body, src_local, tcx);
-
-                // Finally, zap the now-useless assignment instruction.
-                debug!("  Deleting assignment");
-                body.make_statement_nop(location);
-
-                true
-            }
-            Action::PropagateConstant(src_constant) => {
-                // First, remove all markers.
-                //
-                // FIXME(pcwalton): Don't do this. Merge live ranges instead.
-                debug!(
-                    "  Replacing all uses of {:?} with {:?} (constant)",
-                    dest_local, src_constant
-                );
-                let dest_local_info = def_use_analysis.local_info(dest_local);
-                for place_use in &dest_local_info.defs_and_uses {
-                    if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
-                    }
-                }
-
-                // Replace all uses of the destination local with the constant.
-                let mut visitor = ConstantPropagationVisitor::new(dest_local, src_constant, tcx);
-                for dest_place_use in &dest_local_info.defs_and_uses {
-                    visitor.visit_location(body, dest_place_use.location)
-                }
-
-                // Zap the assignment instruction if we eliminated all the uses. We won't have been
-                // able to do that if the destination was used in a projection, because projections
-                // must have places on their LHS.
-                let use_count = dest_local_info.use_count();
-                if visitor.uses_replaced == use_count {
-                    debug!(
-                        "  {} of {} use(s) replaced; deleting assignment",
-                        visitor.uses_replaced, use_count
-                    );
-                    body.make_statement_nop(location);
-                    true
-                } else if visitor.uses_replaced == 0 {
-                    debug!("  No uses replaced; not deleting assignment");
-                    false
-                } else {
-                    debug!(
-                        "  {} of {} use(s) replaced; not deleting assignment",
-                        visitor.uses_replaced, use_count
-                    );
-                    true
-                }
-            }
-        }
-    }
-}
-
-struct ConstantPropagationVisitor<'tcx> {
-    dest_local: Local,
-    constant: Constant<'tcx>,
-    tcx: TyCtxt<'tcx>,
-    uses_replaced: usize,
-}
-
-impl<'tcx> ConstantPropagationVisitor<'tcx> {
-    fn new(
-        dest_local: Local,
-        constant: Constant<'tcx>,
-        tcx: TyCtxt<'tcx>,
-    ) -> ConstantPropagationVisitor<'tcx> {
-        ConstantPropagationVisitor { dest_local, constant, tcx, uses_replaced: 0 }
-    }
-}
-
-impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> {
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
-        self.super_operand(operand, location);
-
-        match operand {
-            Operand::Copy(place) | Operand::Move(place) => {
-                if let Some(local) = place.as_local() {
-                    if local == self.dest_local {
-                    } else {
-                        return;
-                    }
-                } else {
-                    return;
-                }
-            }
-            _ => return,
-        }
-
-        *operand = Operand::Constant(box self.constant);
-        self.uses_replaced += 1
-    }
-}
diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs
index 34aaefdcbea..796ad6c9c29 100644
--- a/compiler/rustc_mir/src/transform/inline.rs
+++ b/compiler/rustc_mir/src/transform/inline.rs
@@ -201,9 +201,13 @@ impl Inliner<'tcx> {
         let terminator = bb_data.terminator();
         if let TerminatorKind::Call { func: ref op, .. } = terminator.kind {
             if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() {
-                let instance = Instance::resolve(self.tcx, self.param_env, callee_def_id, substs)
-                    .ok()
-                    .flatten()?;
+                // To resolve an instance its substs have to be fully normalized, so
+                // we do this here.
+                let normalized_substs = self.tcx.normalize_erasing_regions(self.param_env, substs);
+                let instance =
+                    Instance::resolve(self.tcx, self.param_env, callee_def_id, normalized_substs)
+                        .ok()
+                        .flatten()?;
 
                 if let InstanceDef::Virtual(..) = instance.def {
                     return None;
diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs
index 1a8e281d417..c0b3d5ff18b 100644
--- a/compiler/rustc_mir/src/transform/instcombine.rs
+++ b/compiler/rustc_mir/src/transform/instcombine.rs
@@ -119,6 +119,11 @@ impl OptimizationFinder<'b, 'tcx> {
     }
 
     fn find_deref_of_address(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option<()> {
+        // FIXME(#78192): This optimization can result in unsoundness.
+        if !self.tcx.sess.opts.debugging_opts.unsound_mir_opts {
+            return None;
+        }
+
         // Look for the sequence
         //
         // _2 = &_1;
diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs
index babe10a0f14..6824c73ab60 100644
--- a/compiler/rustc_mir/src/transform/instrument_coverage.rs
+++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs
@@ -22,9 +22,7 @@ use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
 use rustc_span::source_map::original_sp;
-use rustc_span::{
-    BytePos, CharPos, FileName, Pos, RealFileName, SourceFile, Span, Symbol, SyntaxContext,
-};
+use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext};
 
 use std::cmp::Ordering;
 
@@ -549,13 +547,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let mir_body = &self.mir_body;
         let body_span = self.body_span();
         let source_file = source_map.lookup_source_file(body_span.lo());
-        let file_name = match &source_file.name {
-            FileName::Real(RealFileName::Named(path)) => Symbol::intern(&path.to_string_lossy()),
-            _ => bug!(
-                "source_file.name should be a RealFileName, but it was: {:?}",
-                source_file.name
-            ),
-        };
+        let file_name = Symbol::intern(&source_file.name.to_string());
 
         debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span));
 
diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs
index 8b2d6b09aa8..06690dcbf6e 100644
--- a/compiler/rustc_mir/src/transform/match_branches.rs
+++ b/compiler/rustc_mir/src/transform/match_branches.rs
@@ -38,12 +38,16 @@ pub struct MatchBranchSimplification;
 
 impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
+            return;
+        }
+
         let param_env = tcx.param_env(body.source.def_id());
-        let bbs = body.basic_blocks_mut();
+        let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
         'outer: for bb_idx in bbs.indices() {
             let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind {
                 TerminatorKind::SwitchInt {
-                    discr: Operand::Copy(ref place) | Operand::Move(ref place),
+                    discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)),
                     switch_ty,
                     ref targets,
                     ..
@@ -52,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                     if target == targets.otherwise() {
                         continue;
                     }
-                    (place, value, switch_ty, target, targets.otherwise())
+                    (discr, value, switch_ty, target, targets.otherwise())
                 }
                 // Only optimize switch int statements
                 _ => continue,
@@ -92,6 +96,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
             // Take ownership of items now that we know we can optimize.
             let discr = discr.clone();
 
+            // Introduce a temporary for the discriminant value.
+            let source_info = bbs[bb_idx].terminator().source_info;
+            let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span));
+
             // We already checked that first and second are different blocks,
             // and bb_idx has a different terminator from both of them.
             let (from, first, second) = bbs.pick3_mut(bb_idx, first, second);
@@ -120,7 +128,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                                 rustc_span::DUMMY_SP,
                             );
                             let op = if f_b { BinOp::Eq } else { BinOp::Ne };
-                            let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp);
+                            let rhs = Rvalue::BinaryOp(
+                                op,
+                                Operand::Copy(Place::from(discr_local)),
+                                const_cmp,
+                            );
                             Statement {
                                 source_info: f.source_info,
                                 kind: StatementKind::Assign(box (*lhs, rhs)),
@@ -131,7 +143,16 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                     _ => unreachable!(),
                 }
             });
+
+            from.statements
+                .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) });
+            from.statements.push(Statement {
+                source_info,
+                kind: StatementKind::Assign(box (Place::from(discr_local), Rvalue::Use(discr))),
+            });
             from.statements.extend(new_stmts);
+            from.statements
+                .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) });
             from.terminator_mut().kind = first.terminator().kind.clone();
         }
     }
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index ffb84950fc9..20b8c90a9dc 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -22,7 +22,6 @@ pub mod check_packed_ref;
 pub mod check_unsafety;
 pub mod cleanup_post_borrowck;
 pub mod const_prop;
-pub mod copy_prop;
 pub mod deaggregator;
 pub mod dest_prop;
 pub mod dump_mir;
@@ -401,8 +400,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &simplify_try::SimplifyArmIdentity,
         &simplify_try::SimplifyBranchSame,
         &dest_prop::DestinationPropagation,
-        &copy_prop::CopyPropagation,
-        &simplify_branches::SimplifyBranches::new("after-copy-prop"),
+        &simplify_branches::SimplifyBranches::new("final"),
         &remove_noop_landing_pads::RemoveNoopLandingPads,
         &simplify::SimplifyCfg::new("final"),
         &nrvo::RenameReturnPlace,
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index 7373abcc820..292380d7fec 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -301,17 +301,6 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
 struct Unpromotable;
 
 impl<'tcx> Validator<'_, 'tcx> {
-    /// Determines if this code could be executed at runtime and thus is subject to codegen.
-    /// That means even unused constants need to be evaluated.
-    ///
-    /// `const_kind` should not be used in this file other than through this method!
-    fn maybe_runtime(&self) -> bool {
-        match self.const_kind {
-            None | Some(hir::ConstContext::ConstFn) => true,
-            Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false,
-        }
-    }
-
     fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
         match candidate {
             Candidate::Ref(loc) => {
@@ -562,14 +551,12 @@ impl<'tcx> Validator<'_, 'tcx> {
                     }
 
                     ProjectionElem::Field(..) => {
-                        if self.maybe_runtime() {
-                            let base_ty =
-                                Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
-                            if let Some(def) = base_ty.ty_adt_def() {
-                                // No promotion of union field accesses.
-                                if def.is_union() {
-                                    return Err(Unpromotable);
-                                }
+                        let base_ty =
+                            Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
+                        if let Some(def) = base_ty.ty_adt_def() {
+                            // No promotion of union field accesses.
+                            if def.is_union() {
+                                return Err(Unpromotable);
                             }
                         }
                     }
@@ -751,7 +738,14 @@ impl<'tcx> Validator<'_, 'tcx> {
     ) -> Result<(), Unpromotable> {
         let fn_ty = callee.ty(self.body, self.tcx);
 
-        if !self.explicit && self.maybe_runtime() {
+        // When doing explicit promotion and inside const/static items, we promote all (eligible) function calls.
+        // Everywhere else, we require `#[rustc_promotable]` on the callee.
+        let promote_all_const_fn = self.explicit
+            || matches!(
+                self.const_kind,
+                Some(hir::ConstContext::Static(_) | hir::ConstContext::Const)
+            );
+        if !promote_all_const_fn {
             if let ty::FnDef(def_id, _) = *fn_ty.kind() {
                 // Never promote runtime `const fn` calls of
                 // functions without `#[rustc_promotable]`.
diff --git a/compiler/rustc_mir/src/transform/simplify_branches.rs b/compiler/rustc_mir/src/transform/simplify_branches.rs
index 5f63c03993d..a9a45e61a38 100644
--- a/compiler/rustc_mir/src/transform/simplify_branches.rs
+++ b/compiler/rustc_mir/src/transform/simplify_branches.rs
@@ -49,9 +49,10 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches {
                 }
                 TerminatorKind::Assert {
                     target, cond: Operand::Constant(ref c), expected, ..
-                } if (c.literal.try_eval_bool(tcx, param_env) == Some(true)) == expected => {
-                    TerminatorKind::Goto { target }
-                }
+                } => match c.literal.try_eval_bool(tcx, param_env) {
+                    Some(v) if v == expected => TerminatorKind::Goto { target },
+                    _ => continue,
+                },
                 TerminatorKind::FalseEdge { real_target, .. } => {
                     TerminatorKind::Goto { target: real_target }
                 }
diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs
index beffffa727e..7b22d643ab6 100644
--- a/compiler/rustc_mir/src/transform/validate.rs
+++ b/compiler/rustc_mir/src/transform/validate.rs
@@ -5,13 +5,17 @@ use crate::dataflow::{Analysis, ResultsCursor};
 use crate::util::storage::AlwaysLiveLocals;
 
 use super::MirPass;
-use rustc_middle::mir::visit::{PlaceContext, Visitor};
+use rustc_middle::mir::{
+    interpret::Scalar,
+    visit::{PlaceContext, Visitor},
+};
 use rustc_middle::mir::{
     AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, Rvalue,
     SourceScope, Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo,
 };
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
+use rustc_target::abi::Size;
 
 #[derive(Copy, Clone, Debug)]
 enum EdgeKind {
@@ -346,7 +350,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         ),
                     );
                 }
-                for (_, target) in targets.iter() {
+
+                let target_width = self.tcx.sess.target.pointer_width;
+
+                let size = Size::from_bits(match switch_ty.kind() {
+                    ty::Uint(uint) => uint.normalize(target_width).bit_width().unwrap(),
+                    ty::Int(int) => int.normalize(target_width).bit_width().unwrap(),
+                    ty::Char => 32,
+                    ty::Bool => 1,
+                    other => bug!("unhandled type: {:?}", other),
+                });
+
+                for (value, target) in targets.iter() {
+                    if Scalar::<()>::try_from_uint(value, size).is_none() {
+                        self.fail(
+                            location,
+                            format!("the value {:#x} is not a proper {:?}", value, switch_ty),
+                        )
+                    }
+
                     self.check_edge(location, target, EdgeKind::Normal);
                 }
                 self.check_edge(location, targets.otherwise(), EdgeKind::Normal);