1 //! This module implements a dead store elimination (DSE) routine.
3 //! This transformation was written specifically for the needs of dest prop. Although it is
4 //! perfectly sound to use it in any context that might need it, its behavior should not be changed
5 //! without analyzing the interaction this will have with dest prop. Specifically, in addition to
6 //! the soundness of this pass in general, dest prop needs it to satisfy two additional conditions:
8 //! 1. It's idempotent, meaning that running this pass a second time immediately after running it a
9 //! first time will not cause any further changes.
10 //! 2. This idempotence persists across dest prop's main transform, in other words inserting any
11 //! number of iterations of dest prop between the first and second application of this transform
12 //! will still not cause any further changes.
15 use rustc_index::bit_set::BitSet;
16 use rustc_middle::mir::*;
17 use rustc_middle::ty::TyCtxt;
18 use rustc_mir_dataflow::impls::{borrowed_locals, MaybeTransitiveLiveLocals};
19 use rustc_mir_dataflow::Analysis;
21 /// Performs the optimization on the body
23 /// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It
24 /// can be generated via the [`borrowed_locals`] function.
25 pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitSet<Local>) {
26 let mut live = MaybeTransitiveLiveLocals::new(borrowed)
27 .into_engine(tcx, body)
28 .iterate_to_fixpoint()
29 .into_results_cursor(body);
31 let mut patch = Vec::new();
32 for (bb, bb_data) in traversal::preorder(body) {
33 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
34 let loc = Location { block: bb, statement_index };
35 if let StatementKind::Assign(assign) = &statement.kind {
36 if !assign.1.is_safe_to_remove() {
40 match &statement.kind {
41 StatementKind::Assign(box (place, _))
42 | StatementKind::SetDiscriminant { place: box place, .. }
43 | StatementKind::Deinit(box place) => {
44 if !place.is_indirect() && !borrowed.contains(place.local) {
45 live.seek_before_primary_effect(loc);
46 if !live.get().contains(place.local) {
51 StatementKind::Retag(_, _)
52 | StatementKind::StorageLive(_)
53 | StatementKind::StorageDead(_)
54 | StatementKind::Coverage(_)
55 | StatementKind::Intrinsic(_)
56 | StatementKind::Nop => (),
58 StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
59 bug!("{:?} not found in this MIR phase!", &statement.kind)
69 let bbs = body.basic_blocks.as_mut_preserves_cfg();
70 for Location { block, statement_index } in patch {
71 bbs[block].statements[statement_index].make_nop();
74 crate::simplify::SimplifyLocals.run_pass(tcx, body)
77 pub struct DeadStoreElimination;
79 impl<'tcx> MirPass<'tcx> for DeadStoreElimination {
80 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
81 sess.mir_opt_level() >= 2
84 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
85 let borrowed = borrowed_locals(body);
86 eliminate(tcx, body, &borrowed);