]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/dead_store_elimination.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / dead_store_elimination.rs
CommitLineData
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
15use rustc_index::bit_set::BitSet;
16use rustc_middle::mir::*;
17use rustc_middle::ty::TyCtxt;
18use rustc_mir_dataflow::impls::{borrowed_locals, MaybeTransitiveLiveLocals};
19use 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.
25pub 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(_)
55 | StatementKind::CopyNonOverlapping(_)
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
69 let bbs = body.basic_blocks_mut();
70 for Location { block, statement_index } in patch {
71 bbs[block].statements[statement_index].make_nop();
72 }
73}
74
75pub struct DeadStoreElimination;
76
77impl<'tcx> MirPass<'tcx> for DeadStoreElimination {
78 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
79 sess.mir_opt_level() >= 2
80 }
81
82 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
83 let borrowed = borrowed_locals(body);
84 eliminate(tcx, body, &borrowed);
85 }
86}