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::most_packed_projection;
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, ref destination, .. } = 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 LivenessTransferFunction(&mut state).visit_place(
60 destination,
61 visit::PlaceContext::MutatingUse(visit::MutatingUseContext::Call),
62 loc,
63 );
64
65 for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() {
66 if let Operand::Copy(place) = *arg
67 && !place.is_indirect()
68 && !borrowed_locals.contains(place.local)
71 && !state.contains(place.local)
72 && most_packed_projection(tcx, body, place).is_none()
77 {
78 call_operands_to_move.push((bb, index));
79 }
80
81 LivenessTransferFunction(&mut state).visit_operand(arg, loc);
83 }
84 }
85
86 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
87 if let Some(destination) = MaybeTransitiveLiveLocals::can_be_removed_if_dead(
88 &statement.kind,
89 &borrowed_locals,
90 &debuginfo_locals,
91 ) {
92 let loc = Location { block: bb, statement_index };
93 live.seek_before_primary_effect(loc);
94 if !live.get().contains(destination.local) {
95 let drop_debuginfo = !debuginfo_locals.contains(destination.local);
96 assert!(
99 drop_debuginfo || statement.kind.as_debuginfo().is_some(),
100 "don't know how to retain the debug information for {:?}",
101 statement.kind
102 );
103 patch.push((loc, drop_debuginfo));
104 }
105 }
106 }
107 }
108
109 if patch.is_empty() && call_operands_to_move.is_empty() {
110 return false;
111 }
112 let eliminated = !patch.is_empty();
113
114 let bbs = body.basic_blocks.as_mut_preserves_cfg();
115 for (Location { block, statement_index }, drop_debuginfo) in patch {
116 bbs[block].statements[statement_index].make_nop(drop_debuginfo);
117 }
118 for (block, argument_index) in call_operands_to_move {
119 let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
120 bug!()
121 };
122 let arg = &mut args[argument_index].node;
123 let Operand::Copy(place) = *arg else { bug!() };
124 *arg = Operand::Move(place);
125 }
126
127 eliminated
128}
129
130pub(super) enum DeadStoreElimination {
131 Initial,
132 Final,
133}
134
135impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
136 fn name(&self) -> &'static str {
137 match self {
138 DeadStoreElimination::Initial => "DeadStoreElimination-initial",
139 DeadStoreElimination::Final => "DeadStoreElimination-final",
140 }
141 }
142
143 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
144 sess.mir_opt_level() >= 2
145 }
146
147 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
148 if eliminate(tcx, body) {
149 UsedInStmtLocals::new(body).remove_unused_storage_annotations(body);
150 for data in body.basic_blocks.as_mut_preserves_cfg() {
151 data.strip_nops();
152 }
153 }
154 }
155
156 fn is_required(&self) -> bool {
157 false
158 }
159}