]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
1 | //! This module implements a dead store elimination (DSE) routine. |
2 | //! | |
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: | |
7 | //! | |
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. | |
13 | //! | |
14 | ||
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; | |
20 | ||
21 | /// Performs the optimization on the body | |
22 | /// | |
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); | |
30 | ||
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() { | |
37 | continue; | |
38 | } | |
39 | } | |
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) { | |
47 | patch.push(loc); | |
48 | } | |
49 | } | |
50 | } | |
51 | StatementKind::Retag(_, _) | |
52 | | StatementKind::StorageLive(_) | |
53 | | StatementKind::StorageDead(_) | |
54 | | StatementKind::Coverage(_) | |
f2b60f7d | 55 | | StatementKind::Intrinsic(_) |
923072b8 FG |
56 | | StatementKind::Nop => (), |
57 | ||
58 | StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { | |
59 | bug!("{:?} not found in this MIR phase!", &statement.kind) | |
60 | } | |
61 | } | |
62 | } | |
63 | } | |
64 | ||
65 | if patch.is_empty() { | |
66 | return; | |
67 | } | |
68 | ||
064997fb | 69 | let bbs = body.basic_blocks.as_mut_preserves_cfg(); |
923072b8 FG |
70 | for Location { block, statement_index } in patch { |
71 | bbs[block].statements[statement_index].make_nop(); | |
72 | } | |
487cf647 FG |
73 | |
74 | crate::simplify::SimplifyLocals.run_pass(tcx, body) | |
923072b8 FG |
75 | } |
76 | ||
77 | pub struct DeadStoreElimination; | |
78 | ||
79 | impl<'tcx> MirPass<'tcx> for DeadStoreElimination { | |
80 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { | |
81 | sess.mir_opt_level() >= 2 | |
82 | } | |
83 | ||
84 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
85 | let borrowed = borrowed_locals(body); | |
86 | eliminate(tcx, body, &borrowed); | |
87 | } | |
88 | } |