diff options
Diffstat (limited to 'compiler/rustc_mir/src/transform')
| -rw-r--r-- | compiler/rustc_mir/src/transform/check_consts/mod.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/check_consts/ops.rs | 38 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/check_consts/validation.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/check_unsafety.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/copy_prop.rs | 384 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/inline.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/instcombine.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/instrument_coverage.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/match_branches.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/promote_consts.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/simplify_branches.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/validate.rs | 26 |
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, - ©_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); |
