From 245c60749a20c9e8f3cca872446fb37d40b6e072 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sun, 24 Apr 2022 17:34:24 -0400 Subject: [PATCH] Rewrite dest prop. This fixes a number of correctness issues from the previous version. Additionally, we use a new strategy which has much better performance charactersitics and also finds more opportunities to apply the optimization. --- .../src/impls/init_locals.rs | 122 -- compiler/rustc_mir_dataflow/src/impls/mod.rs | 2 - .../src/dead_store_elimination.rs | 2 + .../src/deduce_param_attrs.rs | 48 +- compiler/rustc_mir_transform/src/dest_prop.rs | 1299 ++++++++--------- .../cycle.cycle.DeadStoreElimination.diff | 69 +- ...=> branch.foo.DestinationPropagation.diff} | 40 +- src/test/mir-opt/dest-prop/branch.rs | 12 +- ...on_arg.arg_src.DestinationPropagation.diff | 4 +- ...gation_arg.bar.DestinationPropagation.diff | 18 +- ...gation_arg.baz.DestinationPropagation.diff | 20 +- ...gation_arg.foo.DestinationPropagation.diff | 14 +- .../mir-opt/dest-prop/copy_propagation_arg.rs | 5 +- .../cycle.main.DestinationPropagation.diff | 68 +- src/test/mir-opt/dest-prop/cycle.rs | 2 +- ...s_79191.f.DestinationPropagation.after.mir | 34 + .../mir-opt/dest-prop/dead_stores_79191.rs | 17 + ..._better.f.DestinationPropagation.after.mir | 34 + .../mir-opt/dest-prop/dead_stores_better.rs | 21 + .../simple.nrvo.DestinationPropagation.diff | 12 +- src/test/mir-opt/dest-prop/simple.rs | 2 +- .../union.main.DestinationPropagation.diff | 11 +- .../inline/dyn_trait.get_query.Inline.diff | 12 +- .../dyn_trait.try_execute_query.Inline.diff | 6 +- .../inline_any_operand.bar.Inline.after.mir | 10 +- ...e_closure_borrows_arg.foo.Inline.after.mir | 8 +- .../inline/inline_cycle.two.Inline.diff | 16 +- .../inline/inline_diverging.h.Inline.diff | 12 +- .../inline/inline_generator.main.Inline.diff | 40 +- ...line_trait_method_2.test2.Inline.after.mir | 6 +- .../mir-opt/issue_101973.inner.ConstProp.diff | 24 +- ...ue_59352.num_to_digit.PreCodegen.after.mir | 75 +- ...y_len_e2e.array_bound.PreCodegen.after.mir | 16 +- ...n_e2e.array_bound_mut.PreCodegen.after.mir | 36 +- ...intrinsics_e2e.f_unit.PreCodegen.after.mir | 5 +- .../try_identity_e2e.new.PreCodegen.after.mir | 89 +- .../try_identity_e2e.old.PreCodegen.after.mir | 35 +- .../async-await/large_moves.attribute.stderr | 8 +- .../ui/async-await/large_moves.option.stderr | 8 +- src/test/ui/async-await/large_moves.rs | 1 + 40 files changed, 1039 insertions(+), 1224 deletions(-) delete mode 100644 compiler/rustc_mir_dataflow/src/impls/init_locals.rs rename src/test/mir-opt/dest-prop/{branch.main.DestinationPropagation.diff => branch.foo.DestinationPropagation.diff} (56%) create mode 100644 src/test/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.mir create mode 100644 src/test/mir-opt/dest-prop/dead_stores_79191.rs create mode 100644 src/test/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.mir create mode 100644 src/test/mir-opt/dest-prop/dead_stores_better.rs diff --git a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs b/compiler/rustc_mir_dataflow/src/impls/init_locals.rs deleted file mode 100644 index 83ce4c44b7144..0000000000000 --- a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! A less precise version of `MaybeInitializedPlaces` whose domain is entire locals. -//! -//! A local will be maybe initialized if *any* projections of that local might be initialized. - -use crate::{CallReturnPlaces, GenKill}; - -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::visit::{PlaceContext, Visitor}; -use rustc_middle::mir::{self, BasicBlock, Local, Location}; - -pub struct MaybeInitializedLocals; - -impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeInitializedLocals { - type Domain = BitSet; - - const NAME: &'static str = "maybe_init_locals"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = uninit - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) { - // Function arguments are initialized to begin with. - for arg in body.args_iter() { - entry_set.insert(arg); - } - } -} - -impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeInitializedLocals { - type Idx = Local; - - fn statement_effect( - &self, - trans: &mut impl GenKill, - statement: &mir::Statement<'tcx>, - loc: Location, - ) { - TransferFunction { trans }.visit_statement(statement, loc) - } - - fn terminator_effect( - &self, - trans: &mut impl GenKill, - terminator: &mir::Terminator<'tcx>, - loc: Location, - ) { - TransferFunction { trans }.visit_terminator(terminator, loc) - } - - fn call_return_effect( - &self, - trans: &mut impl GenKill, - _block: BasicBlock, - return_places: CallReturnPlaces<'_, 'tcx>, - ) { - return_places.for_each(|place| trans.gen(place.local)); - } - - /// See `Analysis::apply_yield_resume_effect`. - fn yield_resume_effect( - &self, - trans: &mut impl GenKill, - _resume_block: BasicBlock, - resume_place: mir::Place<'tcx>, - ) { - trans.gen(resume_place.local) - } -} - -struct TransferFunction<'a, T> { - trans: &'a mut T, -} - -impl Visitor<'_> for TransferFunction<'_, T> -where - T: GenKill, -{ - // FIXME: Using `visit_local` here is a bug. For example, on `move _5.field` we mark `_5` as - // deinitialized, although clearly it is only partially deinitialized. This analysis is not - // actually used anywhere at the moment, so this is not critical, but this does need to be fixed - // before it starts being used again. - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext}; - match context { - // These are handled specially in `call_return_effect` and `yield_resume_effect`. - PlaceContext::MutatingUse( - MutatingUseContext::Call - | MutatingUseContext::AsmOutput - | MutatingUseContext::Yield, - ) => {} - - // If it's deinitialized, it's no longer init - PlaceContext::MutatingUse(MutatingUseContext::Deinit) => self.trans.kill(local), - - // Otherwise, when a place is mutated, we must consider it possibly initialized. - PlaceContext::MutatingUse(_) => self.trans.gen(local), - - // If the local is moved out of, or if it gets marked `StorageDead`, consider it no - // longer initialized. - PlaceContext::NonUse(NonUseContext::StorageDead) - | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => self.trans.kill(local), - - // All other uses do not affect this analysis. - PlaceContext::NonUse( - NonUseContext::StorageLive - | NonUseContext::AscribeUserTy - | NonUseContext::VarDebugInfo, - ) - | PlaceContext::NonMutatingUse( - NonMutatingUseContext::Inspect - | NonMutatingUseContext::Copy - | NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::ShallowBorrow - | NonMutatingUseContext::UniqueBorrow - | NonMutatingUseContext::AddressOf - | NonMutatingUseContext::Projection, - ) => {} - } - } -} diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index fd1e492779f1b..bc31ec42b8b6e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -19,13 +19,11 @@ use crate::{drop_flag_effects, on_all_children_bits}; use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis}; mod borrowed_locals; -mod init_locals; mod liveness; mod storage_liveness; pub use self::borrowed_locals::borrowed_locals; pub use self::borrowed_locals::MaybeBorrowedLocals; -pub use self::init_locals::MaybeInitializedLocals; pub use self::liveness::MaybeLiveLocals; pub use self::liveness::MaybeTransitiveLiveLocals; pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 3f3870cc7bad2..42c580c63f060 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -70,6 +70,8 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } + + crate::simplify::SimplifyLocals.run_pass(tcx, body) } pub struct DeadStoreElimination; diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 28b1c5a48099b..92f1fff6beb95 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -110,15 +110,16 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { if let TerminatorKind::Call { ref args, .. } = terminator.kind { for arg in args { - if let Operand::Move(_) = *arg { - // ArgumentChecker panics if a direct move of an argument from a caller to a - // callee was detected. - // - // If, in the future, MIR optimizations cause arguments to be moved directly - // from callers to callees, change the panic to instead add the argument in - // question to `mutating_uses`. - ArgumentChecker::new(self.mutable_args.domain_size()) - .visit_operand(arg, location) + if let Operand::Move(place) = *arg { + let local = place.local; + if place.is_indirect() + || local == RETURN_PLACE + || local.index() > self.mutable_args.domain_size() + { + continue; + } + + self.mutable_args.insert(local.index() - 1); } } }; @@ -127,35 +128,6 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { } } -/// A visitor that simply panics if a direct move of an argument from a caller to a callee was -/// detected. -struct ArgumentChecker { - /// The number of arguments to the calling function. - arg_count: usize, -} - -impl ArgumentChecker { - /// Creates a new ArgumentChecker. - fn new(arg_count: usize) -> Self { - Self { arg_count } - } -} - -impl<'tcx> Visitor<'tcx> for ArgumentChecker { - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - // Check to make sure that, if this local is an argument, we didn't move directly from it. - if matches!(context, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)) - && local != RETURN_PLACE - && local.index() <= self.arg_count - { - // If, in the future, MIR optimizations cause arguments to be moved directly from - // callers to callees, change this panic to instead add the argument in question to - // `mutating_uses`. - panic!("Detected a direct move from a caller's argument to a callee's argument!") - } - } -} - /// Returns true if values of a given type will never be passed indirectly, regardless of ABI. fn type_will_always_be_passed_directly<'tcx>(ty: Ty<'tcx>) -> bool { matches!( diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 9bc47613e4c67..8cd44ab82ccc4 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -20,7 +20,8 @@ //! values or the return place `_0`. On a very high level, independent of the actual implementation //! details, it does the following: //! -//! 1) Identify `dest = src;` statements that can be soundly eliminated. +//! 1) Identify `dest = src;` statements with values for `dest` and `src` whose storage can soundly +//! be merged. //! 2) Replace all mentions of `src` with `dest` ("unifying" them and propagating the destination //! backwards). //! 3) Delete the `dest = src;` statement (by making it a `nop`). @@ -29,44 +30,80 @@ //! //! ## Soundness //! -//! Given an `Assign` statement `dest = src;`, where `dest` is a `Place` and `src` is an `Rvalue`, -//! there are a few requirements that must hold for the optimization to be sound: +//! We have a pair of places `p` and `q`, whose memory we would like to merge. In order for this to +//! be sound, we need to check a number of conditions: //! -//! * `dest` must not contain any *indirection* through a pointer. It must access part of the base -//! local. Otherwise it might point to arbitrary memory that is hard to track. +//! * `p` and `q` must both be *constant* - it does not make much sense to talk about merging them +//! if they do not consistently refer to the same place in memory. This is satisfied if they do +//! not contain any indirection through a pointer or any indexing projections. //! -//! It must also not contain any indexing projections, since those take an arbitrary `Local` as -//! the index, and that local might only be initialized shortly before `dest` is used. +//! * We need to make sure that the goal of "merging the memory" is actually structurally possible +//! in MIR. For example, even if all the other conditions are satisfied, there is no way to +//! "merge" `_5.foo` and `_6.bar`. For now, we ensure this by requiring that both `p` and `q` are +//! locals with no further projections. Future iterations of this pass should improve on this. //! -//! * `src` must be a bare `Local` without any indirections or field projections (FIXME: Is this a -//! fundamental restriction or just current impl state?). It can be copied or moved by the -//! assignment. +//! * Finally, we want `p` and `q` to use the same memory - however, we still need to make sure that +//! each of them has enough "ownership" of that memory to continue "doing its job." More +//! precisely, what we will check is that whenever the program performs a write to `p`, then it +//! does not currently care about what the value in `q` is (and vice versa). We formalize the +//! notion of "does not care what the value in `q` is" by checking the *liveness* of `q`. //! -//! * The `dest` and `src` locals must never be [*live*][liveness] at the same time. If they are, it -//! means that they both hold a (potentially different) value that is needed by a future use of -//! the locals. Unifying them would overwrite one of the values. +//! Because of the difficulty of computing liveness of places that have their address taken, we do +//! not even attempt to do it. Any places that are in a local that has its address taken is +//! excluded from the optimization. //! -//! Note that computing liveness of locals that have had their address taken is more difficult: -//! Short of doing full escape analysis on the address/pointer/reference, the pass would need to -//! assume that any operation that can potentially involve opaque user code (such as function -//! calls, destructors, and inline assembly) may access any local that had its address taken -//! before that point. +//! The first two conditions are simple structural requirements on the `Assign` statements that can +//! be trivially checked. The third requirement however is more difficult and costly to check. //! -//! Here, the first two conditions are simple structural requirements on the `Assign` statements -//! that can be trivially checked. The liveness requirement however is more difficult and costly to -//! check. +//! ## Future Improvements +//! +//! There are a number of ways in which this pass could be improved in the future: +//! +//! * Merging storage liveness ranges instead of removing storage statements completely. This may +//! improve stack usage. +//! +//! * Allow merging locals into places with projections, eg `_5` into `_6.foo`. +//! +//! * Liveness analysis with more precision than whole locals at a time. The smaller benefit of this +//! is that it would allow us to dest prop at "sub-local" levels in some cases. The bigger benefit +//! of this is that such liveness analysis can report more accurate results about whole locals at +//! a time. For example, consider: +//! +//! ```ignore (syntax-highliting-only) +//! _1 = u; +//! // unrelated code +//! _1.f1 = v; +//! _2 = _1.f1; +//! ``` +//! +//! Because the current analysis only thinks in terms of locals, it does not have enough +//! information to report that `_1` is dead in the "unrelated code" section. +//! +//! * Liveness analysis enabled by alias analysis. This would allow us to not just bail on locals +//! that ever have their address taken. Of course that requires actually having alias analysis +//! (and a model to build it on), so this might be a bit of a ways off. +//! +//! * Various perf improvents. There are a bunch of comments in here marked `PERF` with ideas for +//! how to do things more efficiently. However, the complexity of the pass as a whole should be +//! kept in mind. //! //! ## Previous Work //! -//! A [previous attempt] at implementing an optimization like this turned out to be a significant -//! regression in compiler performance. Fixing the regressions introduced a lot of undesirable -//! complexity to the implementation. +//! A [previous attempt][attempt 1] at implementing an optimization like this turned out to be a +//! significant regression in compiler performance. Fixing the regressions introduced a lot of +//! undesirable complexity to the implementation. +//! +//! A [subsequent approach][attempt 2] tried to avoid the costly computation by limiting itself to +//! acyclic CFGs, but still turned out to be far too costly to run due to suboptimal performance +//! within individual basic blocks, requiring a walk across the entire block for every assignment +//! found within the block. For the `tuple-stress` benchmark, which has 458745 statements in a +//! single block, this proved to be far too costly. //! -//! A [subsequent approach] tried to avoid the costly computation by limiting itself to acyclic -//! CFGs, but still turned out to be far too costly to run due to suboptimal performance within -//! individual basic blocks, requiring a walk across the entire block for every assignment found -//! within the block. For the `tuple-stress` benchmark, which has 458745 statements in a single -//! block, this proved to be far too costly. +//! [Another approach after that][attempt 3] was much closer to correct, but had some soundness +//! issues - it was failing to consider stores outside live ranges, and failed to uphold some of the +//! requirements that MIR has for non-overlapping places within statements. However, it also had +//! performance issues caused by `O(l² * s)` runtime, where `l` is the number of locals and `s` is +//! the number of statements and terminators. //! //! Since the first attempt at this, the compiler has improved dramatically, and new analysis //! frameworks have been added that should make this approach viable without requiring a limited @@ -74,8 +111,7 @@ //! - rustc now has a powerful dataflow analysis framework that can handle forwards and backwards //! analyses efficiently. //! - Layout optimizations for generators have been added to improve code generation for -//! async/await, which are very similar in spirit to what this optimization does. Both walk the -//! MIR and record conflicting uses of locals in a `BitMatrix`. +//! async/await, which are very similar in spirit to what this optimization does. //! //! Also, rustc now has a simple NRVO pass (see `nrvo.rs`), which handles a subset of the cases that //! this destination propagation pass handles, proving that similar optimizations can be performed @@ -87,253 +123,205 @@ //! it replaces the eliminated assign statements with `nop`s and leaves unused locals behind. //! //! [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis -//! [previous attempt]: https://github.com/rust-lang/rust/pull/47954 -//! [subsequent approach]: https://github.com/rust-lang/rust/pull/71003 +//! [attempt 1]: https://github.com/rust-lang/rust/pull/47954 +//! [attempt 2]: https://github.com/rust-lang/rust/pull/71003 +//! [attempt 3]: https://github.com/rust-lang/rust/pull/72632 + +use std::collections::hash_map::{Entry, OccupiedEntry}; use crate::MirPass; -use itertools::Itertools; -use rustc_data_structures::unify::{InPlaceUnificationTable, UnifyKey}; -use rustc_index::{ - bit_set::{BitMatrix, BitSet}, - vec::IndexVec, -}; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::{dump_mir, PassWhere}; use rustc_middle::mir::{ - traversal, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, PlaceElem, - Rvalue, Statement, StatementKind, Terminator, TerminatorKind, + traversal, BasicBlock, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, + Rvalue, Statement, StatementKind, TerminatorKind, +}; +use rustc_middle::mir::{ + visit::{MutVisitor, PlaceContext, Visitor}, + ProjectionElem, }; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{borrowed_locals, MaybeInitializedLocals, MaybeLiveLocals}; -use rustc_mir_dataflow::Analysis; - -// Empirical measurements have resulted in some observations: -// - Running on a body with a single block and 500 locals takes barely any time -// - Running on a body with ~400 blocks and ~300 relevant locals takes "too long" -// ...so we just limit both to somewhat reasonable-ish looking values. -const MAX_LOCALS: usize = 500; -const MAX_BLOCKS: usize = 250; +use rustc_mir_dataflow::impls::MaybeLiveLocals; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; pub struct DestinationPropagation; impl<'tcx> MirPass<'tcx> for DestinationPropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // FIXME(#79191, #82678): This is unsound. - // - // Only run at mir-opt-level=3 or higher for now (we don't fix up debuginfo and remove - // storage statements at the moment). - sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() >= 3 + // 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 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); + let mut allocations = Allocations::default(); + trace!(func = ?tcx.def_path_str(def_id)); - let candidates = find_candidates(body); - if candidates.is_empty() { - debug!("{:?}: no dest prop candidates, done", def_id); - return; - } + let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); - // Collect all locals we care about. We only compute conflicts for these to save time. - let mut relevant_locals = BitSet::new_empty(body.local_decls.len()); - for CandidateAssignment { dest, src, loc: _ } in &candidates { - relevant_locals.insert(dest.local); - relevant_locals.insert(*src); - } - - // This pass unfortunately has `O(l² * s)` performance, where `l` is the number of locals - // and `s` is the number of statements and terminators in the function. - // To prevent blowing up compile times too much, we bail out when there are too many locals. - let relevant = relevant_locals.count(); - debug!( - "{:?}: {} locals ({} relevant), {} blocks", - def_id, - body.local_decls.len(), - relevant, - body.basic_blocks.len() - ); - if relevant > MAX_LOCALS { - warn!( - "too many candidate locals in {:?} ({}, max is {}), not optimizing", - def_id, relevant, MAX_LOCALS + // In order to avoid having to collect data for every single pair of locals in the body, we + // do not allow doing more than one merge for places that are derived from the same local at + // once. To avoid missed opportunities, we instead iterate to a fixed point - we'll refer to + // each of these iterations as a "round." + // + // Reaching a fixed point could in theory take up to `min(l, s)` rounds - however, we do not + // expect to see MIR like that. To verify this, a test was run against `[rust-lang/regex]` - + // the average MIR body saw 1.32 full iterations of this loop. The most that was hit were 30 + // for a single function. Only 80/2801 (2.9%) of functions saw at least 5. + // + // [rust-lang/regex]: + // https://github.com/rust-lang/regex/tree/b5372864e2df6a2f5e543a556a62197f50ca3650 + let mut round_count = 0; + loop { + // PERF: Can we do something smarter than recalculating the candidates and liveness + // results? + let mut candidates = find_candidates( + body, + &borrowed, + &mut allocations.candidates, + &mut allocations.candidates_reverse, ); - return; - } - if body.basic_blocks.len() > MAX_BLOCKS { - warn!( - "too many blocks in {:?} ({}, max is {}), not optimizing", - def_id, - body.basic_blocks.len(), - MAX_BLOCKS + trace!(?candidates); + let mut live = MaybeLiveLocals + .into_engine(tcx, body) + .iterate_to_fixpoint() + .into_results_cursor(body); + dest_prop_mir_dump(tcx, body, &mut live, round_count); + + FilterInformation::filter_liveness( + &mut candidates, + &mut live, + &mut allocations.write_info, + body, ); - return; - } - let mut conflicts = Conflicts::build(tcx, body, &relevant_locals); + // Because we do not update liveness information, it is unsound to use a local for more + // than one merge operation within a single round of optimizations. We store here which + // ones we have already used. + let mut merged_locals: BitSet = BitSet::new_empty(body.local_decls.len()); - let mut replacements = Replacements::new(body.local_decls.len()); - for candidate @ CandidateAssignment { dest, src, loc } in candidates { - // Merge locals that don't conflict. - if !conflicts.can_unify(dest.local, src) { - debug!("at assignment {:?}, conflict {:?} vs. {:?}", loc, dest.local, src); - continue; - } + // This is the set of merges we will apply this round. It is a subset of the candidates. + let mut merges = FxHashMap::default(); - if replacements.for_src(candidate.src).is_some() { - debug!("src {:?} already has replacement", candidate.src); - continue; + for (src, candidates) in candidates.c.iter() { + if merged_locals.contains(*src) { + continue; + } + let Some(dest) = + candidates.iter().find(|dest| !merged_locals.contains(**dest)) else { + continue; + }; + if !tcx.consider_optimizing(|| { + format!("{} round {}", tcx.def_path_str(def_id), round_count) + }) { + break; + } + merges.insert(*src, *dest); + merged_locals.insert(*src); + merged_locals.insert(*dest); } + trace!(merging = ?merges); - if !tcx.consider_optimizing(|| { - format!("DestinationPropagation {:?} {:?}", def_id, candidate) - }) { + if merges.is_empty() { break; } + round_count += 1; - replacements.push(candidate); - conflicts.unify(candidate.src, candidate.dest.local); + apply_merges(body, tcx, &merges, &merged_locals); } - replacements.flatten(tcx); - - debug!("replacements {:?}", replacements.map); - - Replacer { tcx, replacements, place_elem_cache: Vec::new() }.visit_body(body); - - // FIXME fix debug info + trace!(round_count); } } -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -struct UnifyLocal(Local); - -impl From for UnifyLocal { - fn from(l: Local) -> Self { - Self(l) - } -} - -impl UnifyKey for UnifyLocal { - type Value = (); - #[inline] - fn index(&self) -> u32 { - self.0.as_u32() - } - #[inline] - fn from_index(u: u32) -> Self { - Self(Local::from_u32(u)) - } - fn tag() -> &'static str { - "UnifyLocal" - } +/// Container for the various allocations that we need. +/// +/// We store these here and hand out `&mut` access to them, instead of dropping and recreating them +/// frequently. Everything with a `&'alloc` lifetime points into here. +#[derive(Default)] +struct Allocations { + candidates: FxHashMap>, + candidates_reverse: FxHashMap>, + write_info: WriteInfo, + // PERF: Do this for `MaybeLiveLocals` allocations too. } -struct Replacements<'tcx> { - /// Maps locals to their replacement. - map: IndexVec>>, - - /// Whose locals' live ranges to kill. - kill: BitSet, +#[derive(Debug)] +struct Candidates<'alloc> { + /// The set of candidates we are considering in this optimization. + /// + /// We will always merge the key into at most one of its values. + /// + /// Whether a place ends up in the key or the value does not correspond to whether it appears as + /// the lhs or rhs of any assignment. As a matter of fact, the places in here might never appear + /// in an assignment at all. This happens because if we see an assignment like this: + /// + /// ```ignore (syntax-highlighting-only) + /// _1.0 = _2.0 + /// ``` + /// + /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to + /// remove that assignment. + c: &'alloc mut FxHashMap>, + /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`, + /// then this contains `b => a`. + // PERF: Possibly these should be `SmallVec`s? + reverse: &'alloc mut FxHashMap>, } -impl<'tcx> Replacements<'tcx> { - fn new(locals: usize) -> Self { - Self { map: IndexVec::from_elem_n(None, locals), kill: BitSet::new_empty(locals) } - } - - fn push(&mut self, candidate: CandidateAssignment<'tcx>) { - trace!("Replacements::push({:?})", candidate); - let entry = &mut self.map[candidate.src]; - assert!(entry.is_none()); - - *entry = Some(candidate.dest); - self.kill.insert(candidate.src); - self.kill.insert(candidate.dest.local); - } - - /// Applies the stored replacements to all replacements, until no replacements would result in - /// locals that need further replacements when applied. - fn flatten(&mut self, tcx: TyCtxt<'tcx>) { - // Note: This assumes that there are no cycles in the replacements, which is enforced via - // `self.unified_locals`. Otherwise this can cause an infinite loop. - - for local in self.map.indices() { - if let Some(replacement) = self.map[local] { - // Substitute the base local of `replacement` until fixpoint. - let mut base = replacement.local; - let mut reversed_projection_slices = Vec::with_capacity(1); - while let Some(replacement_for_replacement) = self.map[base] { - base = replacement_for_replacement.local; - reversed_projection_slices.push(replacement_for_replacement.projection); - } - - let projection: Vec<_> = reversed_projection_slices - .iter() - .rev() - .flat_map(|projs| projs.iter()) - .chain(replacement.projection.iter()) - .collect(); - let projection = tcx.intern_place_elems(&projection); +////////////////////////////////////////////////////////// +// Merging +// +// Applies the actual optimization - // Replace with the final `Place`. - self.map[local] = Some(Place { local: base, projection }); - } - } - } - - fn for_src(&self, src: Local) -> Option> { - self.map[src] - } +fn apply_merges<'tcx>( + body: &mut Body<'tcx>, + tcx: TyCtxt<'tcx>, + merges: &FxHashMap, + merged_locals: &BitSet, +) { + let mut merger = Merger { tcx, merges, merged_locals }; + merger.visit_body_preserves_cfg(body); } -struct Replacer<'tcx> { +struct Merger<'a, 'tcx> { tcx: TyCtxt<'tcx>, - replacements: Replacements<'tcx>, - place_elem_cache: Vec>, + merges: &'a FxHashMap, + merged_locals: &'a BitSet, } -impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn visit_local(&mut self, local: &mut Local, context: PlaceContext, location: Location) { - if context.is_use() && self.replacements.for_src(*local).is_some() { - bug!( - "use of local {:?} should have been replaced by visit_place; context={:?}, loc={:?}", - local, - context, - location, - ); + fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _location: Location) { + if let Some(dest) = self.merges.get(local) { + *local = *dest; } } - fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if let Some(replacement) = self.replacements.for_src(place.local) { - // Rebase `place`s projections onto `replacement`'s. - self.place_elem_cache.clear(); - self.place_elem_cache.extend(replacement.projection.iter().chain(place.projection)); - let projection = self.tcx.intern_place_elems(&self.place_elem_cache); - let new_place = Place { local: replacement.local, projection }; - - debug!("Replacer: {:?} -> {:?}", place, new_place); - *place = new_place; - } - - self.super_place(place, context, location); - } - fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { - self.super_statement(statement, location); - match &statement.kind { - // FIXME: Don't delete storage statements, merge the live ranges instead + // FIXME: Don't delete storage statements, but "merge" the storage ranges instead. StatementKind::StorageDead(local) | StatementKind::StorageLive(local) - if self.replacements.kill.contains(*local) => + if self.merged_locals.contains(*local) => { - statement.make_nop() + statement.make_nop(); + return; } - + _ => (), + }; + self.super_statement(statement, location); + match &statement.kind { StatementKind::Assign(box (dest, rvalue)) => { match rvalue { Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => { @@ -353,524 +341,427 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } } -struct Conflicts<'a> { - relevant_locals: &'a BitSet, - - /// The conflict matrix. It is always symmetric and the adjacency matrix of the corresponding - /// conflict graph. - matrix: BitMatrix, - - /// Preallocated `BitSet` used by `unify`. - unify_cache: BitSet, - - /// Tracks locals that have been merged together to prevent cycles and propagate conflicts. - unified_locals: InPlaceUnificationTable, +////////////////////////////////////////////////////////// +// Liveness filtering +// +// This section enforces bullet point 2 + +struct FilterInformation<'a, 'body, 'alloc, 'tcx> { + body: &'body Body<'tcx>, + live: &'a mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>, + candidates: &'a mut Candidates<'alloc>, + write_info: &'alloc mut WriteInfo, + at: Location, } -impl<'a> Conflicts<'a> { - fn build<'tcx>( - tcx: TyCtxt<'tcx>, - body: &'_ Body<'tcx>, - relevant_locals: &'a BitSet, - ) -> Self { - // We don't have to look out for locals that have their address taken, since - // `find_candidates` already takes care of that. - - let conflicts = BitMatrix::from_row_n( - &BitSet::new_empty(body.local_decls.len()), - body.local_decls.len(), - ); - - let mut init = MaybeInitializedLocals - .into_engine(tcx, body) - .iterate_to_fixpoint() - .into_results_cursor(body); - let mut live = - MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint().into_results_cursor(body); - - let mut reachable = None; - dump_mir(tcx, None, "DestinationPropagation-dataflow", &"", body, |pass_where, w| { - let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); - - match pass_where { - PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { - init.seek_before_primary_effect(loc); - live.seek_after_primary_effect(loc); - - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } - PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { - let loc = body.terminator_loc(bb); - init.seek_after_primary_effect(loc); - live.seek_before_primary_effect(loc); - - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } - - PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { - init.seek_to_block_start(bb); - live.seek_to_block_start(bb); - - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } - - PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} - - PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { - writeln!(w, " // init: ")?; - writeln!(w, " // live: ")?; - } - - PassWhere::BeforeBlock(_) => { - writeln!(w, " // init: ")?; - writeln!(w, " // live: ")?; - } +// We first implement some utility functions which we will expose removing candidates according to +// different needs. Throughout the livenss filtering, the `candidates` are only ever accessed +// through these methods, and not directly. +impl<'alloc> Candidates<'alloc> { + /// Just `Vec::retain`, but the condition is inverted and we add debugging output + fn vec_remove_debug( + src: Local, + v: &mut Vec, + mut f: impl FnMut(Local) -> bool, + at: Location, + ) { + v.retain(|dest| { + let remove = f(*dest); + if remove { + trace!("eliminating {:?} => {:?} due to conflict at {:?}", src, dest, at); } - - Ok(()) + !remove }); + } - let mut this = Self { - relevant_locals, - matrix: conflicts, - unify_cache: BitSet::new_empty(body.local_decls.len()), - unified_locals: { - let mut table = InPlaceUnificationTable::new(); - // Pre-fill table with all locals (this creates N nodes / "connected" components, - // "graph"-ically speaking). - for local in 0..body.local_decls.len() { - assert_eq!(table.new_key(()), UnifyLocal(Local::from_usize(local))); - } - table - }, - }; - - let mut live_and_init_locals = Vec::new(); - - // Visit only reachable basic blocks. The exact order is not important. - for (block, data) in traversal::preorder(body) { - // We need to observe the dataflow state *before* all possible locations (statement or - // terminator) in each basic block, and then observe the state *after* the terminator - // effect is applied. As long as neither `init` nor `borrowed` has a "before" effect, - // we will observe all possible dataflow states. - - // Since liveness is a backwards analysis, we need to walk the results backwards. To do - // that, we first collect in the `MaybeInitializedLocals` results in a forwards - // traversal. - - live_and_init_locals.resize_with(data.statements.len() + 1, || { - BitSet::new_empty(body.local_decls.len()) - }); - - // First, go forwards for `MaybeInitializedLocals` and apply intra-statement/terminator - // conflicts. - for (i, statement) in data.statements.iter().enumerate() { - this.record_statement_conflicts(statement); - - let loc = Location { block, statement_index: i }; - init.seek_before_primary_effect(loc); + /// `vec_remove_debug` but for an `Entry` + fn entry_remove( + mut entry: OccupiedEntry<'_, Local, Vec>, + p: Local, + f: impl FnMut(Local) -> bool, + at: Location, + ) { + let candidates = entry.get_mut(); + Self::vec_remove_debug(p, candidates, f, at); + if candidates.len() == 0 { + entry.remove(); + } + } - live_and_init_locals[i].clone_from(init.get()); + /// Removes all candidates `(p, q)` or `(q, p)` where `p` is the indicated local and `f(q)` is true. + fn remove_candidates_if(&mut self, p: Local, mut f: impl FnMut(Local) -> bool, at: Location) { + // Cover the cases where `p` appears as a `src` + if let Entry::Occupied(entry) = self.c.entry(p) { + Self::entry_remove(entry, p, &mut f, at); + } + // And the cases where `p` appears as a `dest` + let Some(srcs) = self.reverse.get_mut(&p) else { + return; + }; + // We use `retain` here to remove the elements from the reverse set if we've removed the + // matching candidate in the forward set. + srcs.retain(|src| { + if !f(*src) { + return true; } + let Entry::Occupied(entry) = self.c.entry(*src) else { + return false; + }; + Self::entry_remove(entry, *src, |dest| dest == p, at); + false + }); + } +} - this.record_terminator_conflicts(data.terminator()); - let term_loc = Location { block, statement_index: data.statements.len() }; - init.seek_before_primary_effect(term_loc); - live_and_init_locals[term_loc.statement_index].clone_from(init.get()); - - // Now, go backwards and union with the liveness results. - for statement_index in (0..=data.statements.len()).rev() { - let loc = Location { block, statement_index }; - live.seek_after_primary_effect(loc); - - live_and_init_locals[statement_index].intersect(live.get()); - - trace!("record conflicts at {:?}", loc); +impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { + /// Filters the set of candidates to remove those that conflict. + /// + /// The steps we take are exactly those that are outlined at the top of the file. For each + /// statement/terminator, we collect the set of locals that are written to in that + /// statement/terminator, and then we remove all pairs of candidates that contain one such local + /// and another one that is live. + /// + /// We need to be careful about the ordering of operations within each statement/terminator + /// here. Many statements might write and read from more than one place, and we need to consider + /// them all. The strategy for doing this is as follows: We first gather all the places that are + /// written to within the statement/terminator via `WriteInfo`. Then, we use the liveness + /// analysis from *before* the statement/terminator (in the control flow sense) to eliminate + /// candidates - this is because we want to conservatively treat a pair of locals that is both + /// read and written in the statement/terminator to be conflicting, and the liveness analysis + /// before the statement/terminator will correctly report locals that are read in the + /// statement/terminator to be live. We are additionally conservative by treating all written to + /// locals as also being read from. + fn filter_liveness<'b>( + candidates: &mut Candidates<'alloc>, + live: &mut ResultsCursor<'b, 'tcx, MaybeLiveLocals>, + write_info_alloc: &'alloc mut WriteInfo, + body: &'b Body<'tcx>, + ) { + let mut this = FilterInformation { + body, + live, + candidates, + // We don't actually store anything at this scope, we just keep things here to be able + // to reuse the allocation. + write_info: write_info_alloc, + // Doesn't matter what we put here, will be overwritten before being used + at: Location { block: BasicBlock::from_u32(0), statement_index: 0 }, + }; + this.internal_filter_liveness(); + } - this.record_dataflow_conflicts(&mut live_and_init_locals[statement_index]); + fn internal_filter_liveness(&mut self) { + for (block, data) in traversal::preorder(self.body) { + self.at = Location { block, statement_index: data.statements.len() }; + self.live.seek_after_primary_effect(self.at); + self.write_info.for_terminator(&data.terminator().kind); + self.apply_conflicts(); + + for (i, statement) in data.statements.iter().enumerate().rev() { + self.at = Location { block, statement_index: i }; + self.live.seek_after_primary_effect(self.at); + self.get_statement_write_info(&statement.kind); + self.apply_conflicts(); } - - init.seek_to_block_end(block); - live.seek_to_block_end(block); - let mut conflicts = init.get().clone(); - conflicts.intersect(live.get()); - trace!("record conflicts at end of {:?}", block); - - this.record_dataflow_conflicts(&mut conflicts); } - - this } - fn record_dataflow_conflicts(&mut self, new_conflicts: &mut BitSet) { - // Remove all locals that are not candidates. - new_conflicts.intersect(self.relevant_locals); + fn apply_conflicts(&mut self) { + let writes = &self.write_info.writes; + for p in writes { + self.candidates.remove_candidates_if( + *p, + // It is possible that a local may be live for less than the + // duration of a statement This happens in the case of function + // calls or inline asm. Because of this, we also mark locals as + // conflicting when both of them are written to in the same + // statement. + |q| self.live.contains(q) || writes.contains(&q), + self.at, + ); + } + } - for local in new_conflicts.iter() { - self.matrix.union_row_with(&new_conflicts, local); + /// Gets the write info for the `statement`. + fn get_statement_write_info(&mut self, statement: &StatementKind<'tcx>) { + self.write_info.writes.clear(); + match statement { + StatementKind::Assign(box (lhs, rhs)) => match rhs { + Rvalue::Use(op) => { + if !lhs.is_indirect() { + self.get_assign_use_write_info(*lhs, op); + return; + } + } + _ => (), + }, + _ => (), } + + self.write_info.for_statement(statement); } - fn record_local_conflict(&mut self, a: Local, b: Local, why: &str) { - trace!("conflict {:?} <-> {:?} due to {}", a, b, why); - self.matrix.insert(a, b); - self.matrix.insert(b, a); + fn get_assign_use_write_info(&mut self, lhs: Place<'tcx>, rhs: &Operand<'tcx>) { + // We register the writes for the operand unconditionally + self.write_info.add_operand(rhs); + // However, we cannot do the same thing for the `lhs` as that would always block the + // optimization. Instead, we consider removing candidates manually. + let Some(rhs) = rhs.place() else { + self.write_info.add_place(lhs); + return; + }; + // Find out which candidate pair we should skip, if any + let Some((src, dest)) = places_to_candidate_pair(lhs, rhs, self.body) else { + self.write_info.add_place(lhs); + return; + }; + self.candidates.remove_candidates_if( + lhs.local, + |other| { + // Check if this is the candidate pair that should not be removed + if (lhs.local == src && other == dest) || (lhs.local == dest && other == src) { + return false; + } + // Otherwise, do the "standard" thing + self.live.contains(other) + }, + self.at, + ) } +} - /// Records locals that must not overlap during the evaluation of `stmt`. These locals conflict - /// and must not be merged. - fn record_statement_conflicts(&mut self, stmt: &Statement<'_>) { - match &stmt.kind { - // While the left and right sides of an assignment must not overlap, we do not mark - // conflicts here as that would make this optimization useless. When we optimize, we - // eliminate the resulting self-assignments automatically. - StatementKind::Assign(_) => {} - - StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) - | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::Retag(..) - | StatementKind::FakeRead(..) - | StatementKind::AscribeUserType(..) - | StatementKind::Coverage(..) - | StatementKind::Intrinsic(..) - | StatementKind::Nop => {} +/// Describes where a statement/terminator writes to +#[derive(Default, Debug)] +struct WriteInfo { + writes: Vec, +} + +impl WriteInfo { + fn for_statement<'tcx>(&mut self, statement: &StatementKind<'tcx>) { + match statement { + StatementKind::Assign(box (lhs, rhs)) => { + self.add_place(*lhs); + match rhs { + Rvalue::Use(op) | Rvalue::Repeat(op, _) => { + self.add_operand(op); + } + Rvalue::Cast(_, op, _) + | Rvalue::UnaryOp(_, op) + | Rvalue::ShallowInitBox(op, _) => { + self.add_operand(op); + } + Rvalue::BinaryOp(_, ops) | Rvalue::CheckedBinaryOp(_, ops) => { + for op in [&ops.0, &ops.1] { + self.add_operand(op); + } + } + Rvalue::Aggregate(_, ops) => { + for op in ops { + self.add_operand(op); + } + } + Rvalue::ThreadLocalRef(_) + | Rvalue::NullaryOp(_, _) + | Rvalue::Ref(_, _, _) + | Rvalue::AddressOf(_, _) + | Rvalue::Len(_) + | Rvalue::Discriminant(_) + | Rvalue::CopyForDeref(_) => (), + } + } + // Retags are technically also reads, but reporting them as a write suffices + StatementKind::SetDiscriminant { place, .. } + | StatementKind::Deinit(place) + | StatementKind::Retag(_, place) => { + self.add_place(**place); + } + StatementKind::Intrinsic(_) + | StatementKind::Nop + | StatementKind::Coverage(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) => (), + StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { + bug!("{:?} not found in this MIR phase", statement) + } } } - fn record_terminator_conflicts(&mut self, term: &Terminator<'_>) { - match &term.kind { - TerminatorKind::DropAndReplace { - place: dropped_place, - value, - target: _, - unwind: _, - } => { - if let Some(place) = value.place() - && !place.is_indirect() - && !dropped_place.is_indirect() - { - self.record_local_conflict( - place.local, - dropped_place.local, - "DropAndReplace operand overlap", - ); - } + fn for_terminator<'tcx>(&mut self, terminator: &TerminatorKind<'tcx>) { + self.writes.clear(); + match terminator { + TerminatorKind::SwitchInt { discr: op, .. } + | TerminatorKind::Assert { cond: op, .. } => { + self.add_operand(op); } - TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => { - if let Some(place) = value.place() { - if !place.is_indirect() && !resume_arg.is_indirect() { - self.record_local_conflict( - place.local, - resume_arg.local, - "Yield operand overlap", - ); - } + TerminatorKind::Call { destination, func, args, .. } => { + self.add_place(*destination); + self.add_operand(func); + for arg in args { + self.add_operand(arg); } } - TerminatorKind::Call { - func, - args, - destination, - target: _, - cleanup: _, - from_hir_call: _, - fn_span: _, - } => { - // No arguments may overlap with the destination. - for arg in args.iter().chain(Some(func)) { - if let Some(place) = arg.place() { - if !place.is_indirect() && !destination.is_indirect() { - self.record_local_conflict( - destination.local, - place.local, - "call dest/arg overlap", - ); + TerminatorKind::InlineAsm { operands, .. } => { + for asm_operand in operands { + match asm_operand { + InlineAsmOperand::In { value, .. } => { + self.add_operand(value); } - } - } - } - TerminatorKind::InlineAsm { - template: _, - operands, - options: _, - line_spans: _, - destination: _, - cleanup: _, - } => { - // The intended semantics here aren't documented, we just assume that nothing that - // could be written to by the assembly may overlap with any other operands. - for op in operands { - match op { - InlineAsmOperand::Out { reg: _, late: _, place: Some(dest_place) } - | InlineAsmOperand::InOut { - reg: _, - late: _, - in_value: _, - out_place: Some(dest_place), - } => { - // For output place `place`, add all places accessed by the inline asm. - for op in operands { - match op { - InlineAsmOperand::In { reg: _, value } => { - if let Some(p) = value.place() - && !p.is_indirect() - && !dest_place.is_indirect() - { - self.record_local_conflict( - p.local, - dest_place.local, - "asm! operand overlap", - ); - } - } - InlineAsmOperand::Out { - reg: _, - late: _, - place: Some(place), - } => { - if !place.is_indirect() && !dest_place.is_indirect() { - self.record_local_conflict( - place.local, - dest_place.local, - "asm! operand overlap", - ); - } - } - InlineAsmOperand::InOut { - reg: _, - late: _, - in_value, - out_place, - } => { - if let Some(place) = in_value.place() - && !place.is_indirect() - && !dest_place.is_indirect() - { - self.record_local_conflict( - place.local, - dest_place.local, - "asm! operand overlap", - ); - } - - if let Some(place) = out_place - && !place.is_indirect() - && !dest_place.is_indirect() - { - self.record_local_conflict( - place.local, - dest_place.local, - "asm! operand overlap", - ); - } - } - InlineAsmOperand::Out { reg: _, late: _, place: None } - | InlineAsmOperand::Const { value: _ } - | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} - } + InlineAsmOperand::Out { place, .. } => { + if let Some(place) = place { + self.add_place(*place); } } - InlineAsmOperand::InOut { - reg: _, - late: _, - in_value: _, - out_place: None, + // Note that the `late` field in `InOut` is about whether the registers used + // for these things overlap, and is of absolutely no interest to us. + InlineAsmOperand::InOut { in_value, out_place, .. } => { + if let Some(place) = out_place { + self.add_place(*place); + } + self.add_operand(in_value); } - | InlineAsmOperand::In { reg: _, value: _ } - | InlineAsmOperand::Out { reg: _, late: _, place: None } - | InlineAsmOperand::Const { value: _ } - | InlineAsmOperand::SymFn { value: _ } - | InlineAsmOperand::SymStatic { def_id: _ } => {} + InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::SymStatic { .. } => (), } } } - TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Resume - | TerminatorKind::Abort + | TerminatorKind::Resume { .. } + | TerminatorKind::Abort { .. } | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::Assert { .. } + | TerminatorKind::Unreachable { .. } => (), + TerminatorKind::Drop { .. } => { + // `Drop`s create a `&mut` and so are not considered + } + TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => {} + | TerminatorKind::FalseUnwind { .. } => { + bug!("{:?} not found in this MIR phase", terminator) + } } } - /// Checks whether `a` and `b` may be merged. Returns `false` if there's a conflict. - fn can_unify(&mut self, a: Local, b: Local) -> bool { - // After some locals have been unified, their conflicts are only tracked in the root key, - // so look that up. - let a = self.unified_locals.find(a).0; - let b = self.unified_locals.find(b).0; - - if a == b { - // Already merged (part of the same connected component). - return false; - } + fn add_place<'tcx>(&mut self, place: Place<'tcx>) { + self.writes.push(place.local); + } - if self.matrix.contains(a, b) { - // Conflict (derived via dataflow, intra-statement conflicts, or inherited from another - // local during unification). - return false; + fn add_operand<'tcx>(&mut self, op: &Operand<'tcx>) { + match op { + // FIXME(JakobDegen): In a previous version, the `Move` case was incorrectly treated as + // being a read only. This was unsound, however we cannot add a regression test because + // it is not possible to set this off with current MIR. Once we have that ability, a + // regression test should be added. + Operand::Move(p) => self.add_place(*p), + Operand::Copy(_) | Operand::Constant(_) => (), } - - true } +} - /// Merges the conflicts of `a` and `b`, so that each one inherits all conflicts of the other. - /// - /// `can_unify` must have returned `true` for the same locals, or this may panic or lead to - /// miscompiles. - /// - /// This is called when the pass makes the decision to unify `a` and `b` (or parts of `a` and - /// `b`) and is needed to ensure that future unification decisions take potentially newly - /// introduced conflicts into account. - /// - /// For an example, assume we have locals `_0`, `_1`, `_2`, and `_3`. There are these conflicts: - /// - /// * `_0` <-> `_1` - /// * `_1` <-> `_2` - /// * `_3` <-> `_0` - /// - /// We then decide to merge `_2` with `_3` since they don't conflict. Then we decide to merge - /// `_2` with `_0`, which also doesn't have a conflict in the above list. However `_2` is now - /// `_3`, which does conflict with `_0`. - fn unify(&mut self, a: Local, b: Local) { - trace!("unify({:?}, {:?})", a, b); - - // Get the root local of the connected components. The root local stores the conflicts of - // all locals in the connected component (and *is stored* as the conflicting local of other - // locals). - let a = self.unified_locals.find(a).0; - let b = self.unified_locals.find(b).0; - assert_ne!(a, b); - - trace!("roots: a={:?}, b={:?}", a, b); - trace!("{:?} conflicts: {:?}", a, self.matrix.iter(a).format(", ")); - trace!("{:?} conflicts: {:?}", b, self.matrix.iter(b).format(", ")); - - self.unified_locals.union(a, b); - - let root = self.unified_locals.find(a).0; - assert!(root == a || root == b); - - // Make all locals that conflict with `a` also conflict with `b`, and vice versa. - self.unify_cache.clear(); - for conflicts_with_a in self.matrix.iter(a) { - self.unify_cache.insert(conflicts_with_a); - } - for conflicts_with_b in self.matrix.iter(b) { - self.unify_cache.insert(conflicts_with_b); - } - for conflicts_with_a_or_b in self.unify_cache.iter() { - // Set both `a` and `b` for this local's row. - self.matrix.insert(conflicts_with_a_or_b, a); - self.matrix.insert(conflicts_with_a_or_b, b); - } +///////////////////////////////////////////////////// +// Candidate accumulation - // Write the locals `a` conflicts with to `b`'s row. - self.matrix.union_rows(a, b); - // Write the locals `b` conflicts with to `a`'s row. - self.matrix.union_rows(b, a); - } +fn is_constant<'tcx>(place: Place<'tcx>) -> bool { + place.projection.iter().all(|p| !matches!(p, ProjectionElem::Deref | ProjectionElem::Index(_))) } -/// A `dest = {move} src;` statement at `loc`. +/// If the pair of places is being considered for merging, returns the candidate which would be +/// merged in order to accomplish this. +/// +/// The contract here is in one direction - there is a guarantee that merging the locals that are +/// outputted by this function would result in an assignment between the inputs becoming a +/// self-assignment. However, there is no guarantee that the returned pair is actually suitable for +/// merging - candidate collection must still check this independently. /// -/// We want to consider merging `dest` and `src` due to this assignment. -#[derive(Debug, Copy, Clone)] -struct CandidateAssignment<'tcx> { - /// Does not contain indirection or indexing (so the only local it contains is the place base). - dest: Place<'tcx>, - src: Local, - loc: Location, +/// This output is unique for each unordered pair of input places. +fn places_to_candidate_pair<'tcx>( + a: Place<'tcx>, + b: Place<'tcx>, + body: &Body<'tcx>, +) -> Option<(Local, Local)> { + let (mut a, mut b) = if a.projection.len() == 0 && b.projection.len() == 0 { + (a.local, b.local) + } else { + return None; + }; + + // By sorting, we make sure we're input order independent + if a > b { + std::mem::swap(&mut a, &mut b); + } + + // We could now return `(a, b)`, but then we miss some candidates in the case where `a` can't be + // used as a `src`. + if is_local_required(a, body) { + std::mem::swap(&mut a, &mut b); + } + // We could check `is_local_required` again here, but there's no need - after all, we make no + // promise that the candidate pair is actually valid + Some((a, b)) } -/// Scans the MIR for assignments between locals that we might want to consider merging. +/// Collects the candidates for merging /// -/// This will filter out assignments that do not match the right form (as described in the top-level -/// comment) and also throw out assignments that involve a local that has its address taken or is -/// otherwise ineligible (eg. locals used as array indices are ignored because we cannot propagate -/// arbitrary places into array indices). -fn find_candidates<'tcx>(body: &Body<'tcx>) -> Vec> { - let mut visitor = FindAssignments { - body, - candidates: Vec::new(), - ever_borrowed_locals: borrowed_locals(body), - locals_used_as_array_index: locals_used_as_array_index(body), - }; +/// This is responsible for enforcing the first and third bullet point. +fn find_candidates<'alloc, 'tcx>( + body: &Body<'tcx>, + borrowed: &BitSet, + candidates: &'alloc mut FxHashMap>, + candidates_reverse: &'alloc mut FxHashMap>, +) -> Candidates<'alloc> { + candidates.clear(); + candidates_reverse.clear(); + let mut visitor = FindAssignments { body, candidates, borrowed }; visitor.visit_body(body); - visitor.candidates + // Deduplicate candidates + for (_, cands) in candidates.iter_mut() { + cands.sort(); + cands.dedup(); + } + // Generate the reverse map + for (src, cands) in candidates.iter() { + for dest in cands.iter().copied() { + candidates_reverse.entry(dest).or_default().push(*src); + } + } + Candidates { c: candidates, reverse: candidates_reverse } } -struct FindAssignments<'a, 'tcx> { +struct FindAssignments<'a, 'alloc, 'tcx> { body: &'a Body<'tcx>, - candidates: Vec>, - ever_borrowed_locals: BitSet, - locals_used_as_array_index: BitSet, + candidates: &'alloc mut FxHashMap>, + borrowed: &'a BitSet, } -impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { +impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { if let StatementKind::Assign(box ( - dest, - Rvalue::Use(Operand::Copy(src) | Operand::Move(src)), + lhs, + Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), )) = &statement.kind { - // `dest` must not have pointer indirection. - if dest.is_indirect() { - return; - } - - // `src` must be a plain local. - if !src.projection.is_empty() { + if !is_constant(*lhs) || !is_constant(*rhs) { return; } - // Since we want to replace `src` with `dest`, `src` must not be required. - if is_local_required(src.local, self.body) { + let Some((src, dest)) = places_to_candidate_pair(*lhs, *rhs, self.body) else { return; - } + }; - // Can't optimize if either local ever has their address taken. This optimization does - // liveness analysis only based on assignments, and a local can be live even if its - // never assigned to again, because a reference to it might be live. - // FIXME: This can be smarter and take `StorageDead` into account (which invalidates - // borrows). - if self.ever_borrowed_locals.contains(dest.local) - || self.ever_borrowed_locals.contains(src.local) - { + // As described at the top of the file, we do not go near things that have their address + // taken. + if self.borrowed.contains(src) || self.borrowed.contains(dest) { return; } - assert_ne!(dest.local, src.local, "self-assignments are UB"); - - // We can't replace locals occurring in `PlaceElem::Index` for now. - if self.locals_used_as_array_index.contains(src.local) { + // Also, we need to make sure that MIR actually allows the `src` to be removed + if is_local_required(src, self.body) { return; } - for elem in dest.projection { - if let PlaceElem::Index(_) = elem { - // `dest` contains an indexing projection. - return; - } - } - - self.candidates.push(CandidateAssignment { - dest: *dest, - src: src.local, - loc: location, - }); + // We may insert duplicates here, but that's fine + self.candidates.entry(src).or_default().push(dest); } } } @@ -886,32 +777,46 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool { } } -/// `PlaceElem::Index` only stores a `Local`, so we can't replace that with a full `Place`. -/// -/// Collect locals used as indices so we don't generate candidates that are impossible to apply -/// later. -fn locals_used_as_array_index(body: &Body<'_>) -> BitSet { - let mut visitor = IndexCollector { locals: BitSet::new_empty(body.local_decls.len()) }; - visitor.visit_body(body); - visitor.locals -} +///////////////////////////////////////////////////////// +// MIR Dump -struct IndexCollector { - locals: BitSet, -} +fn dest_prop_mir_dump<'body, 'tcx>( + tcx: TyCtxt<'tcx>, + body: &'body Body<'tcx>, + live: &mut ResultsCursor<'body, 'tcx, MaybeLiveLocals>, + round: usize, +) { + let mut reachable = None; + dump_mir(tcx, None, "DestinationPropagation-dataflow", &round, body, |pass_where, w| { + let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); + + match pass_where { + PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { + live.seek_after_primary_effect(loc); + writeln!(w, " // live: {:?}", live.get())?; + } + PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { + let loc = body.terminator_loc(bb); + live.seek_before_primary_effect(loc); + writeln!(w, " // live: {:?}", live.get())?; + } -impl<'tcx> Visitor<'tcx> for IndexCollector { - fn visit_projection_elem( - &mut self, - local: Local, - proj_base: &[PlaceElem<'tcx>], - elem: PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - if let PlaceElem::Index(i) = elem { - self.locals.insert(i); + PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { + live.seek_to_block_start(bb); + writeln!(w, " // live: {:?}", live.get())?; + } + + PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} + + PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { + writeln!(w, " // live: ")?; + } + + PassWhere::BeforeBlock(_) => { + writeln!(w, " // live: ")?; + } } - self.super_projection_elem(local, proj_base, elem, context, location); - } + + Ok(()) + }); } diff --git a/src/test/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination.diff b/src/test/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination.diff index 58dd788b6afca..61d24c3b517fb 100644 --- a/src/test/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination.diff +++ b/src/test/mir-opt/dead-store-elimination/cycle.cycle.DeadStoreElimination.diff @@ -6,17 +6,20 @@ debug y => _2; // in scope 0 at $DIR/cycle.rs:+0:22: +0:27 debug z => _3; // in scope 0 at $DIR/cycle.rs:+0:34: +0:39 let mut _0: (); // return place in scope 0 at $DIR/cycle.rs:+0:46: +0:46 - let mut _4: (); // in scope 0 at $DIR/cycle.rs:+0:1: +9:2 - let mut _5: bool; // in scope 0 at $DIR/cycle.rs:+3:11: +3:17 - let _6: i32; // in scope 0 at $DIR/cycle.rs:+4:13: +4:17 - let mut _7: i32; // in scope 0 at $DIR/cycle.rs:+5:13: +5:14 - let mut _8: i32; // in scope 0 at $DIR/cycle.rs:+6:13: +6:14 - let mut _9: i32; // in scope 0 at $DIR/cycle.rs:+7:13: +7:17 - let mut _10: !; // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 - let _11: (); // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 - let mut _12: !; // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 +- let mut _4: (); // in scope 0 at $DIR/cycle.rs:+0:1: +9:2 +- let mut _5: bool; // in scope 0 at $DIR/cycle.rs:+3:11: +3:17 +- let _6: i32; // in scope 0 at $DIR/cycle.rs:+4:13: +4:17 +- let mut _7: i32; // in scope 0 at $DIR/cycle.rs:+5:13: +5:14 +- let mut _8: i32; // in scope 0 at $DIR/cycle.rs:+6:13: +6:14 +- let mut _9: i32; // in scope 0 at $DIR/cycle.rs:+7:13: +7:17 +- let mut _10: !; // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 +- let _11: (); // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 +- let mut _12: !; // in scope 0 at $DIR/cycle.rs:+3:5: +8:6 ++ let mut _4: bool; // in scope 0 at $DIR/cycle.rs:+3:11: +3:17 ++ let _5: i32; // in scope 0 at $DIR/cycle.rs:+4:13: +4:17 scope 1 { - debug temp => _6; // in scope 1 at $DIR/cycle.rs:+4:13: +4:17 +- debug temp => _6; // in scope 1 at $DIR/cycle.rs:+4:13: +4:17 ++ debug temp => _5; // in scope 1 at $DIR/cycle.rs:+4:13: +4:17 } bb0: { @@ -24,51 +27,57 @@ } bb1: { - StorageLive(_5); // scope 0 at $DIR/cycle.rs:+3:11: +3:17 - _5 = cond() -> bb2; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 +- StorageLive(_5); // scope 0 at $DIR/cycle.rs:+3:11: +3:17 +- _5 = cond() -> bb2; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 ++ StorageLive(_4); // scope 0 at $DIR/cycle.rs:+3:11: +3:17 ++ _4 = cond() -> bb2; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 // mir::Constant // + span: $DIR/cycle.rs:12:11: 12:15 // + literal: Const { ty: fn() -> bool {cond}, val: Value() } } bb2: { - switchInt(move _5) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 +- switchInt(move _5) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 ++ switchInt(move _4) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/cycle.rs:+3:11: +3:17 } bb3: { - StorageLive(_6); // scope 0 at $DIR/cycle.rs:+4:13: +4:17 +- StorageLive(_6); // scope 0 at $DIR/cycle.rs:+4:13: +4:17 - _6 = _3; // scope 0 at $DIR/cycle.rs:+4:20: +4:21 -+ nop; // scope 0 at $DIR/cycle.rs:+4:20: +4:21 - StorageLive(_7); // scope 1 at $DIR/cycle.rs:+5:13: +5:14 +- StorageLive(_7); // scope 1 at $DIR/cycle.rs:+5:13: +5:14 - _7 = _2; // scope 1 at $DIR/cycle.rs:+5:13: +5:14 - _3 = move _7; // scope 1 at $DIR/cycle.rs:+5:9: +5:14 -+ nop; // scope 1 at $DIR/cycle.rs:+5:13: +5:14 -+ nop; // scope 1 at $DIR/cycle.rs:+5:9: +5:14 - StorageDead(_7); // scope 1 at $DIR/cycle.rs:+5:13: +5:14 - StorageLive(_8); // scope 1 at $DIR/cycle.rs:+6:13: +6:14 +- StorageDead(_7); // scope 1 at $DIR/cycle.rs:+5:13: +5:14 +- StorageLive(_8); // scope 1 at $DIR/cycle.rs:+6:13: +6:14 - _8 = _1; // scope 1 at $DIR/cycle.rs:+6:13: +6:14 - _2 = move _8; // scope 1 at $DIR/cycle.rs:+6:9: +6:14 -+ nop; // scope 1 at $DIR/cycle.rs:+6:13: +6:14 -+ nop; // scope 1 at $DIR/cycle.rs:+6:9: +6:14 - StorageDead(_8); // scope 1 at $DIR/cycle.rs:+6:13: +6:14 - StorageLive(_9); // scope 1 at $DIR/cycle.rs:+7:13: +7:17 +- StorageDead(_8); // scope 1 at $DIR/cycle.rs:+6:13: +6:14 +- StorageLive(_9); // scope 1 at $DIR/cycle.rs:+7:13: +7:17 - _9 = _6; // scope 1 at $DIR/cycle.rs:+7:13: +7:17 - _1 = move _9; // scope 1 at $DIR/cycle.rs:+7:9: +7:17 +- StorageDead(_9); // scope 1 at $DIR/cycle.rs:+7:16: +7:17 +- _4 = const (); // scope 0 at $DIR/cycle.rs:+3:18: +8:6 +- StorageDead(_6); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 ++ StorageLive(_5); // scope 0 at $DIR/cycle.rs:+4:13: +4:17 ++ nop; // scope 0 at $DIR/cycle.rs:+4:20: +4:21 ++ nop; // scope 1 at $DIR/cycle.rs:+5:13: +5:14 ++ nop; // scope 1 at $DIR/cycle.rs:+5:9: +5:14 ++ nop; // scope 1 at $DIR/cycle.rs:+6:13: +6:14 ++ nop; // scope 1 at $DIR/cycle.rs:+6:9: +6:14 + nop; // scope 1 at $DIR/cycle.rs:+7:13: +7:17 + nop; // scope 1 at $DIR/cycle.rs:+7:9: +7:17 - StorageDead(_9); // scope 1 at $DIR/cycle.rs:+7:16: +7:17 -- _4 = const (); // scope 0 at $DIR/cycle.rs:+3:18: +8:6 + nop; // scope 0 at $DIR/cycle.rs:+3:18: +8:6 - StorageDead(_6); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 StorageDead(_5); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 ++ StorageDead(_4); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 goto -> bb1; // scope 0 at $DIR/cycle.rs:+3:5: +8:6 } bb4: { - StorageLive(_11); // scope 0 at $DIR/cycle.rs:+3:5: +8:6 +- StorageLive(_11); // scope 0 at $DIR/cycle.rs:+3:5: +8:6 _0 = const (); // scope 0 at $DIR/cycle.rs:+3:5: +8:6 - StorageDead(_11); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 - StorageDead(_5); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 +- StorageDead(_11); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 +- StorageDead(_5); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 ++ StorageDead(_4); // scope 0 at $DIR/cycle.rs:+8:5: +8:6 return; // scope 0 at $DIR/cycle.rs:+9:2: +9:2 } } diff --git a/src/test/mir-opt/dest-prop/branch.main.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/branch.foo.DestinationPropagation.diff similarity index 56% rename from src/test/mir-opt/dest-prop/branch.main.DestinationPropagation.diff rename to src/test/mir-opt/dest-prop/branch.foo.DestinationPropagation.diff index 8929f2cc779a7..5fa7013d5ca76 100644 --- a/src/test/mir-opt/dest-prop/branch.main.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/branch.foo.DestinationPropagation.diff @@ -1,29 +1,34 @@ -- // MIR for `main` before DestinationPropagation -+ // MIR for `main` after DestinationPropagation +- // MIR for `foo` before DestinationPropagation ++ // MIR for `foo` after DestinationPropagation - fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/branch.rs:+0:11: +0:11 + fn foo() -> i32 { + let mut _0: i32; // return place in scope 0 at $DIR/branch.rs:+0:13: +0:16 let _1: i32; // in scope 0 at $DIR/branch.rs:+1:9: +1:10 let mut _3: bool; // in scope 0 at $DIR/branch.rs:+3:16: +3:22 let _4: i32; // in scope 0 at $DIR/branch.rs:+6:9: +6:14 scope 1 { - debug x => _1; // in scope 1 at $DIR/branch.rs:+1:9: +1:10 +- debug x => _1; // in scope 1 at $DIR/branch.rs:+1:9: +1:10 ++ debug x => _0; // in scope 1 at $DIR/branch.rs:+1:9: +1:10 let _2: i32; // in scope 1 at $DIR/branch.rs:+3:9: +3:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/branch.rs:+3:9: +3:10 +- debug y => _2; // in scope 2 at $DIR/branch.rs:+3:9: +3:10 ++ debug y => _0; // in scope 2 at $DIR/branch.rs:+3:9: +3:10 } } bb0: { - StorageLive(_1); // scope 0 at $DIR/branch.rs:+1:9: +1:10 - _1 = val() -> bb1; // scope 0 at $DIR/branch.rs:+1:13: +1:18 +- StorageLive(_1); // scope 0 at $DIR/branch.rs:+1:9: +1:10 +- _1 = val() -> bb1; // scope 0 at $DIR/branch.rs:+1:13: +1:18 ++ nop; // scope 0 at $DIR/branch.rs:+1:9: +1:10 ++ _0 = val() -> bb1; // scope 0 at $DIR/branch.rs:+1:13: +1:18 // mir::Constant // + span: $DIR/branch.rs:13:13: 13:16 // + literal: Const { ty: fn() -> i32 {val}, val: Value() } } bb1: { - StorageLive(_2); // scope 1 at $DIR/branch.rs:+3:9: +3:10 +- StorageLive(_2); // scope 1 at $DIR/branch.rs:+3:9: +3:10 ++ nop; // scope 1 at $DIR/branch.rs:+3:9: +3:10 StorageLive(_3); // scope 1 at $DIR/branch.rs:+3:16: +3:22 _3 = cond() -> bb2; // scope 1 at $DIR/branch.rs:+3:16: +3:22 // mir::Constant @@ -36,7 +41,8 @@ } bb3: { - nop; // scope 1 at $DIR/branch.rs:+4:9: +4:10 +- _2 = _1; // scope 1 at $DIR/branch.rs:+4:9: +4:10 ++ nop; // scope 1 at $DIR/branch.rs:+4:9: +4:10 goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6 } @@ -50,16 +56,20 @@ bb5: { StorageDead(_4); // scope 1 at $DIR/branch.rs:+6:14: +6:15 - nop; // scope 1 at $DIR/branch.rs:+7:9: +7:10 +- _2 = _1; // scope 1 at $DIR/branch.rs:+7:9: +7:10 ++ nop; // scope 1 at $DIR/branch.rs:+7:9: +7:10 goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6 } bb6: { StorageDead(_3); // scope 1 at $DIR/branch.rs:+8:5: +8:6 - nop; // scope 0 at $DIR/branch.rs:+0:11: +9:2 - StorageDead(_2); // scope 1 at $DIR/branch.rs:+9:1: +9:2 - StorageDead(_1); // scope 0 at $DIR/branch.rs:+9:1: +9:2 - return; // scope 0 at $DIR/branch.rs:+9:2: +9:2 +- _0 = _2; // scope 2 at $DIR/branch.rs:+10:5: +10:6 +- StorageDead(_2); // scope 1 at $DIR/branch.rs:+11:1: +11:2 +- StorageDead(_1); // scope 0 at $DIR/branch.rs:+11:1: +11:2 ++ nop; // scope 2 at $DIR/branch.rs:+10:5: +10:6 ++ nop; // scope 1 at $DIR/branch.rs:+11:1: +11:2 ++ nop; // scope 0 at $DIR/branch.rs:+11:1: +11:2 + return; // scope 0 at $DIR/branch.rs:+11:2: +11:2 } } diff --git a/src/test/mir-opt/dest-prop/branch.rs b/src/test/mir-opt/dest-prop/branch.rs index fffcf82b3f1d8..898c908b18c70 100644 --- a/src/test/mir-opt/dest-prop/branch.rs +++ b/src/test/mir-opt/dest-prop/branch.rs @@ -1,5 +1,5 @@ //! Tests that assignment in both branches of an `if` are eliminated. -// compile-flags: -Zunsound-mir-opts +// unit-test: DestinationPropagation fn val() -> i32 { 1 } @@ -8,8 +8,8 @@ fn cond() -> bool { true } -// EMIT_MIR branch.main.DestinationPropagation.diff -fn main() { +// EMIT_MIR branch.foo.DestinationPropagation.diff +fn foo() -> i32 { let x = val(); let y = if cond() { @@ -18,4 +18,10 @@ fn main() { val(); x }; + + y +} + +fn main() { + foo(); } diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.arg_src.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/copy_propagation_arg.arg_src.DestinationPropagation.diff index f28bc72df58cb..4343a593542ba 100644 --- a/src/test/mir-opt/dest-prop/copy_propagation_arg.arg_src.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.arg_src.DestinationPropagation.diff @@ -2,7 +2,7 @@ + // MIR for `arg_src` after DestinationPropagation fn arg_src(_1: i32) -> i32 { - debug x => const 123_i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:12: +0:17 + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:12: +0:17 let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:27: +0:30 let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10 scope 1 { @@ -15,7 +15,7 @@ - _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14 + nop; // scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10 + _0 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14 - nop; // scope 1 at $DIR/copy_propagation_arg.rs:+2:5: +2:12 + _1 = const 123_i32; // scope 1 at $DIR/copy_propagation_arg.rs:+2:5: +2:12 - _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:+3:5: +3:6 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+4:1: +4:2 + nop; // scope 1 at $DIR/copy_propagation_arg.rs:+3:5: +3:6 diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.bar.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/copy_propagation_arg.bar.DestinationPropagation.diff index a8a7e9ab6d44d..298991b5ad1cb 100644 --- a/src/test/mir-opt/dest-prop/copy_propagation_arg.bar.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.bar.DestinationPropagation.diff @@ -2,26 +2,30 @@ + // MIR for `bar` after DestinationPropagation fn bar(_1: u8) -> () { - debug x => const 5_u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13 + debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13 let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +0:19 let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13 let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 bb0: { StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13 - StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 - _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 - _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13 +- StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 +- _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 +- _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12 ++ _2 = dummy(move _1) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13 // mir::Constant // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10 // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value() } } bb1: { - StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:12: +1:13 +- StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:12: +1:13 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+1:12: +1:13 StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2 + _1 = const 5_u8; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2 return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2 } } diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.baz.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/copy_propagation_arg.baz.DestinationPropagation.diff index ce9be4c27183d..bc88787e64b2d 100644 --- a/src/test/mir-opt/dest-prop/copy_propagation_arg.baz.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.baz.DestinationPropagation.diff @@ -1,18 +1,22 @@ - // MIR for `baz` before DestinationPropagation + // MIR for `baz` after DestinationPropagation - fn baz(_1: i32) -> () { + fn baz(_1: i32) -> i32 { debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13 - let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:20: +0:20 + let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:23: +0:26 let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+0:20: +3:2 - return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2 +- StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 +- _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 +- _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10 +- StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10 + _0 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+3:5: +3:6 + return; // scope 0 at $DIR/copy_propagation_arg.rs:+4:2: +4:2 } } diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.diff index d7a0b950fc227..d37a9f71d3ebd 100644 --- a/src/test/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.foo.DestinationPropagation.diff @@ -8,10 +8,12 @@ let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16 bb0: { - StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 +- StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16 _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16 - _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 +- _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 ++ _1 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17 // mir::Constant // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14 // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value() } @@ -19,9 +21,11 @@ bb1: { StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:17 - StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17 - nop; // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2 +- _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:17 +- StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:17 ++ nop; // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17 + _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2 return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2 } } diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.rs b/src/test/mir-opt/dest-prop/copy_propagation_arg.rs index a5fb0f640b24a..31be6c931393d 100644 --- a/src/test/mir-opt/dest-prop/copy_propagation_arg.rs +++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.rs @@ -1,6 +1,6 @@ // Check that DestinationPropagation does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) -// compile-flags: -Zunsound-mir-opts +// unit-test: DestinationPropagation fn dummy(x: u8) -> u8 { x } @@ -18,9 +18,10 @@ fn bar(mut x: u8) { } // EMIT_MIR copy_propagation_arg.baz.DestinationPropagation.diff -fn baz(mut x: i32) { +fn baz(mut x: i32) -> i32 { // self-assignment to a function argument should be eliminated x = x; + x } // EMIT_MIR copy_propagation_arg.arg_src.DestinationPropagation.diff diff --git a/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff index 8eeb0d354c698..cfc203c5f89a1 100644 --- a/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff @@ -8,45 +8,69 @@ let _5: (); // in scope 0 at $DIR/cycle.rs:+6:5: +6:12 let mut _6: i32; // in scope 0 at $DIR/cycle.rs:+6:10: +6:11 scope 1 { - debug x => _1; // in scope 1 at $DIR/cycle.rs:+1:9: +1:14 +- debug x => _1; // in scope 1 at $DIR/cycle.rs:+1:9: +1:14 ++ debug x => _6; // in scope 1 at $DIR/cycle.rs:+1:9: +1:14 let _2: i32; // in scope 1 at $DIR/cycle.rs:+2:9: +2:10 scope 2 { - debug y => _2; // in scope 2 at $DIR/cycle.rs:+2:9: +2:10 +- debug y => _2; // in scope 2 at $DIR/cycle.rs:+2:9: +2:10 ++ debug y => _6; // in scope 2 at $DIR/cycle.rs:+2:9: +2:10 let _3: i32; // in scope 2 at $DIR/cycle.rs:+3:9: +3:10 scope 3 { - debug z => _3; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10 - scope 4 (inlined std::mem::drop::) { // at $DIR/cycle.rs:14:5: 14:12 - debug _x => _6; // in scope 4 at $SRC_DIR/core/src/mem/mod.rs:LL:COL - } +- debug z => _3; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10 ++ debug z => _6; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10 } } } bb0: { - StorageLive(_1); // scope 0 at $DIR/cycle.rs:+1:9: +1:14 - _1 = val() -> bb1; // scope 0 at $DIR/cycle.rs:+1:17: +1:22 +- StorageLive(_1); // scope 0 at $DIR/cycle.rs:+1:9: +1:14 +- _1 = val() -> bb1; // scope 0 at $DIR/cycle.rs:+1:17: +1:22 ++ nop; // scope 0 at $DIR/cycle.rs:+1:9: +1:14 ++ _6 = val() -> bb1; // scope 0 at $DIR/cycle.rs:+1:17: +1:22 // mir::Constant // + span: $DIR/cycle.rs:9:17: 9:20 // + literal: Const { ty: fn() -> i32 {val}, val: Value() } } bb1: { - StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10 - nop; // scope 1 at $DIR/cycle.rs:+2:13: +2:14 - StorageLive(_3); // scope 2 at $DIR/cycle.rs:+3:9: +3:10 - nop; // scope 2 at $DIR/cycle.rs:+3:13: +3:14 - StorageLive(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10 - nop; // scope 3 at $DIR/cycle.rs:+4:9: +4:10 - nop; // scope 3 at $DIR/cycle.rs:+4:5: +4:10 - StorageDead(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10 +- StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10 +- _2 = _1; // scope 1 at $DIR/cycle.rs:+2:13: +2:14 +- StorageLive(_3); // scope 2 at $DIR/cycle.rs:+3:9: +3:10 +- _3 = _2; // scope 2 at $DIR/cycle.rs:+3:13: +3:14 +- StorageLive(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10 +- _4 = _3; // scope 3 at $DIR/cycle.rs:+4:9: +4:10 +- _1 = move _4; // scope 3 at $DIR/cycle.rs:+4:5: +4:10 +- StorageDead(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10 ++ nop; // scope 1 at $DIR/cycle.rs:+2:9: +2:10 ++ nop; // scope 1 at $DIR/cycle.rs:+2:13: +2:14 ++ nop; // scope 2 at $DIR/cycle.rs:+3:9: +3:10 ++ nop; // scope 2 at $DIR/cycle.rs:+3:13: +3:14 ++ nop; // scope 3 at $DIR/cycle.rs:+4:9: +4:10 ++ nop; // scope 3 at $DIR/cycle.rs:+4:9: +4:10 ++ nop; // scope 3 at $DIR/cycle.rs:+4:5: +4:10 ++ nop; // scope 3 at $DIR/cycle.rs:+4:9: +4:10 StorageLive(_5); // scope 3 at $DIR/cycle.rs:+6:5: +6:12 - StorageLive(_6); // scope 3 at $DIR/cycle.rs:+6:10: +6:11 - nop; // scope 3 at $DIR/cycle.rs:+6:10: +6:11 - StorageDead(_6); // scope 3 at $DIR/cycle.rs:+6:11: +6:12 +- StorageLive(_6); // scope 3 at $DIR/cycle.rs:+6:10: +6:11 +- _6 = _1; // scope 3 at $DIR/cycle.rs:+6:10: +6:11 ++ nop; // scope 3 at $DIR/cycle.rs:+6:10: +6:11 ++ nop; // scope 3 at $DIR/cycle.rs:+6:10: +6:11 + _5 = std::mem::drop::(move _6) -> bb2; // scope 3 at $DIR/cycle.rs:+6:5: +6:12 + // mir::Constant + // + span: $DIR/cycle.rs:14:5: 14:9 + // + literal: Const { ty: fn(i32) {std::mem::drop::}, val: Value() } + } + + bb2: { +- StorageDead(_6); // scope 3 at $DIR/cycle.rs:+6:11: +6:12 ++ nop; // scope 3 at $DIR/cycle.rs:+6:11: +6:12 StorageDead(_5); // scope 3 at $DIR/cycle.rs:+6:12: +6:13 - StorageDead(_3); // scope 2 at $DIR/cycle.rs:+7:1: +7:2 - StorageDead(_2); // scope 1 at $DIR/cycle.rs:+7:1: +7:2 - StorageDead(_1); // scope 0 at $DIR/cycle.rs:+7:1: +7:2 + _0 = const (); // scope 0 at $DIR/cycle.rs:+0:11: +7:2 +- StorageDead(_3); // scope 2 at $DIR/cycle.rs:+7:1: +7:2 +- StorageDead(_2); // scope 1 at $DIR/cycle.rs:+7:1: +7:2 +- StorageDead(_1); // scope 0 at $DIR/cycle.rs:+7:1: +7:2 ++ nop; // scope 2 at $DIR/cycle.rs:+7:1: +7:2 ++ nop; // scope 1 at $DIR/cycle.rs:+7:1: +7:2 ++ nop; // scope 0 at $DIR/cycle.rs:+7:1: +7:2 return; // scope 0 at $DIR/cycle.rs:+7:2: +7:2 } } diff --git a/src/test/mir-opt/dest-prop/cycle.rs b/src/test/mir-opt/dest-prop/cycle.rs index c9187d408675c..6182878f3413d 100644 --- a/src/test/mir-opt/dest-prop/cycle.rs +++ b/src/test/mir-opt/dest-prop/cycle.rs @@ -1,5 +1,5 @@ //! Tests that cyclic assignments don't hang DestinationPropagation, and result in reasonable code. -// compile-flags: -Zunsound-mir-opts +// unit-test: DestinationPropagation fn val() -> i32 { 1 } diff --git a/src/test/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.mir b/src/test/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.mir new file mode 100644 index 0000000000000..63cac133b73bf --- /dev/null +++ b/src/test/mir-opt/dest-prop/dead_stores_79191.f.DestinationPropagation.after.mir @@ -0,0 +1,34 @@ +// MIR for `f` after DestinationPropagation + +fn f(_1: usize) -> usize { + debug a => _1; // in scope 0 at $DIR/dead_stores_79191.rs:+0:6: +0:11 + let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_79191.rs:+0:23: +0:28 + let _2: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10 + let mut _3: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+3:9: +3:10 + let mut _4: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+4:8: +4:9 + scope 1 { + debug b => _3; // in scope 1 at $DIR/dead_stores_79191.rs:+1:9: +1:10 + } + + bb0: { + nop; // scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10 + _3 = _1; // scope 0 at $DIR/dead_stores_79191.rs:+1:13: +1:14 + _1 = const 5_usize; // scope 1 at $DIR/dead_stores_79191.rs:+2:5: +2:10 + nop; // scope 1 at $DIR/dead_stores_79191.rs:+3:9: +3:10 + nop; // scope 1 at $DIR/dead_stores_79191.rs:+3:9: +3:10 + _1 = move _3; // scope 1 at $DIR/dead_stores_79191.rs:+3:5: +3:10 + nop; // scope 1 at $DIR/dead_stores_79191.rs:+3:9: +3:10 + nop; // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9 + nop; // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9 + _0 = id::(move _1) -> bb1; // scope 1 at $DIR/dead_stores_79191.rs:+4:5: +4:10 + // mir::Constant + // + span: $DIR/dead_stores_79191.rs:12:5: 12:7 + // + literal: Const { ty: fn(usize) -> usize {id::}, val: Value() } + } + + bb1: { + nop; // scope 1 at $DIR/dead_stores_79191.rs:+4:9: +4:10 + nop; // scope 0 at $DIR/dead_stores_79191.rs:+5:1: +5:2 + return; // scope 0 at $DIR/dead_stores_79191.rs:+5:2: +5:2 + } +} diff --git a/src/test/mir-opt/dest-prop/dead_stores_79191.rs b/src/test/mir-opt/dest-prop/dead_stores_79191.rs new file mode 100644 index 0000000000000..43e0bf66418aa --- /dev/null +++ b/src/test/mir-opt/dest-prop/dead_stores_79191.rs @@ -0,0 +1,17 @@ +// unit-test: DestinationPropagation + +fn id(x: T) -> T { + x +} + +// EMIT_MIR dead_stores_79191.f.DestinationPropagation.after.mir +fn f(mut a: usize) -> usize { + let b = a; + a = 5; + a = b; + id(a) +} + +fn main() { + f(0); +} diff --git a/src/test/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.mir b/src/test/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.mir new file mode 100644 index 0000000000000..ba7f76d2841f3 --- /dev/null +++ b/src/test/mir-opt/dest-prop/dead_stores_better.f.DestinationPropagation.after.mir @@ -0,0 +1,34 @@ +// MIR for `f` after DestinationPropagation + +fn f(_1: usize) -> usize { + debug a => _1; // in scope 0 at $DIR/dead_stores_better.rs:+0:10: +0:15 + let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_better.rs:+0:27: +0:32 + let _2: usize; // in scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10 + let mut _3: usize; // in scope 0 at $DIR/dead_stores_better.rs:+3:9: +3:10 + let mut _4: usize; // in scope 0 at $DIR/dead_stores_better.rs:+4:8: +4:9 + scope 1 { + debug b => _1; // in scope 1 at $DIR/dead_stores_better.rs:+1:9: +1:10 + } + + bb0: { + nop; // scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10 + nop; // scope 0 at $DIR/dead_stores_better.rs:+1:13: +1:14 + nop; // scope 1 at $DIR/dead_stores_better.rs:+2:5: +2:10 + nop; // scope 1 at $DIR/dead_stores_better.rs:+3:9: +3:10 + nop; // scope 1 at $DIR/dead_stores_better.rs:+3:9: +3:10 + nop; // scope 1 at $DIR/dead_stores_better.rs:+3:5: +3:10 + nop; // scope 1 at $DIR/dead_stores_better.rs:+3:9: +3:10 + nop; // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9 + nop; // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9 + _0 = id::(move _1) -> bb1; // scope 1 at $DIR/dead_stores_better.rs:+4:5: +4:10 + // mir::Constant + // + span: $DIR/dead_stores_better.rs:16:5: 16:7 + // + literal: Const { ty: fn(usize) -> usize {id::}, val: Value() } + } + + bb1: { + nop; // scope 1 at $DIR/dead_stores_better.rs:+4:9: +4:10 + nop; // scope 0 at $DIR/dead_stores_better.rs:+5:1: +5:2 + return; // scope 0 at $DIR/dead_stores_better.rs:+5:2: +5:2 + } +} diff --git a/src/test/mir-opt/dest-prop/dead_stores_better.rs b/src/test/mir-opt/dest-prop/dead_stores_better.rs new file mode 100644 index 0000000000000..003ad57d83ea7 --- /dev/null +++ b/src/test/mir-opt/dest-prop/dead_stores_better.rs @@ -0,0 +1,21 @@ +// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates +// that that pass enables this one to do more optimizations. + +// unit-test: DestinationPropagation +// compile-flags: -Zmir-enable-passes=+DeadStoreElimination + +fn id(x: T) -> T { + x +} + +// EMIT_MIR dead_stores_better.f.DestinationPropagation.after.mir +pub fn f(mut a: usize) -> usize { + let b = a; + a = 5; + a = b; + id(a) +} + +fn main() { + f(0); +} diff --git a/src/test/mir-opt/dest-prop/simple.nrvo.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/simple.nrvo.DestinationPropagation.diff index 80b09ed5f8d35..c2a3a0025840a 100644 --- a/src/test/mir-opt/dest-prop/simple.nrvo.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/simple.nrvo.DestinationPropagation.diff @@ -17,18 +17,22 @@ StorageLive(_2); // scope 0 at $DIR/simple.rs:+1:9: +1:16 _2 = [const 0_u8; 1024]; // scope 0 at $DIR/simple.rs:+1:19: +1:28 StorageLive(_3); // scope 1 at $DIR/simple.rs:+2:5: +2:19 - StorageLive(_4); // scope 1 at $DIR/simple.rs:+2:5: +2:9 - _4 = _1; // scope 1 at $DIR/simple.rs:+2:5: +2:9 +- StorageLive(_4); // scope 1 at $DIR/simple.rs:+2:5: +2:9 +- _4 = _1; // scope 1 at $DIR/simple.rs:+2:5: +2:9 ++ nop; // scope 1 at $DIR/simple.rs:+2:5: +2:9 ++ nop; // scope 1 at $DIR/simple.rs:+2:5: +2:9 StorageLive(_5); // scope 1 at $DIR/simple.rs:+2:10: +2:18 StorageLive(_6); // scope 1 at $DIR/simple.rs:+2:10: +2:18 _6 = &mut _2; // scope 1 at $DIR/simple.rs:+2:10: +2:18 _5 = &mut (*_6); // scope 1 at $DIR/simple.rs:+2:10: +2:18 - _3 = move _4(move _5) -> bb1; // scope 1 at $DIR/simple.rs:+2:5: +2:19 +- _3 = move _4(move _5) -> bb1; // scope 1 at $DIR/simple.rs:+2:5: +2:19 ++ _3 = move _1(move _5) -> bb1; // scope 1 at $DIR/simple.rs:+2:5: +2:19 } bb1: { StorageDead(_5); // scope 1 at $DIR/simple.rs:+2:18: +2:19 - StorageDead(_4); // scope 1 at $DIR/simple.rs:+2:18: +2:19 +- StorageDead(_4); // scope 1 at $DIR/simple.rs:+2:18: +2:19 ++ nop; // scope 1 at $DIR/simple.rs:+2:18: +2:19 StorageDead(_6); // scope 1 at $DIR/simple.rs:+2:19: +2:20 StorageDead(_3); // scope 1 at $DIR/simple.rs:+2:19: +2:20 _0 = _2; // scope 1 at $DIR/simple.rs:+3:5: +3:8 diff --git a/src/test/mir-opt/dest-prop/simple.rs b/src/test/mir-opt/dest-prop/simple.rs index 3627d479a9aa3..d4c27228fe425 100644 --- a/src/test/mir-opt/dest-prop/simple.rs +++ b/src/test/mir-opt/dest-prop/simple.rs @@ -1,5 +1,5 @@ //! Copy of `nrvo-simple.rs`, to ensure that full dest-prop handles it too. -// compile-flags: -Zunsound-mir-opts +// unit-test: DestinationPropagation // EMIT_MIR simple.nrvo.DestinationPropagation.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; diff --git a/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff index accdb00852ede..85d994bc8b973 100644 --- a/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff @@ -5,14 +5,13 @@ let mut _0: (); // return place in scope 0 at $DIR/union.rs:+0:11: +0:11 let _1: main::Un; // in scope 0 at $DIR/union.rs:+5:9: +5:11 let mut _2: u32; // in scope 0 at $DIR/union.rs:+5:23: +5:28 - let _3: (); // in scope 0 at $DIR/union.rs:+7:5: +7:27 - let mut _4: u32; // in scope 0 at $DIR/union.rs:+7:10: +7:26 + let mut _3: u32; // in scope 0 at $DIR/union.rs:+7:10: +7:26 scope 1 { debug un => _1; // in scope 1 at $DIR/union.rs:+5:9: +5:11 scope 2 { } scope 3 (inlined std::mem::drop::) { // at $DIR/union.rs:15:5: 15:27 - debug _x => _4; // in scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL + debug _x => _3; // in scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL } } @@ -29,11 +28,9 @@ nop; // scope 0 at $DIR/union.rs:+5:14: +5:30 nop; // scope 0 at $DIR/union.rs:+5:14: +5:30 StorageDead(_2); // scope 0 at $DIR/union.rs:+5:29: +5:30 - StorageLive(_3); // scope 1 at $DIR/union.rs:+7:5: +7:27 - StorageLive(_4); // scope 1 at $DIR/union.rs:+7:10: +7:26 + StorageLive(_3); // scope 1 at $DIR/union.rs:+7:10: +7:26 nop; // scope 2 at $DIR/union.rs:+7:19: +7:24 - StorageDead(_4); // scope 1 at $DIR/union.rs:+7:26: +7:27 - StorageDead(_3); // scope 1 at $DIR/union.rs:+7:27: +7:28 + StorageDead(_3); // scope 1 at $DIR/union.rs:+7:26: +7:27 StorageDead(_1); // scope 0 at $DIR/union.rs:+8:1: +8:2 return; // scope 0 at $DIR/union.rs:+8:2: +8:2 } diff --git a/src/test/mir-opt/inline/dyn_trait.get_query.Inline.diff b/src/test/mir-opt/inline/dyn_trait.get_query.Inline.diff index 284306a352d15..8ea1a0757f2f0 100644 --- a/src/test/mir-opt/inline/dyn_trait.get_query.Inline.diff +++ b/src/test/mir-opt/inline/dyn_trait.get_query.Inline.diff @@ -12,10 +12,8 @@ + scope 2 (inlined try_execute_query::<::C>) { // at $DIR/dyn_trait.rs:34:5: 34:25 + debug c => _4; // in scope 2 at $DIR/dyn_trait.rs:26:36: 26:37 + let mut _5: &dyn Cache::V>; // in scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ let mut _6: &::C; // in scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 + scope 3 (inlined mk_cycle::<::V>) { // at $DIR/dyn_trait.rs:27:5: 27:16 + debug c => _5; // in scope 3 at $DIR/dyn_trait.rs:20:27: 20:28 -+ let mut _7: &dyn Cache::V>; // in scope 3 at $DIR/dyn_trait.rs:21:5: 21:22 + } + } } @@ -37,13 +35,8 @@ _4 = &(*_2); // scope 1 at $DIR/dyn_trait.rs:+2:23: +2:24 - _0 = try_execute_query::<::C>(move _4) -> bb2; // scope 1 at $DIR/dyn_trait.rs:+2:5: +2:25 + StorageLive(_5); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ StorageLive(_6); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ _6 = _4; // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ _5 = move _6 as &dyn Cache::V> (Pointer(Unsize)); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ StorageDead(_6); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 -+ StorageLive(_7); // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22 -+ _7 = _5; // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22 -+ _0 = ::V> as Cache>::store_nocache(move _7) -> bb2; // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22 ++ _5 = move _4 as &dyn Cache::V> (Pointer(Unsize)); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15 ++ _0 = ::V> as Cache>::store_nocache(move _5) -> bb2; // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22 // mir::Constant - // + span: $DIR/dyn_trait.rs:34:5: 34:22 - // + literal: Const { ty: for<'a> fn(&'a ::C) {try_execute_query::<::C>}, val: Value() } @@ -52,7 +45,6 @@ } bb2: { -+ StorageDead(_7); // scope 3 at $DIR/dyn_trait.rs:21:21: 21:22 + StorageDead(_5); // scope 2 at $DIR/dyn_trait.rs:27:15: 27:16 StorageDead(_4); // scope 1 at $DIR/dyn_trait.rs:+2:24: +2:25 StorageDead(_2); // scope 0 at $DIR/dyn_trait.rs:+3:1: +3:2 diff --git a/src/test/mir-opt/inline/dyn_trait.try_execute_query.Inline.diff b/src/test/mir-opt/inline/dyn_trait.try_execute_query.Inline.diff index 0191045f3d1a5..a71d73b745354 100644 --- a/src/test/mir-opt/inline/dyn_trait.try_execute_query.Inline.diff +++ b/src/test/mir-opt/inline/dyn_trait.try_execute_query.Inline.diff @@ -8,7 +8,6 @@ let mut _3: &C; // in scope 0 at $DIR/dyn_trait.rs:+1:14: +1:15 + scope 1 (inlined mk_cycle::<::V>) { // at $DIR/dyn_trait.rs:27:5: 27:16 + debug c => _2; // in scope 1 at $DIR/dyn_trait.rs:20:27: 20:28 -+ let mut _4: &dyn Cache::V>; // in scope 1 at $DIR/dyn_trait.rs:21:5: 21:22 + } bb0: { @@ -18,9 +17,7 @@ _2 = move _3 as &dyn Cache::V> (Pointer(Unsize)); // scope 0 at $DIR/dyn_trait.rs:+1:14: +1:15 StorageDead(_3); // scope 0 at $DIR/dyn_trait.rs:+1:14: +1:15 - _0 = mk_cycle::<::V>(move _2) -> bb1; // scope 0 at $DIR/dyn_trait.rs:+1:5: +1:16 -+ StorageLive(_4); // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22 -+ _4 = _2; // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22 -+ _0 = ::V> as Cache>::store_nocache(move _4) -> bb1; // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22 ++ _0 = ::V> as Cache>::store_nocache(move _2) -> bb1; // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22 // mir::Constant - // + span: $DIR/dyn_trait.rs:27:5: 27:13 - // + literal: Const { ty: for<'a> fn(&'a (dyn Cache::V> + 'a)) {mk_cycle::<::V>}, val: Value() } @@ -29,7 +26,6 @@ } bb1: { -+ StorageDead(_4); // scope 1 at $DIR/dyn_trait.rs:21:21: 21:22 StorageDead(_2); // scope 0 at $DIR/dyn_trait.rs:+1:15: +1:16 return; // scope 0 at $DIR/dyn_trait.rs:+2:2: +2:2 } diff --git a/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir b/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir index 8956c80dcd2a9..3502c25864bf9 100644 --- a/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir +++ b/src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir @@ -11,8 +11,6 @@ fn bar() -> bool { scope 2 (inlined foo) { // at $DIR/inline_any_operand.rs:12:5: 12:13 debug x => _3; // in scope 2 at $DIR/inline_any_operand.rs:16:8: 16:9 debug y => _4; // in scope 2 at $DIR/inline_any_operand.rs:16:16: 16:17 - let mut _5: i32; // in scope 2 at $DIR/inline_any_operand.rs:17:5: 17:6 - let mut _6: i32; // in scope 2 at $DIR/inline_any_operand.rs:17:10: 17:11 } } @@ -28,13 +26,7 @@ fn bar() -> bool { _3 = const 1_i32; // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13 StorageLive(_4); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13 _4 = const -1_i32; // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13 - StorageLive(_5); // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:6 - _5 = _3; // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:6 - StorageLive(_6); // scope 2 at $DIR/inline_any_operand.rs:17:10: 17:11 - _6 = _4; // scope 2 at $DIR/inline_any_operand.rs:17:10: 17:11 - _0 = Eq(move _5, move _6); // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:11 - StorageDead(_6); // scope 2 at $DIR/inline_any_operand.rs:17:10: 17:11 - StorageDead(_5); // scope 2 at $DIR/inline_any_operand.rs:17:10: 17:11 + _0 = Eq(move _3, move _4); // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:11 StorageDead(_4); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13 StorageDead(_3); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13 StorageDead(_2); // scope 1 at $DIR/inline_any_operand.rs:+2:12: +2:13 diff --git a/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir index e6275ac7f5dd9..dd32eb2d8d1f7 100644 --- a/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir +++ b/src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir @@ -16,9 +16,8 @@ fn foo(_1: T, _2: &i32) -> i32 { scope 2 (inlined foo::::{closure#0}) { // at $DIR/inline_closure_borrows_arg.rs:16:5: 16:12 debug r => _8; // in scope 2 at $DIR/inline_closure_borrows_arg.rs:+1:14: +1:15 debug _s => _9; // in scope 2 at $DIR/inline_closure_borrows_arg.rs:+1:23: +1:25 - let _10: &i32; // in scope 2 at $DIR/inline_closure_borrows_arg.rs:+2:13: +2:21 scope 3 { - debug variable => _10; // in scope 3 at $DIR/inline_closure_borrows_arg.rs:+2:13: +2:21 + debug variable => _8; // in scope 3 at $DIR/inline_closure_borrows_arg.rs:+2:13: +2:21 } } } @@ -40,10 +39,7 @@ fn foo(_1: T, _2: &i32) -> i32 { _8 = move (_5.0: &i32); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12 StorageLive(_9); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12 _9 = move (_5.1: &i32); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12 - StorageLive(_10); // scope 2 at $DIR/inline_closure_borrows_arg.rs:+2:13: +2:21 - _10 = _8; // scope 2 at $DIR/inline_closure_borrows_arg.rs:+2:24: +2:27 - _0 = (*_10); // scope 3 at $DIR/inline_closure_borrows_arg.rs:+3:9: +3:18 - StorageDead(_10); // scope 2 at $DIR/inline_closure_borrows_arg.rs:+4:5: +4:6 + _0 = (*_8); // scope 3 at $DIR/inline_closure_borrows_arg.rs:+3:9: +3:18 StorageDead(_9); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12 StorageDead(_8); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12 StorageDead(_7); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:11: +5:12 diff --git a/src/test/mir-opt/inline/inline_cycle.two.Inline.diff b/src/test/mir-opt/inline/inline_cycle.two.Inline.diff index eceeb96f79f08..ab1ea0e3b2c24 100644 --- a/src/test/mir-opt/inline/inline_cycle.two.Inline.diff +++ b/src/test/mir-opt/inline/inline_cycle.two.Inline.diff @@ -8,11 +8,10 @@ + scope 1 (inlined call::) { // at $DIR/inline_cycle.rs:49:5: 49:12 + debug f => _2; // in scope 1 at $DIR/inline_cycle.rs:53:22: 53:23 + let _3: (); // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 -+ let mut _4: fn() {f}; // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:6 -+ let mut _5: (); // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 ++ let mut _4: (); // in scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 + scope 2 (inlined >::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:54:5: 54:8 + scope 3 (inlined f) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL -+ let _6: (); // in scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 ++ let _5: (); // in scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 + } + } + } @@ -27,11 +26,9 @@ + // + span: $DIR/inline_cycle.rs:49:10: 49:11 + // + literal: Const { ty: fn() {f}, val: Value() } + StorageLive(_3); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 -+ StorageLive(_4); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:6 -+ _4 = move _2; // scope 1 at $DIR/inline_cycle.rs:54:5: 54:6 -+ StorageLive(_5); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 -+ StorageLive(_6); // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 -+ _6 = call::(f) -> bb1; // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 ++ StorageLive(_4); // scope 1 at $DIR/inline_cycle.rs:54:5: 54:8 ++ StorageLive(_5); // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 ++ _5 = call::(f) -> bb1; // scope 3 at $DIR/inline_cycle.rs:59:5: 59:12 + // mir::Constant + // + span: $DIR/inline_cycle.rs:59:5: 59:9 // + literal: Const { ty: fn(fn() {f}) {call::}, val: Value() } @@ -42,8 +39,7 @@ } bb1: { -+ StorageDead(_6); // scope 3 at $DIR/inline_cycle.rs:59:12: 59:13 -+ StorageDead(_5); // scope 1 at $DIR/inline_cycle.rs:54:7: 54:8 ++ StorageDead(_5); // scope 3 at $DIR/inline_cycle.rs:59:12: 59:13 + StorageDead(_4); // scope 1 at $DIR/inline_cycle.rs:54:7: 54:8 + StorageDead(_3); // scope 1 at $DIR/inline_cycle.rs:54:8: 54:9 + StorageDead(_2); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 diff --git a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff index 152153a813ce2..75a6ab3700898 100644 --- a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff +++ b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff @@ -10,15 +10,14 @@ + let _3: !; // in scope 1 at $DIR/inline_diverging.rs:27:9: 27:10 + let mut _4: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline_diverging.rs:27:13: 27:14 + let mut _5: (); // in scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 -+ let mut _7: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline_diverging.rs:28:13: 28:14 -+ let mut _8: (); // in scope 1 at $DIR/inline_diverging.rs:28:13: 28:16 -+ let mut _9: !; // in scope 1 at $DIR/inline_diverging.rs:29:6: 29:7 -+ let mut _10: !; // in scope 1 at $DIR/inline_diverging.rs:29:9: 29:10 ++ let mut _6: &fn() -> ! {sleep}; // in scope 1 at $DIR/inline_diverging.rs:28:13: 28:14 ++ let mut _7: (); // in scope 1 at $DIR/inline_diverging.rs:28:13: 28:16 ++ let mut _8: !; // in scope 1 at $DIR/inline_diverging.rs:29:6: 29:7 ++ let mut _9: !; // in scope 1 at $DIR/inline_diverging.rs:29:9: 29:10 + scope 2 { + debug a => _3; // in scope 2 at $DIR/inline_diverging.rs:27:9: 27:10 -+ let _6: !; // in scope 2 at $DIR/inline_diverging.rs:28:9: 28:10 + scope 3 { -+ debug b => _6; // in scope 3 at $DIR/inline_diverging.rs:28:9: 28:10 ++ debug b => _9; // in scope 3 at $DIR/inline_diverging.rs:28:9: 28:10 + } + scope 6 (inlined ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline_diverging.rs:28:13: 28:16 + scope 7 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -42,6 +41,7 @@ - // mir::Constant // + span: $DIR/inline_diverging.rs:22:16: 22:21 // + literal: Const { ty: fn() -> ! {sleep}, val: Value() } ++ StorageLive(_9); // scope 0 at $DIR/inline_diverging.rs:+1:5: +1:22 + StorageLive(_3); // scope 1 at $DIR/inline_diverging.rs:27:9: 27:10 + StorageLive(_4); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14 + _4 = &_2; // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14 diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff index 26202f2f40db5..91bff3d3234db 100644 --- a/src/test/mir-opt/inline/inline_generator.main.Inline.diff +++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff @@ -24,15 +24,12 @@ + } + } + scope 6 (inlined g::{closure#0}) { // at $DIR/inline_generator.rs:9:14: 9:46 -+ debug a => _11; // in scope 6 at $DIR/inline_generator.rs:15:6: 15:7 ++ debug a => _7; // in scope 6 at $DIR/inline_generator.rs:15:6: 15:7 + let mut _8: i32; // in scope 6 at $DIR/inline_generator.rs:15:17: 15:39 -+ let mut _9: bool; // in scope 6 at $DIR/inline_generator.rs:15:20: 15:21 -+ let mut _10: bool; // in scope 6 at $DIR/inline_generator.rs:15:9: 15:9 -+ let _11: bool; // in scope 6 at $DIR/inline_generator.rs:15:6: 15:7 -+ let mut _12: u32; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ let mut _13: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ let mut _14: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ let mut _15: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ let mut _9: u32; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ let mut _10: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ let mut _11: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ let mut _12: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]; // in scope 6 at $DIR/inline_generator.rs:15:5: 15:41 + } bb0: { @@ -73,17 +70,13 @@ - // + literal: Const { ty: for<'a> fn(Pin<&'a mut [generator@$DIR/inline_generator.rs:15:5: 15:8]>, bool) -> GeneratorState<<[generator@$DIR/inline_generator.rs:15:5: 15:8] as Generator>::Yield, <[generator@$DIR/inline_generator.rs:15:5: 15:8] as Generator>::Return> {<[generator@$DIR/inline_generator.rs:15:5: 15:8] as Generator>::resume}, val: Value() } + StorageLive(_7); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 + _7 = const false; // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 -+ StorageLive(_10); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 -+ StorageLive(_11); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 -+ _13 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ _12 = discriminant((*_13)); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ switchInt(move _12) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ _10 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ _9 = discriminant((*_10)); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 ++ switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 } - bb3: { + bb1: { -+ StorageDead(_11); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 -+ StorageDead(_10); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 + StorageDead(_7); // scope 0 at $DIR/inline_generator.rs:+1:14: +1:46 StorageDead(_2); // scope 0 at $DIR/inline_generator.rs:+1:45: +1:46 StorageDead(_4); // scope 0 at $DIR/inline_generator.rs:+1:46: +1:47 @@ -98,11 +91,8 @@ + } + + bb3: { -+ _11 = move _7; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 + StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:17: 15:39 -+ StorageLive(_9); // scope 6 at $DIR/inline_generator.rs:15:20: 15:21 -+ _9 = _11; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21 -+ switchInt(move _9) -> [false: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21 ++ switchInt(move _7) -> [false: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21 + } + + bb4: { @@ -116,24 +106,22 @@ + } + + bb6: { -+ StorageDead(_9); // scope 6 at $DIR/inline_generator.rs:15:38: 15:39 + Deinit(_1); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 + ((_1 as Yielded).0: i32) = move _8; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 + discriminant(_1) = 0; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 -+ _14 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 -+ discriminant((*_14)) = 3; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 ++ _11 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 ++ discriminant((*_11)) = 3; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39 + goto -> bb1; // scope 0 at $DIR/inline_generator.rs:15:11: 15:39 + } + + bb7: { + StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 -+ _10 = move _7; // scope 6 at $DIR/inline_generator.rs:15:5: 15:41 + StorageDead(_8); // scope 6 at $DIR/inline_generator.rs:15:38: 15:39 + Deinit(_1); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 -+ ((_1 as Complete).0: bool) = move _10; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 ++ ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 + discriminant(_1) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 -+ _15 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 -+ discriminant((*_15)) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 ++ _12 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 ++ discriminant((*_12)) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41 + goto -> bb1; // scope 0 at $DIR/inline_generator.rs:15:41: 15:41 + } + diff --git a/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir b/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir index dab8bb9a0c62b..73aea719eed51 100644 --- a/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir +++ b/src/test/mir-opt/inline/inline_trait_method_2.test2.Inline.after.mir @@ -7,7 +7,6 @@ fn test2(_1: &dyn X) -> bool { let mut _3: &dyn X; // in scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11 scope 1 (inlined test) { // at $DIR/inline_trait_method_2.rs:5:5: 5:12 debug x => _2; // in scope 1 at $DIR/inline_trait_method_2.rs:9:9: 9:10 - let mut _4: &dyn X; // in scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10 } bb0: { @@ -16,16 +15,13 @@ fn test2(_1: &dyn X) -> bool { _3 = &(*_1); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11 _2 = move _3 as &dyn X (Pointer(Unsize)); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11 StorageDead(_3); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11 - StorageLive(_4); // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10 - _4 = _2; // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10 - _0 = ::y(move _4) -> bb1; // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10 + _0 = ::y(move _2) -> bb1; // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10 // mir::Constant // + span: $DIR/inline_trait_method_2.rs:10:7: 10:8 // + literal: Const { ty: for<'a> fn(&'a dyn X) -> bool {::y}, val: Value() } } bb1: { - StorageDead(_4); // scope 1 at $DIR/inline_trait_method_2.rs:10:9: 10:10 StorageDead(_2); // scope 0 at $DIR/inline_trait_method_2.rs:+1:11: +1:12 return; // scope 0 at $DIR/inline_trait_method_2.rs:+2:2: +2:2 } diff --git a/src/test/mir-opt/issue_101973.inner.ConstProp.diff b/src/test/mir-opt/issue_101973.inner.ConstProp.diff index bce40f277d23b..8fe60a0245d7d 100644 --- a/src/test/mir-opt/issue_101973.inner.ConstProp.diff +++ b/src/test/mir-opt/issue_101973.inner.ConstProp.diff @@ -18,8 +18,7 @@ debug x => _5; // in scope 1 at $DIR/issue_101973.rs:5:13: 5:14 let mut _12: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:27 let mut _13: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20 - let mut _14: u32; // in scope 1 at $DIR/issue_101973.rs:7:13: 7:14 - let mut _15: (u32, bool); // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20 + let mut _14: (u32, bool); // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20 scope 2 { debug out => _4; // in scope 2 at $DIR/issue_101973.rs:6:9: 6:16 } @@ -27,8 +26,8 @@ scope 3 (inlined core::num::::rotate_right) { // at $DIR/issue_101973.rs:14:5: 14:58 debug self => _4; // in scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL debug n => _6; // in scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + let mut _15: u32; // in scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL let mut _16: u32; // in scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - let mut _17: u32; // in scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL } bb0: { @@ -39,10 +38,8 @@ _5 = _1; // scope 0 at $DIR/issue_101973.rs:+1:10: +1:16 StorageLive(_12); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27 StorageLive(_13); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 - StorageLive(_14); // scope 2 at $DIR/issue_101973.rs:7:13: 7:14 - _14 = _5; // scope 2 at $DIR/issue_101973.rs:7:13: 7:14 - _15 = CheckedShr(_14, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 - assert(!move (_15.1: bool), "attempt to shift right by `{}`, which would overflow", const 0_i32) -> bb3; // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 + _14 = CheckedShr(_5, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 + assert(!move (_14.1: bool), "attempt to shift right by `{}`, which would overflow", const 0_i32) -> bb3; // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 } bb1: { @@ -57,19 +54,18 @@ bb2: { _6 = move (_11.0: u32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57 StorageDead(_7); // scope 0 at $DIR/issue_101973.rs:+1:56: +1:57 + StorageLive(_15); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + _15 = _4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageLive(_16); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - _16 = _4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - StorageLive(_17); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - _17 = _6; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - _3 = rotate_right::(move _16, move _17) -> bb4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + _16 = _6; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + _3 = rotate_right::(move _15, move _16) -> bb4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/num/uint_macros.rs:LL:COL // + literal: Const { ty: extern "rust-intrinsic" fn(u32, u32) -> u32 {rotate_right::}, val: Value() } } bb3: { - _13 = move (_15.0: u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 - StorageDead(_14); // scope 2 at $DIR/issue_101973.rs:7:19: 7:20 + _13 = move (_14.0: u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20 _12 = BitAnd(move _13, const 255_u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27 StorageDead(_13); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27 _4 = BitOr(const 0_u32, move _12); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27 @@ -85,8 +81,8 @@ } bb4: { - StorageDead(_17); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageDead(_16); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + StorageDead(_15); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageDead(_6); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58 StorageDead(_4); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58 _2 = move _3 as i32 (IntToInt); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65 diff --git a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir index 1cfa41b4daa49..5a2f4feff3552 100644 --- a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir +++ b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir @@ -3,64 +3,54 @@ fn num_to_digit(_1: char) -> u32 { debug num => _1; // in scope 0 at $DIR/issue_59352.rs:+0:21: +0:24 let mut _0: u32; // return place in scope 0 at $DIR/issue_59352.rs:+0:35: +0:38 - let mut _2: char; // in scope 0 at $DIR/issue_59352.rs:+2:8: +2:11 - let mut _3: std::option::Option; // in scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 - let mut _4: char; // in scope 0 at $DIR/issue_59352.rs:+2:26: +2:29 - let mut _5: u32; // in scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 - let mut _12: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _2: std::option::Option; // in scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 + let mut _3: u32; // in scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 + let mut _9: isize; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL scope 1 (inlined char::methods::::is_digit) { // at $DIR/issue_59352.rs:14:8: 14:23 - debug self => _2; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - debug radix => _5; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - let mut _6: &std::option::Option; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - let _7: std::option::Option; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - let mut _8: char; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + debug self => _1; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + debug radix => _3; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + let mut _4: &std::option::Option; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + let _5: std::option::Option; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + let mut _6: char; // in scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL scope 2 (inlined Option::::is_some) { // at $SRC_DIR/core/src/char/methods.rs:LL:COL - debug self => _6; // in scope 2 at $SRC_DIR/core/src/option.rs:LL:COL - let mut _9: isize; // in scope 2 at $SRC_DIR/core/src/option.rs:LL:COL + debug self => _4; // in scope 2 at $SRC_DIR/core/src/option.rs:LL:COL } } scope 3 (inlined #[track_caller] Option::::unwrap) { // at $DIR/issue_59352.rs:14:26: 14:50 - debug self => _3; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL - let mut _10: isize; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL - let mut _11: !; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + debug self => _2; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + let mut _7: isize; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + let mut _8: !; // in scope 3 at $SRC_DIR/core/src/option.rs:LL:COL scope 4 { debug val => _0; // in scope 4 at $SRC_DIR/core/src/option.rs:LL:COL } } bb0: { - StorageLive(_2); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:11 - _2 = _1; // scope 0 at $DIR/issue_59352.rs:+2:8: +2:11 - StorageLive(_5); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 + StorageLive(_3); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 + StorageLive(_4); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + StorageLive(_5); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL StorageLive(_6); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - StorageLive(_7); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - StorageLive(_8); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - _8 = _2; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - _7 = char::methods::::to_digit(move _8, const 8_u32) -> bb5; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + _6 = _1; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + _5 = char::methods::::to_digit(move _6, const 8_u32) -> bb5; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/char/methods.rs:LL:COL // + literal: Const { ty: fn(char, u32) -> Option {char::methods::::to_digit}, val: Value() } } bb1: { - StorageDead(_12); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 - StorageLive(_3); // scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 - StorageLive(_4); // scope 0 at $DIR/issue_59352.rs:+2:26: +2:29 - _4 = _1; // scope 0 at $DIR/issue_59352.rs:+2:26: +2:29 - _3 = char::methods::::to_digit(move _4, const 8_u32) -> bb2; // scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 + StorageLive(_2); // scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 + _2 = char::methods::::to_digit(move _1, const 8_u32) -> bb2; // scope 0 at $DIR/issue_59352.rs:+2:26: +2:41 // mir::Constant // + span: $DIR/issue_59352.rs:14:30: 14:38 // + literal: Const { ty: fn(char, u32) -> Option {char::methods::::to_digit}, val: Value() } } bb2: { - StorageDead(_4); // scope 0 at $DIR/issue_59352.rs:+2:40: +2:41 - _10 = discriminant(_3); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL - switchInt(move _10) -> [0_isize: bb6, 1_isize: bb8, otherwise: bb7]; // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + _7 = discriminant(_2); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + switchInt(move _7) -> [0_isize: bb6, 1_isize: bb8, otherwise: bb7]; // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL } bb3: { - StorageDead(_12); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 _0 = const 0_u32; // scope 0 at $DIR/issue_59352.rs:+2:60: +2:61 goto -> bb4; // scope 0 at $DIR/issue_59352.rs:+2:5: +2:63 } @@ -70,21 +60,18 @@ fn num_to_digit(_1: char) -> u32 { } bb5: { - _6 = &_7; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - StorageDead(_8); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - _9 = discriminant((*_6)); // scope 2 at $SRC_DIR/core/src/option.rs:LL:COL - StorageLive(_12); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL - _12 = move _9; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _4 = &_5; // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL StorageDead(_6); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - StorageDead(_7); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL - StorageDead(_5); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 - StorageDead(_2); // scope 0 at $DIR/issue_59352.rs:+2:22: +2:23 - switchInt(move _12) -> [1_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 + _9 = discriminant((*_4)); // scope 2 at $SRC_DIR/core/src/option.rs:LL:COL + StorageDead(_4); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/core/src/char/methods.rs:LL:COL + StorageDead(_3); // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 + switchInt(move _9) -> [1_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/issue_59352.rs:+2:8: +2:23 } bb6: { - StorageLive(_11); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL - _11 = core::panicking::panic(const "called `Option::unwrap()` on a `None` value"); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + StorageLive(_8); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + _8 = core::panicking::panic(const "called `Option::unwrap()` on a `None` value"); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/option.rs:LL:COL // + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value() } @@ -98,8 +85,8 @@ fn num_to_digit(_1: char) -> u32 { } bb8: { - _0 = move ((_3 as Some).0: u32); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL - StorageDead(_3); // scope 0 at $DIR/issue_59352.rs:+2:49: +2:50 + _0 = move ((_2 as Some).0: u32); // scope 3 at $SRC_DIR/core/src/option.rs:LL:COL + StorageDead(_2); // scope 0 at $DIR/issue_59352.rs:+2:49: +2:50 goto -> bb4; // scope 0 at $DIR/issue_59352.rs:+2:5: +2:63 } } diff --git a/src/test/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir b/src/test/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir index 2c6c93cb1d83b..9b1b07f38fcd3 100644 --- a/src/test/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir +++ b/src/test/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir @@ -7,9 +7,8 @@ fn array_bound(_1: usize, _2: &[u8; N]) -> u8 { let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13 let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - let _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - let mut _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let mut _8: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + let mut _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + let mut _7: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 bb0: { StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 @@ -24,16 +23,13 @@ fn array_bound(_1: usize, _2: &[u8; N]) -> u8 { } bb1: { - StorageLive(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - _6 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - _7 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + _6 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + _7 = Lt(_1, _6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 } bb2: { - _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - StorageDead(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+3:5: +3:6 + _0 = (*_2)[_1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 goto -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +5:6 } diff --git a/src/test/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir b/src/test/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir index aee3a8242cdda..29e379777b058 100644 --- a/src/test/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir +++ b/src/test/mir-opt/lower_array_len_e2e.array_bound_mut.PreCodegen.after.mir @@ -7,12 +7,11 @@ fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 { let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13 let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27 - let _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - let mut _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let mut _8: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - let _9: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - let mut _10: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - let mut _11: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 + let mut _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + let mut _7: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + let _8: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 + let mut _9: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 + let mut _10: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 bb0: { StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27 @@ -27,30 +26,27 @@ fn array_bound_mut(_1: usize, _2: &mut [u8; N]) -> u8 { } bb1: { - StorageLive(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - _6 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:15: +2:20 - _7 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - _8 = Lt(_6, _7); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + _6 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + _7 = Lt(_1, _6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 + assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 } bb2: { - _0 = (*_2)[_6]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 - StorageDead(_6); // scope 0 at $DIR/lower_array_len_e2e.rs:+3:5: +3:6 + _0 = (*_2)[_1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21 goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6 } bb3: { - StorageLive(_9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - _9 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 - _10 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - _11 = Lt(const 0_usize, _10); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 - assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 + StorageLive(_8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 + _8 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16 + _9 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 + _10 = Lt(const 0_usize, _9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 + assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17 } bb4: { - (*_2)[_9] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22 - StorageDead(_9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23 + (*_2)[_8] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22 + StorageDead(_8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23 _0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:9: +6:11 goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6 } diff --git a/src/test/mir-opt/lower_intrinsics_e2e.f_unit.PreCodegen.after.mir b/src/test/mir-opt/lower_intrinsics_e2e.f_unit.PreCodegen.after.mir index a5b396ca0bc7f..b672e1a6e6397 100644 --- a/src/test/mir-opt/lower_intrinsics_e2e.f_unit.PreCodegen.after.mir +++ b/src/test/mir-opt/lower_intrinsics_e2e.f_unit.PreCodegen.after.mir @@ -6,7 +6,6 @@ fn f_unit() -> () { scope 1 (inlined f_dispatch::<()>) { // at $DIR/lower_intrinsics_e2e.rs:9:5: 9:19 debug t => _1; // in scope 1 at $DIR/lower_intrinsics_e2e.rs:19:22: 19:23 let _2: (); // in scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17 - let mut _3: (); // in scope 1 at $DIR/lower_intrinsics_e2e.rs:21:15: 21:16 scope 2 (inlined std::mem::size_of::<()>) { // at $DIR/lower_intrinsics_e2e.rs:20:8: 20:32 } } @@ -14,15 +13,13 @@ fn f_unit() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:16: +1:18 StorageLive(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17 - StorageLive(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:15: 21:16 - _2 = f_zst::<()>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17 + _2 = f_zst::<()>(move _1) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:9: 21:17 // mir::Constant // + span: $DIR/lower_intrinsics_e2e.rs:21:9: 21:14 // + literal: Const { ty: fn(()) {f_zst::<()>}, val: Value() } } bb1: { - StorageDead(_3); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:16: 21:17 StorageDead(_2); // scope 1 at $DIR/lower_intrinsics_e2e.rs:21:17: 21:18 StorageDead(_1); // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:18: +1:19 return; // scope 0 at $DIR/lower_intrinsics_e2e.rs:+2:2: +2:2 diff --git a/src/test/mir-opt/try_identity_e2e.new.PreCodegen.after.mir b/src/test/mir-opt/try_identity_e2e.new.PreCodegen.after.mir index 330929c58c914..30185f3ffab29 100644 --- a/src/test/mir-opt/try_identity_e2e.new.PreCodegen.after.mir +++ b/src/test/mir-opt/try_identity_e2e.new.PreCodegen.after.mir @@ -3,77 +3,56 @@ fn new(_1: Result) -> Result { debug x => _1; // in scope 0 at $DIR/try_identity_e2e.rs:+0:14: +0:15 let mut _0: std::result::Result; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46 - let mut _2: T; // in scope 0 at $DIR/try_identity_e2e.rs:+2:9: +10:10 - let mut _3: std::ops::ControlFlow; // in scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 - let mut _4: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:22 - let _5: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21 - let mut _6: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:48: +4:49 - let _7: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22 - let mut _8: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:46: +5:47 - let mut _9: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+8:13: +8:37 - let _10: T; // in scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36 - let _11: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33 - let mut _12: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:49: +9:50 + let mut _2: std::ops::ControlFlow; // in scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 + let mut _3: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:22 + let mut _4: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:48: +4:49 + let mut _5: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:46: +5:47 + let mut _6: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+8:13: +8:37 + let _7: T; // in scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36 + let mut _8: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:49: +9:50 scope 1 { - debug v => _5; // in scope 1 at $DIR/try_identity_e2e.rs:+4:20: +4:21 + debug v => _4; // in scope 1 at $DIR/try_identity_e2e.rs:+4:20: +4:21 } scope 2 { - debug e => _7; // in scope 2 at $DIR/try_identity_e2e.rs:+5:21: +5:22 + debug e => _5; // in scope 2 at $DIR/try_identity_e2e.rs:+5:21: +5:22 } scope 3 { - debug v => _10; // in scope 3 at $DIR/try_identity_e2e.rs:+8:35: +8:36 + debug v => _7; // in scope 3 at $DIR/try_identity_e2e.rs:+8:35: +8:36 } scope 4 { - debug e => _11; // in scope 4 at $DIR/try_identity_e2e.rs:+9:32: +9:33 + debug e => _8; // in scope 4 at $DIR/try_identity_e2e.rs:+9:32: +9:33 } bb0: { - StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +10:10 - StorageLive(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 - _4 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20 - switchInt(move _4) -> [0_isize: bb2, 1_isize: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20 + StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 + _3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20 + switchInt(move _3) -> [0_isize: bb2, 1_isize: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20 } bb1: { - StorageLive(_7); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22 - _7 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22 - StorageLive(_8); // scope 2 at $DIR/try_identity_e2e.rs:+5:46: +5:47 - _8 = move _7; // scope 2 at $DIR/try_identity_e2e.rs:+5:46: +5:47 - Deinit(_3); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 - ((_3 as Break).0: E) = move _8; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 - discriminant(_3) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 - StorageDead(_8); // scope 2 at $DIR/try_identity_e2e.rs:+5:47: +5:48 - StorageDead(_7); // scope 0 at $DIR/try_identity_e2e.rs:+5:47: +5:48 - _9 = discriminant(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 - switchInt(move _9) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10 + _5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22 + Deinit(_2); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 + ((_2 as Break).0: E) = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 + discriminant(_2) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48 + _6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 + switchInt(move _6) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10 } bb2: { - StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21 - _5 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21 - StorageLive(_6); // scope 1 at $DIR/try_identity_e2e.rs:+4:48: +4:49 - _6 = move _5; // scope 1 at $DIR/try_identity_e2e.rs:+4:48: +4:49 - Deinit(_3); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 - ((_3 as Continue).0: T) = move _6; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 - discriminant(_3) = 0; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 - StorageDead(_6); // scope 1 at $DIR/try_identity_e2e.rs:+4:49: +4:50 - StorageDead(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:49: +4:50 - _9 = discriminant(_3); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 - switchInt(move _9) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10 + _4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21 + Deinit(_2); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 + ((_2 as Continue).0: T) = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 + discriminant(_2) = 0; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50 + _6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10 + switchInt(move _6) -> [0_isize: bb5, 1_isize: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10 } bb3: { - StorageLive(_11); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33 - _11 = move ((_3 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33 - StorageLive(_12); // scope 4 at $DIR/try_identity_e2e.rs:+9:49: +9:50 - _12 = move _11; // scope 4 at $DIR/try_identity_e2e.rs:+9:49: +9:50 + _8 = move ((_2 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33 Deinit(_0); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51 - ((_0 as Err).0: E) = move _12; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51 + ((_0 as Err).0: E) = move _8; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51 discriminant(_0) = 1; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51 - StorageDead(_12); // scope 4 at $DIR/try_identity_e2e.rs:+9:50: +9:51 - StorageDead(_11); // scope 0 at $DIR/try_identity_e2e.rs:+9:50: +9:51 - StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+11:5: +11:6 - StorageDead(_3); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 + StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 } @@ -82,15 +61,11 @@ fn new(_1: Result) -> Result { } bb5: { - StorageLive(_10); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36 - _10 = move ((_3 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36 - _2 = move _10; // scope 3 at $DIR/try_identity_e2e.rs:+8:41: +8:42 - StorageDead(_10); // scope 0 at $DIR/try_identity_e2e.rs:+8:41: +8:42 + _7 = move ((_2 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36 Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6 - ((_0 as Ok).0: T) = move _2; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6 + ((_0 as Ok).0: T) = move _7; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6 discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6 - StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+11:5: +11:6 - StorageDead(_3); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 + StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2 } } diff --git a/src/test/mir-opt/try_identity_e2e.old.PreCodegen.after.mir b/src/test/mir-opt/try_identity_e2e.old.PreCodegen.after.mir index 18d3e0fb2639d..2a9c7408c66eb 100644 --- a/src/test/mir-opt/try_identity_e2e.old.PreCodegen.after.mir +++ b/src/test/mir-opt/try_identity_e2e.old.PreCodegen.after.mir @@ -3,35 +3,26 @@ fn old(_1: Result) -> Result { debug x => _1; // in scope 0 at $DIR/try_identity_e2e.rs:+0:14: +0:15 let mut _0: std::result::Result; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46 - let mut _2: T; // in scope 0 at $DIR/try_identity_e2e.rs:+2:9: +5:10 - let mut _3: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:18 - let _4: T; // in scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17 - let _5: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18 - let mut _6: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:34: +4:35 + let mut _2: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:18 + let _3: T; // in scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17 + let mut _4: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:34: +4:35 scope 1 { - debug v => _4; // in scope 1 at $DIR/try_identity_e2e.rs:+3:16: +3:17 + debug v => _3; // in scope 1 at $DIR/try_identity_e2e.rs:+3:16: +3:17 } scope 2 { - debug e => _5; // in scope 2 at $DIR/try_identity_e2e.rs:+4:17: +4:18 + debug e => _4; // in scope 2 at $DIR/try_identity_e2e.rs:+4:17: +4:18 } bb0: { - StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +5:10 - _3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +2:16 - switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +2:16 + _2 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +2:16 + switchInt(move _2) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +2:16 } bb1: { - StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18 - _5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18 - StorageLive(_6); // scope 2 at $DIR/try_identity_e2e.rs:+4:34: +4:35 - _6 = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+4:34: +4:35 + _4 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18 Deinit(_0); // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36 - ((_0 as Err).0: E) = move _6; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36 + ((_0 as Err).0: E) = move _4; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36 discriminant(_0) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36 - StorageDead(_6); // scope 2 at $DIR/try_identity_e2e.rs:+4:35: +4:36 - StorageDead(_5); // scope 0 at $DIR/try_identity_e2e.rs:+4:35: +4:36 - StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+6:5: +6:6 return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2 } @@ -40,14 +31,10 @@ fn old(_1: Result) -> Result { } bb3: { - StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17 - _4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17 - _2 = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+3:22: +3:23 - StorageDead(_4); // scope 0 at $DIR/try_identity_e2e.rs:+3:22: +3:23 + _3 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17 Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6 - ((_0 as Ok).0: T) = move _2; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6 + ((_0 as Ok).0: T) = move _3; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6 discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6 - StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+6:5: +6:6 return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2 } } diff --git a/src/test/ui/async-await/large_moves.attribute.stderr b/src/test/ui/async-await/large_moves.attribute.stderr index da34f44b2d680..0c5452475a657 100644 --- a/src/test/ui/async-await/large_moves.attribute.stderr +++ b/src/test/ui/async-await/large_moves.attribute.stderr @@ -1,5 +1,5 @@ error: moving 10024 bytes - --> $DIR/large_moves.rs:12:13 + --> $DIR/large_moves.rs:13:13 | LL | let x = async { | _____________^ @@ -18,7 +18,7 @@ LL | #![deny(large_assignments)] | ^^^^^^^^^^^^^^^^^ error: moving 10024 bytes - --> $DIR/large_moves.rs:18:14 + --> $DIR/large_moves.rs:19:14 | LL | let z = (x, 42); | ^ value moved from here @@ -26,7 +26,7 @@ LL | let z = (x, 42); = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` error: moving 10024 bytes - --> $DIR/large_moves.rs:18:13 + --> $DIR/large_moves.rs:19:13 | LL | let z = (x, 42); | ^^^^^^^ value moved from here @@ -34,7 +34,7 @@ LL | let z = (x, 42); = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` error: moving 10024 bytes - --> $DIR/large_moves.rs:20:13 + --> $DIR/large_moves.rs:21:13 | LL | let a = z.0; | ^^^ value moved from here diff --git a/src/test/ui/async-await/large_moves.option.stderr b/src/test/ui/async-await/large_moves.option.stderr index da34f44b2d680..0c5452475a657 100644 --- a/src/test/ui/async-await/large_moves.option.stderr +++ b/src/test/ui/async-await/large_moves.option.stderr @@ -1,5 +1,5 @@ error: moving 10024 bytes - --> $DIR/large_moves.rs:12:13 + --> $DIR/large_moves.rs:13:13 | LL | let x = async { | _____________^ @@ -18,7 +18,7 @@ LL | #![deny(large_assignments)] | ^^^^^^^^^^^^^^^^^ error: moving 10024 bytes - --> $DIR/large_moves.rs:18:14 + --> $DIR/large_moves.rs:19:14 | LL | let z = (x, 42); | ^ value moved from here @@ -26,7 +26,7 @@ LL | let z = (x, 42); = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` error: moving 10024 bytes - --> $DIR/large_moves.rs:18:13 + --> $DIR/large_moves.rs:19:13 | LL | let z = (x, 42); | ^^^^^^^ value moved from here @@ -34,7 +34,7 @@ LL | let z = (x, 42); = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` error: moving 10024 bytes - --> $DIR/large_moves.rs:20:13 + --> $DIR/large_moves.rs:21:13 | LL | let a = z.0; | ^^^ value moved from here diff --git a/src/test/ui/async-await/large_moves.rs b/src/test/ui/async-await/large_moves.rs index 18bb538a81eb1..d43d0eec0cabc 100644 --- a/src/test/ui/async-await/large_moves.rs +++ b/src/test/ui/async-await/large_moves.rs @@ -7,6 +7,7 @@ // [option]compile-flags: -Zmove-size-limit=1000 // edition:2018 +// compile-flags: -Zmir-opt-level=0 fn main() { let x = async { //~ ERROR large_assignments