]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/transform/copy_prop.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_mir / transform / copy_prop.rs
index ac8ebd306d321e844b2beda2cca17d3a7517ac33..2966290c2964c145ec98da79b5c3359b14cf6e00 100644 (file)
 //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
 //! future.
 
+use rustc::hir;
 use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind};
-use rustc::mir::transform::{MirPass, MirSource};
 use rustc::mir::visit::MutVisitor;
 use rustc::ty::TyCtxt;
+use transform::{MirPass, MirSource};
 use util::def_use::DefUseAnalysis;
 
 pub struct CopyPropagation;
@@ -42,25 +43,22 @@ impl MirPass for CopyPropagation {
                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           source: MirSource,
                           mir: &mut Mir<'tcx>) {
-        match source {
-            MirSource::Const(_) => {
-                // Don't run on constants, because constant qualification might reject the
-                // optimized IR.
-                return
-            }
-            MirSource::Static(..) | MirSource::Promoted(..) => {
-                // Don't run on statics and promoted statics, because trans might not be able to
-                // evaluate the optimized IR.
-                return
-            }
-            MirSource::Fn(function_node_id) => {
-                if tcx.is_const_fn(tcx.hir.local_def_id(function_node_id)) {
+        // 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
                 }
             }
-            MirSource::GeneratorDrop(_) => (),
         }
 
         // We only run when the MIR optimization level is > 1.
@@ -69,10 +67,14 @@ impl MirPass for CopyPropagation {
             return;
         }
 
+        let mut def_use_analysis = DefUseAnalysis::new(mir);
         loop {
-            let mut def_use_analysis = DefUseAnalysis::new(mir);
             def_use_analysis.analyze(mir);
 
+            if eliminate_self_assignments(mir, &def_use_analysis) {
+                def_use_analysis.analyze(mir);
+            }
+
             let mut changed = false;
             for dest_local in mir.local_decls.indices() {
                 debug!("Considering destination local: {:?}", dest_local);
@@ -99,9 +101,14 @@ impl MirPass for CopyPropagation {
                                dest_local);
                         continue
                     }
-                    let dest_lvalue_def = dest_use_info.defs_and_uses.iter().filter(|lvalue_def| {
-                        lvalue_def.context.is_mutating_use() && !lvalue_def.context.is_drop()
-                    }).next().unwrap();
+                    // Conservatively gives up if the dest is an argument,
+                    // because there may be uses of the original argument value.
+                    if mir.local_kind(dest_local) == LocalKind::Arg {
+                        debug!("  Can't copy-propagate local: dest {:?} (argument)",
+                            dest_local);
+                        continue;
+                    }
+                    let dest_lvalue_def = dest_use_info.defs_not_including_drop().next().unwrap();
                     location = dest_lvalue_def.location;
 
                     let basic_block = &mir[location.block];
@@ -151,6 +158,39 @@ impl MirPass for CopyPropagation {
     }
 }
 
+fn eliminate_self_assignments<'tcx>(
+    mir: &mut Mir<'tcx>,
+    def_use_analysis: &DefUseAnalysis<'tcx>,
+) -> bool {
+    let mut changed = false;
+
+    for dest_local in mir.local_decls.indices() {
+        let dest_use_info = def_use_analysis.local_info(dest_local);
+
+        for def in dest_use_info.defs_not_including_drop() {
+            let location = def.location;
+            if let Some(stmt) = mir[location.block].statements.get(location.statement_index) {
+                match stmt.kind {
+                    StatementKind::Assign(
+                        Lvalue::Local(local),
+                        Rvalue::Use(Operand::Consume(Lvalue::Local(src_local))),
+                    ) if local == dest_local && dest_local == src_local => {}
+                    _ => {
+                        continue;
+                    }
+                }
+            } else {
+                continue;
+            }
+            debug!("Deleting a self-assignment for {:?}", dest_local);
+            mir.make_statement_nop(location);
+            changed = true;
+        }
+    }
+
+    changed
+}
+
 enum Action<'tcx> {
     PropagateLocalCopy(Local),
     PropagateConstant(Constant<'tcx>),