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::util::is_within_packed;
26
27fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
32 let borrowed_locals = borrowed_locals(body);
33
34 let mut always_live = debuginfo_locals(body);
37 always_live.union(&borrowed_locals);
38
39 let mut live = MaybeTransitiveLiveLocals::new(&always_live)
40 .iterate_to_fixpoint(tcx, body, None)
41 .into_results_cursor(body);
42
43 let mut call_operands_to_move = Vec::new();
46 let mut patch = Vec::new();
47
48 for (bb, bb_data) in traversal::preorder(body) {
49 if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind {
50 let loc = Location { block: bb, statement_index: bb_data.statements.len() };
51
52 live.seek_to_block_end(bb);
54 let mut state = live.get().clone();
55
56 for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() {
57 if let Operand::Copy(place) = *arg
58 && !place.is_indirect()
59 && !borrowed_locals.contains(place.local)
62 && !state.contains(place.local)
63 && is_within_packed(tcx, body, place).is_none()
68 {
69 call_operands_to_move.push((bb, index));
70 }
71
72 LivenessTransferFunction(&mut state).visit_operand(arg, loc);
74 }
75 }
76
77 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
78 let loc = Location { block: bb, statement_index };
79 if let StatementKind::Assign(assign) = &statement.kind {
80 if !assign.1.is_safe_to_remove() {
81 continue;
82 }
83 }
84 match &statement.kind {
85 StatementKind::Assign(box (place, _))
86 | StatementKind::SetDiscriminant { place: box place, .. }
87 | StatementKind::Deinit(box place) => {
88 if !place.is_indirect() && !always_live.contains(place.local) {
89 live.seek_before_primary_effect(loc);
90 if !live.get().contains(place.local) {
91 patch.push(loc);
92 }
93 }
94 }
95 StatementKind::Retag(_, _)
96 | StatementKind::StorageLive(_)
97 | StatementKind::StorageDead(_)
98 | StatementKind::Coverage(_)
99 | StatementKind::Intrinsic(_)
100 | StatementKind::ConstEvalCounter
101 | StatementKind::PlaceMention(_)
102 | StatementKind::BackwardIncompatibleDropHint { .. }
103 | StatementKind::Nop => {}
104
105 StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
106 bug!("{:?} not found in this MIR phase!", statement.kind)
107 }
108 }
109 }
110 }
111
112 if patch.is_empty() && call_operands_to_move.is_empty() {
113 return;
114 }
115
116 let bbs = body.basic_blocks.as_mut_preserves_cfg();
117 for Location { block, statement_index } in patch {
118 bbs[block].statements[statement_index].make_nop();
119 }
120 for (block, argument_index) in call_operands_to_move {
121 let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else {
122 bug!()
123 };
124 let arg = &mut args[argument_index].node;
125 let Operand::Copy(place) = *arg else { bug!() };
126 *arg = Operand::Move(place);
127 }
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 eliminate(tcx, body);
149 }
150
151 fn is_required(&self) -> bool {
152 false
153 }
154}