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::simplify::UsedInStmtLocals;
26use crate::util::is_within_packed;
27
28fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
34 let borrowed_locals = borrowed_locals(body);
35
36 let debuginfo_locals = debuginfo_locals(body);
39
40 let mut live = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals)
41 .iterate_to_fixpoint(tcx, body, None)
42 .into_results_cursor(body);
43
44 let mut call_operands_to_move = Vec::new();
47 let mut patch = Vec::new();
48
49 for (bb, bb_data) in traversal::preorder(body) {
50 if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind {
51 let loc = Location { block: bb, statement_index: bb_data.statements.len() };
52
53 live.seek_to_block_end(bb);
55 let mut state = live.get().clone();
56
57 for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() {
58 if let Operand::Copy(place) = *arg
59 && !place.is_indirect()
60 && !borrowed_locals.contains(place.local)
63 && !state.contains(place.local)
64 && is_within_packed(tcx, body, place).is_none()
69 {
70 call_operands_to_move.push((bb, index));
71 }
72
73 LivenessTransferFunction(&mut state).visit_operand(arg, loc);
75 }
76 }
77
78 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
79 if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead(
80 &statement.kind,
81 &borrowed_locals,
82 &debuginfo_locals,
83 ) {
84 let loc = Location { block: bb, statement_index };
85 live.seek_before_primary_effect(loc);
86 if !live.get().contains(destination.local) {
87 let drop_debuginfo = !debuginfo_locals.contains(destination.local);
88 assert!(
91 drop_debuginfo || statement.kind.as_debuginfo().is_some(),
92 "don't know how to retain the debug information for {:?}",
93 statement.kind
94 );
95 patch.push((loc, drop_debuginfo));
96 }
97 }
98 }
99 }
100
101 if patch.is_empty() && call_operands_to_move.is_empty() {
102 return false;
103 }
104 let eliminated = !patch.is_empty();
105
106 let bbs = body.basic_blocks.as_mut_preserves_cfg();
107 for (Location { block, statement_index }, drop_debuginfo) in patch {
108 bbs[block].statements[statement_index].make_nop(drop_debuginfo);
109 }
110 for (block, argument_index) in call_operands_to_move {
111 let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
112 bug!()
113 };
114 let arg = &mut args[argument_index].node;
115 let Operand::Copy(place) = *arg else { bug!() };
116 *arg = Operand::Move(place);
117 }
118
119 eliminated
120}
121
122pub(super) enum DeadStoreElimination {
123 Initial,
124 Final,
125}
126
127impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
128 fn name(&self) -> &'static str {
129 match self {
130 DeadStoreElimination::Initial => "DeadStoreElimination-initial",
131 DeadStoreElimination::Final => "DeadStoreElimination-final",
132 }
133 }
134
135 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
136 sess.mir_opt_level() >= 2
137 }
138
139 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
140 if eliminate(tcx, body) {
141 UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
142 for data in body.basic_blocks.as_mut_preserves_cfg() {
143 data.strip_nops();
144 }
145 }
146 }
147
148 fn is_required(&self) -> bool {
149 false
150 }
151}