rustc_mir_transform/
dead_store_elimination.rs1use rustc_middle::bug;
16use rustc_middle::mir::visit::Visitor;
17use rustc_middle::mir::*;
18use rustc_middle::ty::TyCtxt;
19use rustc_mir_dataflow::Analysis;
20use rustc_mir_dataflow::debuginfo::debuginfo_locals;
21use rustc_mir_dataflow::impls::{
22 LivenessTransferFunction, MaybeTransitiveLiveLocals, borrowed_locals,
23};
24
25use crate::PassPolicy;
26use crate::simplify::UsedInStmtLocals;
27use crate::util::most_packed_projection;
28
29fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
35 let borrowed_locals = borrowed_locals(body);
36
37 let debuginfo_locals = debuginfo_locals(body);
40
41 let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals)
42 .iterate_to_fixpoint(tcx, body, None)
43 .into_results_cursor(body);
44
45 let mut call_operands_to_move = Vec::new();
48 let mut patch = Vec::new();
49
50 for (bb, bb_data) in traversal::preorder(body) {
51 if let TerminatorKind::Call { ref args, ref destination, .. } = bb_data.terminator().kind {
52 let loc = Location { block: bb, statement_index: bb_data.statements.len() };
53
54 live.seek_to_block_end(bb);
56 let mut state = live.get().clone();
57
58 LivenessTransferFunction(&mut state).visit_place(
61 destination,
62 visit::PlaceContext::MutatingUse(visit::MutatingUseContext::Call),
63 loc,
64 );
65
66 for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() {
67 if let Operand::Copy(place) = *arg
68 && !place.is_indirect()
69 && !borrowed_locals.contains(place.local)
72 && !state.contains(place.local)
73 && most_packed_projection(tcx, body, place).is_none()
78 {
79 call_operands_to_move.push((bb, index));
80 }
81
82 LivenessTransferFunction(&mut state).visit_operand(arg, loc);
84 }
85 }
86
87 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
88 if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead(
89 &statement.kind,
90 &borrowed_locals,
91 &debuginfo_locals,
92 ) {
93 let loc = Location { block: bb, statement_index };
94 live.seek_before_primary_effect(loc);
95 if !live.get().contains(destination.local) {
96 let drop_debuginfo = !debuginfo_locals.contains(destination.local);
97 assert!(
100 drop_debuginfo || statement.kind.as_debuginfo().is_some(),
101 "don't know how to retain the debug information for {:?}",
102 statement.kind
103 );
104 patch.push((loc, drop_debuginfo));
105 }
106 }
107 }
108 }
109
110 if patch.is_empty() && call_operands_to_move.is_empty() {
111 return false;
112 }
113 let eliminated = !patch.is_empty();
114
115 let bbs = body.basic_blocks.as_mut_preserves_cfg();
116 for (Location { block, statement_index }, drop_debuginfo) in patch {
117 bbs[block].statements[statement_index].make_nop(drop_debuginfo);
118 }
119 for (block, argument_index) in call_operands_to_move {
120 let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
121 bug!()
122 };
123 let arg = &mut args[argument_index].node;
124 let Operand::Copy(place) = *arg else { bug!() };
125 *arg = Operand::Move(place);
126 }
127
128 eliminated
129}
130
131pub(super) enum DeadStoreElimination {
132 Initial,
133 Final,
134}
135
136impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
137 fn name(&self) -> &'static str {
138 match self {
139 DeadStoreElimination::Initial => "DeadStoreElimination-initial",
140 DeadStoreElimination::Final => "DeadStoreElimination-final",
141 }
142 }
143
144 fn policy(&self, sess: &rustc_session::Session) -> PassPolicy {
145 PassPolicy::optimization(sess.mir_opt_level() >= 2)
146 }
147
148 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
149 if eliminate(tcx, body) {
150 UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
151 for data in body.basic_blocks.as_mut_preserves_cfg() {
152 data.strip_nops();
153 }
154 }
155 }
156}