//! (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;
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.
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);
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];
}
}
+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>),