diff --git a/src/librustc_mir/analysis/eventflow.rs b/src/librustc_mir/analysis/eventflow.rs index ac4adf7c5754a..95deeed4a6323 100644 --- a/src/librustc_mir/analysis/eventflow.rs +++ b/src/librustc_mir/analysis/eventflow.rs @@ -42,6 +42,10 @@ impl SparseBitSet { } } + pub fn capacity(&self) -> usize { + self.map.len() * 128 + } + pub fn contains(&self, index: I) -> bool { let (key, mask) = key_and_mask(index); self.map.get(&key).map_or(false, |bits| (bits & mask) != 0) diff --git a/src/librustc_mir/analysis/local_paths/mod.rs b/src/librustc_mir/analysis/local_paths/mod.rs index 9e9b07832331f..e34fbc3647351 100644 --- a/src/librustc_mir/analysis/local_paths/mod.rs +++ b/src/librustc_mir/analysis/local_paths/mod.rs @@ -109,6 +109,23 @@ impl<'tcx> LocalPaths<'tcx> { } } } + + /// Add a new local to the given `mir` (which is assumed to be the + /// one `self` was created from) and record a new path for it. + pub fn create_and_record_new_local(&mut self, + mir: &mut Mir<'tcx>, + decl: LocalDecl<'tcx>) + -> Local { + let path = self.data.push(PathData { + last_descendant: PathId::new(0), + ty: decl.ty + }); + self.data[path].last_descendant = path; + + let local = mir.local_decls.push(decl); + assert_eq!(self.locals.push(path), local); + local + } } pub struct Children<'a, 'tcx: 'a> { diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 563405fccc970..e38af9bc92251 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -44,6 +44,7 @@ pub mod copy_prop; pub mod generator; pub mod inline; pub mod lower_128bit; +pub mod unify_places; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -255,14 +256,19 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Optimizations begin. inline::Inline, + + // Lowering generator control-flow and variables + // has to happen before we do anything else to them. + generator::StateTransform, + instcombine::InstCombine, deaggregator::Deaggregator, + unify_places::UnifyPlaces, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("final"), simplify::SimplifyLocals, - generator::StateTransform, add_call_guards::CriticalCallEdges, dump_mir::Marker("PreTrans"), ]; diff --git a/src/librustc_mir/transform/unify_places.rs b/src/librustc_mir/transform/unify_places.rs new file mode 100644 index 0000000000000..40abac6396969 --- /dev/null +++ b/src/librustc_mir/transform/unify_places.rs @@ -0,0 +1,502 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_set::IdxSet; +use rustc::hir; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; +use rustc::session::config::FullDebugInfo; +use rustc::ty::TyCtxt; +use std::iter; +use std::mem; +use analysis::eventflow::{After, Before, SparseBitSet}; +use analysis::local_paths::{LocalPaths, PathId}; +use analysis::local_paths::accesses::Accesses; +use analysis::locations::FlatLocations; +use transform::{MirPass, MirSource}; + +struct Finder { + parent: IndexVec, +} + +impl Finder { + fn find(&mut self, i: I) -> I { + let parent = self.parent[i]; + if i == parent { + return i; + } + let root = self.find(parent); + if root != parent { + self.parent[i] = root; + } + root + } +} + +/// Union-find for the points of a binary symmetric relation. +/// Note that the relation is not transitive, only `union` is. +struct UnionFindSymRel { + finder: Finder, + relation: IndexVec>, +} + +impl UnionFindSymRel { + fn union(&mut self, a: I, b: I) -> I { + let a = self.finder.find(a); + let b = self.finder.find(b); + if a == b { + return a; + } + + let (root, child) = if self.relation[a].capacity() > self.relation[b].capacity() { + (a, b) + } else { + (b, a) + }; + self.finder.parent[child] = root; + + // Have to juggle the `self.relation` elements as we have + // no way to borrow two disjoint elements at the same time. + let child_relation = mem::replace(&mut self.relation[child], SparseBitSet::new()); + // FIXME(eddyb) This could use per-"word" bitwise operations. + for i in child_relation.iter() { + // HACK(eddyb) this is really bad, but used to propagate the relation. + let i = self.finder.find(i); + self.relation[root].insert(i); + self.relation[i].insert(root); + // FIXME(eddyb) check that this is even correct still... + } + self.relation[child] = child_relation; + + root + } + + fn relates(&mut self, a: I, b: I) -> bool { + let a = self.finder.find(a); + let b = self.finder.find(b); + self.relation[a].contains(b) || self.relation[b].contains(a) + } +} + +struct UnionFindWithConflictsAndPriority { + finder: Finder, + conflicts: UnionFindSymRel, + priority: P +} + +#[derive(Debug)] +struct Conflict; +impl T, T: Ord> UnionFindWithConflictsAndPriority { + fn union(&mut self, a: I, b: I) -> Result { + let a = self.finder.find(a); + let b = self.finder.find(b); + if a == b { + return Ok(a); + } + + if self.conflicts.relates(a, b) { + return Err(Conflict); + } + + let conflict_root = self.conflicts.union(a, b); + + let a_priority = ((self.priority)(a), a == conflict_root); + let b_priority = ((self.priority)(b), b == conflict_root); + let (root, child) = if a_priority > b_priority { + (a, b) + } else { + (b, a) + }; + self.finder.parent[child] = root; + + Ok(root) + } +} + +pub struct UnifyPlaces; + +impl MirPass for UnifyPlaces { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (_, Some(_)) | + (hir::BodyOwnerKind::Const, _) | + (hir::BodyOwnerKind::Static(_), _) => return, + + (hir::BodyOwnerKind::Fn, _) => { + if tcx.is_const_fn(source.def_id) { + // Don't run on const functions, as, again, trans might not be able to evaluate + // the optimized IR. + return + } + } + } + + // FIXME(eddyb) We should allow multiple user variables + // per local for debuginfo instead of not optimizing. + // FIXME(eddyb) (easier) Mark user variables as not renameble, + // we can always optimize *at least* temporaries! + if tcx.sess.opts.debuginfo == FullDebugInfo { + return; + } + // HACK(eddyb) testing to debug misoptimization + // if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { return; } + + let local_paths = &mut LocalPaths::collect(mir); + let mut path_to_place: FxHashMap = FxHashMap::default(); + let mut dependent_suffix; + let mut replacement_finder = { + let flat_locations = &FlatLocations::collect(mir); + let accesses = &Accesses::collect(mir, local_paths, flat_locations); + let mut observers = accesses.results.observe(); + let mut conflicts = IndexVec::from_elem_n(SparseBitSet::new(), + local_paths.total_count()); + let mut candidates = vec![]; + for (block, data) in mir.basic_blocks().iter_enumerated() { + let mut add_conflicts_at = |past: &IdxSet<_>, future: &IdxSet<_>| { + // FIXME(eddyb) use `diff_at_location` (how?) as an optimization. + for i in past.iter() { + // If `i` was accessed, so should anything inside it. + // FIXME(eddyb) Mark those fields as dependent directly. + // FIXME(eddyb) Maybe relax this? Right now it'll introduce + // artificial conflicts even if `a` and `a.x` are used on + // separate codepaths. Not sure how to make it sound though. + for descendant in local_paths.descendants(i) { + // FIXME(eddyb) Here and elsewhere this might not need + // to be symmetric because `UnionFindSymRel` is itself. + conflicts[descendant].insert(i); + conflicts[i].insert(descendant); + } + + if future.contains(&i) { + // FIXME(eddyb) Reduce the cost of this already Q_Q. + for j in past.iter() { + if i != j && future.contains(&j) { + conflicts[i].insert(j); + } + } + } + } + }; + let mut checked_after_last_statement = false; + for (statement_index, stmt) in data.statements.iter().enumerate() { + let location = Location { block, statement_index }; + match stmt.kind { + // FIXME(eddyb) figure out how to allow copies. + // Maybe if there is any candidate that's a copy, + // mark the unification as needing copies? Unclear. + // StatementKind::Assign(ref dest, Rvalue::Use(Operand::Copy(ref src))) | + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Move(ref src))) => { + if let Ok(dest_path) = local_paths.place_path(dest) { + if let Ok(src_path) = local_paths.place_path(src) { + candidates.push((dest, dest_path, src, src_path)); + if !checked_after_last_statement { + add_conflicts_at( + observers.past.seek(Before(location)), + observers.future.seek(Before(location))); + } + checked_after_last_statement = false; + continue; + } + } + } + _ => {} + } + add_conflicts_at( + observers.past.seek(After(location)), + observers.future.seek(Before(location))); + checked_after_last_statement = true; + } + let location = Location { + block, + statement_index: data.statements.len() + }; + add_conflicts_at( + observers.past.seek(After(location)), + observers.future.seek(Before(location))); + } + + let mut conflicts = UnionFindSymRel { + finder: Finder { + parent: (0..local_paths.total_count()).map(PathId::new).collect(), + }, + relation: conflicts + }; + + // FIXME(eddyb) find a better name than `dependent_suffix`. + dependent_suffix = { + let mut dependency_collector = DependencyCollector { + local_paths, + conflicts: &mut conflicts, + dependent_suffix: IndexVec::from_elem_n(0, local_paths.total_count()) + }; + dependency_collector.visit_mir(mir); + + // Treat every path in the return place and arguments + // as dependent, to avoid any of them being renamed away. + for local in iter::once(RETURN_PLACE).chain(mir.args_iter()) { + let path = local_paths.locals[local]; + dependency_collector.dependent_suffix[path] = 1; + for descendant in local_paths.descendants(path) { + dependency_collector.dependent_suffix[descendant] = 1; + dependency_collector.conflicts.union(descendant, path); + } + } + + for local in mir.vars_and_temps_iter() { + dependency_collector.collect_path(local_paths.locals[local], &mut vec![]); + } + + dependency_collector.dependent_suffix + }; + + let mut uf = UnionFindWithConflictsAndPriority { + finder: Finder { + parent: (0..local_paths.total_count()).map(PathId::new).collect(), + }, + conflicts, + priority: |path| { + // FIXME(eddyb) prefer shorter paths if they're both + // the same category (var/arg/return). + // That would achieve a form of SROA, maybe? + + dependent_suffix[path] > 0 + } + }; + + // Union together all the candidate source and targets. + // Candidates may fail if they could cause a conflict. + for (a, a_path, b, b_path) in candidates { + // HACK(eddyb) this is a bit messy, maybe should go into `union`. + let either_independent = + dependent_suffix[uf.finder.find(a_path)] == 0 || + dependent_suffix[uf.finder.find(b_path)] == 0; + if !either_independent { + continue; + } + + let union_result = uf.union(a_path, b_path); + debug!("unify_places: candidate ({:?} => {:?}) <- ({:?} => {:?}) unified to {:?}", + a_path, a, b_path, b, union_result); + if let Ok(root) = union_result { + // Cache the MIR `Place` representations of paths, for + // the paths that we might end up replacing to later. + let place = if root == a_path { + a + } else if root == b_path { + b + } else { + continue; + }; + path_to_place.entry(root).or_insert_with(|| place.clone()); + } + } + uf.finder + }; + + // Rename unrenamed independent fields when they would be + // clobbered from one of their parents being renamed over. + { + let mut used_as_rename_destination = SparseBitSet::new(); + for from in PathId::new(0)..PathId::new(local_paths.total_count()) { + let to = replacement_finder.find(from); + if from != to { + used_as_rename_destination.insert(to); + } + } + + for local in mir.vars_and_temps_iter() { + // FIXME(eddyb) use a range iterator over `used_as_rename_destination`. + let local_path = local_paths.locals[local]; + let rename_destinations = iter::once(local_path) + .chain(local_paths.descendants(local_path)) + .filter(|&p| used_as_rename_destination.contains(p)); + for destination in rename_destinations { + for path in local_paths.descendants(destination) { + let independent = dependent_suffix[path] == 0; + let renamed = replacement_finder.find(path) != path; + if independent && !renamed { + // Rename `path` to a a fresh temp to avoid clobbering. + let mut decl = mir.local_decls[local].clone(); + decl.ty = local_paths.data[path].ty; + decl.name = None; + decl.is_user_variable = false; + let new = local_paths.create_and_record_new_local(mir, decl); + let new_path = local_paths.locals[new]; + assert_eq!(new_path, replacement_finder.parent.push(new_path)); + assert_eq!(new_path, dependent_suffix.push(0)); + path_to_place.insert(new_path, Place::Local(new)); + replacement_finder.parent[path] = new_path; + + debug!("unify_places: de-clobbering {:?} (in {:?}) to \ + ({:?} => {:?})", + path, local, new_path, new); + } + } + } + } + } + + // Apply the replacements we computed previously. + let mut replacer = Replacer { + local_paths, + dependent_suffix: &dependent_suffix, + path_to_place, + replacement_finder + }; + replacer.visit_mir(mir); + } +} + +struct DependencyCollector<'a, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + conflicts: &'a mut UnionFindSymRel, + dependent_suffix: IndexVec +} + +impl<'a, 'tcx> DependencyCollector<'a, 'tcx> { + // FIXME(eddyb) Keep track of left/right ranges of variants + // at every level of parnts and check conflicts between. + // fields within the current path and lateral parent variants. + // Note that it appears to be working anyway, because of + // `Discriminant` and `SetDiscriminant` touching the whole enum. + fn collect_path(&mut self, path: PathId, prefixes: &mut Vec) { + prefixes.push(path); + for child in self.local_paths.children(path) { + self.collect_path(child, prefixes); + } + assert_eq!(prefixes.pop(), Some(path)); + + // This path is dependent (if at all) on the longest prefix it + // conflicts with, including conflicts included from child paths + // that depend on this one, gathered by the recursion above. + for (i, &parent) in prefixes.iter().rev().enumerate() { + if self.conflicts.relates(path, parent) { + debug!("unify_places: {:?} depends on {:?} (suffix length {})", + path, parent, i + 1); + self.dependent_suffix[path] = i as u32 + 1; + + // This path is now dependent on `parent`, so we need + // all of its conflicts to be reachable from `parent`. + // FIXME(eddyb) This is symmetric transitivity - maybe + // we want it to be directional instead? This will + // happily put `a.x` and `a.y` in the same conflict + // group (`a`'s`) just because they depend on `a`. + self.conflicts.union(path, parent); + + break; + } + } + + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DependencyCollector<'a, 'tcx> { + fn visit_projection_elem(&mut self, + elem: &PlaceElem<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + if let ProjectionElem::Index(i) = *elem { + let path = self.local_paths.locals[i]; + // Array indices should be integers which have no fields. + assert_eq!(self.local_paths.descendants(path).count(), 0); + self.dependent_suffix[path] = 1; + } + self.super_projection_elem(elem, context, location); + } +} + +struct Replacer<'a, 'tcx: 'a> { + local_paths: &'a LocalPaths<'tcx>, + dependent_suffix: &'a IndexVec, + path_to_place: FxHashMap>, + replacement_finder: Finder +} + +impl<'a, 'tcx> Replacer<'a, 'tcx> { + fn replace_if_needed(&mut self, place: &mut Place<'tcx>, mut skip: u32) { + // FIXME(eddyb) use a helper function to get the `PathId` + // at every level of the `Place` in `O(n)` instead of `O(n^2)`. + if skip == 0 { + if let Ok(from) = self.local_paths.place_path(place) { + let dependent_suffix = self.dependent_suffix[from]; + let to = self.replacement_finder.find(from); + + // This place is fully independent, so we either replace it, + // or leave alone (even if e.g. its parent might be replaced). + if dependent_suffix == 0 { + if to != from { + *place = self.path_to_place[&to].clone(); + + // Recurse in case the replacement is dependent + // on a parent that also needs to be replaced. + // FIXME(eddyb) precompute this into `path_to_place`. + self.replace_if_needed(place, 0); + } + return; + } + + assert_eq!(to, from); + skip = dependent_suffix - 1; + } + } else { + skip -= 1; + } + + if let Place::Projection(ref mut proj) = *place { + self.replace_if_needed(&mut proj.base, skip); + } + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for Replacer<'a, 'tcx> { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext<'tcx>, _: Location) { + self.replace_if_needed(place, 0); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + // FIXME(eddyb) fuse storage liveness ranges instead of removing them. + match statement.kind { + StatementKind::StorageLive(_local) | + StatementKind::StorageDead(_local) => { + // FIXME(eddyb) figure out how to even detect relevancy. + statement.kind = StatementKind::Nop; + } + _ => {} + } + + self.super_statement(block, statement, location); + + // Remove self-assignments resulting from replaced move chains. + // FIXME(eddyb) do this without comparing `Place`s. + // (within `super_statement` above we know `PathId`s for both sides) + let nop = match statement.kind { + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Copy(ref src))) | + StatementKind::Assign(ref dest, Rvalue::Use(Operand::Move(ref src))) => { + dest == src + } + _ => false + }; + if nop { + statement.kind = StatementKind::Nop; + } + } +} diff --git a/src/test/codegen/align-struct.rs b/src/test/codegen/align-struct.rs index 155319cb1541f..6d0de4472fe92 100644 --- a/src/test/codegen/align-struct.rs +++ b/src/test/codegen/align-struct.rs @@ -14,9 +14,11 @@ #![crate_type = "lib"] #[repr(align(64))] +#[derive(Copy, Clone)] pub struct Align64(i32); // CHECK: %Align64 = type { [0 x i32], i32, [15 x i32] } +#[derive(Copy, Clone)] pub struct Nested64 { a: Align64, b: i32, @@ -32,6 +34,7 @@ pub enum Enum4 { // CHECK: %Enum4 = type { [0 x i32], i32, [1 x i32] } // CHECK: %"Enum4::A" = type { [1 x i32], i32, [0 x i32] } +#[derive(Copy, Clone)] pub enum Enum64 { A(Align64), B(i32), @@ -59,7 +62,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { // CHECK-LABEL: @enum4 #[no_mangle] pub fn enum4(a: i32) -> Enum4 { -// CHECK: %e4 = alloca %Enum4, align 4 +// CHECK: alloca %Enum4, align 4 let e4 = Enum4::A(a); e4 } diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs deleted file mode 100644 index 1f900a3770eb3..0000000000000 --- a/src/test/codegen/lifetime_start_end.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -O -C no-prepopulate-passes - -#![crate_type = "lib"] - -// CHECK-LABEL: @test -#[no_mangle] -pub fn test() { - let a = 0; - &a; // keep variable in an alloca - -// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_a]]) - - { - let b = &Some(a); - &b; // keep variable in an alloca - -// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option"** %b to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_b]]) - -// CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__5]]) - -// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option"** %b to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]]) - -// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__5]]) - } - - let c = 1; - &c; // keep variable in an alloca - -// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8* -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_c]]) - -// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_c]]) - -// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8* -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_a]]) -} diff --git a/src/test/codegen/zip.rs b/src/test/codegen/zip.rs index d0051c5165fe1..e9088cb0d53cf 100644 --- a/src/test/codegen/zip.rs +++ b/src/test/codegen/zip.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -C no-prepopulate-passes -O +// compile-flags: -O #![crate_type = "lib"] diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 105afd30d28ab..8f8a707b5ac30 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -59,7 +59,7 @@ pub fn change_iteration_variable_name() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")] +#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_name() { let mut _x = 0; diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 50d8a5154c449..ce435bf94b5a3 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -24,10 +24,7 @@ fn main() { // ... // _3 = _1; // ... -// _2 = move _3; -// ... -// _4 = _2; -// _0 = move _4; +// _0 = _3; // ... // return; // } @@ -35,7 +32,7 @@ fn main() { // START rustc.test.CopyPropagation.after.mir // bb0: { // ... -// _0 = move _1; +// _0 = _1; // ... // return; // } diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs index 35bb231df5adf..820983a4fed22 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -71,12 +71,12 @@ fn main() { // END rustc.foo.CopyPropagation.after.mir // START rustc.bar.CopyPropagation.before.mir // bb0: { -// StorageLive(_3); +// ... // _3 = _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { -// StorageDead(_3); +// ... // _1 = const 5u8; // _0 = (); // return; @@ -96,10 +96,10 @@ fn main() { // END rustc.bar.CopyPropagation.after.mir // START rustc.baz.CopyPropagation.before.mir // bb0: { -// StorageLive(_2); +// ... // _2 = _1; // _1 = move _2; -// StorageDead(_2); +// ... // _0 = (); // return; // } @@ -116,12 +116,10 @@ fn main() { // bb0: { // ... // _3 = _1; -// _2 = move _3; // ... // _1 = const 123i32; // ... -// _4 = _2; -// _0 = move _4; +// _0 = _3; // ... // return; // } @@ -133,7 +131,7 @@ fn main() { // ... // _1 = const 123i32; // ... -// _0 = move _3; +// _0 = _3; // ... // return; // } diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs index 3fb54f90984dd..6ce51be3ec5db 100644 --- a/src/test/mir-opt/inline-closure-borrows-arg.rs +++ b/src/test/mir-opt/inline-closure-borrows-arg.rs @@ -38,11 +38,9 @@ fn foo(_t: T, q: &i32) -> i32 { // ... // _7 = &(*_2); // _5 = (move _6, move _7); -// _9 = move (_5.0: &i32); -// _10 = move (_5.1: &i32); -// StorageLive(_8); -// _8 = (*_9); -// _0 = move _8; +// _8 = move (_5.0: &i32); +// _9 = move (_5.1: &i32); +// _0 = (*_8); // ... // return; // } diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs index dc8ff13c03a88..22e7de31e90cf 100644 --- a/src/test/mir-opt/inline-closure.rs +++ b/src/test/mir-opt/inline-closure.rs @@ -36,7 +36,7 @@ fn foo(_t: T, q: i32) -> i32 { // _5 = (move _6, move _7); // _8 = move (_5.0: i32); // _9 = move (_5.1: i32); -// _0 = move _8; +// _0 = _8; // ... // return; // }