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 crate::util
::is_within_packed
;
16 use rustc_index
::bit_set
::BitSet
;
17 use rustc_middle
::mir
::visit
::Visitor
;
18 use rustc_middle
::mir
::*;
19 use rustc_middle
::ty
::TyCtxt
;
20 use rustc_mir_dataflow
::impls
::{
21 borrowed_locals
, LivenessTransferFunction
, MaybeTransitiveLiveLocals
,
23 use rustc_mir_dataflow
::Analysis
;
25 /// Performs the optimization on the body
27 /// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It
28 /// can be generated via the [`borrowed_locals`] function.
29 pub fn eliminate
<'tcx
>(tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>, borrowed
: &BitSet
<Local
>) {
30 let mut live
= MaybeTransitiveLiveLocals
::new(borrowed
)
31 .into_engine(tcx
, body
)
32 .iterate_to_fixpoint()
33 .into_results_cursor(body
);
35 // For blocks with a call terminator, if an argument copy can be turned into a move,
36 // record it as (block, argument index).
37 let mut call_operands_to_move
= Vec
::new();
38 let mut patch
= Vec
::new();
40 for (bb
, bb_data
) in traversal
::preorder(body
) {
41 if let TerminatorKind
::Call { ref args, .. }
= bb_data
.terminator().kind
{
42 let loc
= Location { block: bb, statement_index: bb_data.statements.len() }
;
44 // Position ourselves between the evaluation of `args` and the write to `destination`.
45 live
.seek_to_block_end(bb
);
46 let mut state
= live
.get().clone();
48 for (index
, arg
) in args
.iter().enumerate().rev() {
49 if let Operand
::Copy(place
) = *arg
50 && !place
.is_indirect()
51 && !borrowed
.contains(place
.local
)
52 && !state
.contains(place
.local
)
53 // If `place` is a projection of a disaligned field in a packed ADT,
54 // the move may be codegened as a pointer to that field.
55 // Using that disaligned pointer may trigger UB in the callee,
57 && is_within_packed(tcx
, body
, place
).is_none()
59 call_operands_to_move
.push((bb
, index
));
62 // Account that `arg` is read from, so we don't promote another argument to a move.
63 LivenessTransferFunction(&mut state
).visit_operand(arg
, loc
);
67 for (statement_index
, statement
) in bb_data
.statements
.iter().enumerate().rev() {
68 let loc
= Location { block: bb, statement_index }
;
69 if let StatementKind
::Assign(assign
) = &statement
.kind
{
70 if !assign
.1.is_safe_to_remove
() {
74 match &statement
.kind
{
75 StatementKind
::Assign(box (place
, _
))
76 | StatementKind
::SetDiscriminant { place: box place, .. }
77 | StatementKind
::Deinit(box place
) => {
78 if !place
.is_indirect() && !borrowed
.contains(place
.local
) {
79 live
.seek_before_primary_effect(loc
);
80 if !live
.get().contains(place
.local
) {
85 StatementKind
::Retag(_
, _
)
86 | StatementKind
::StorageLive(_
)
87 | StatementKind
::StorageDead(_
)
88 | StatementKind
::Coverage(_
)
89 | StatementKind
::Intrinsic(_
)
90 | StatementKind
::ConstEvalCounter
91 | StatementKind
::PlaceMention(_
)
92 | StatementKind
::Nop
=> (),
94 StatementKind
::FakeRead(_
) | StatementKind
::AscribeUserType(_
, _
) => {
95 bug
!("{:?} not found in this MIR phase!", &statement
.kind
)
101 if patch
.is_empty() && call_operands_to_move
.is_empty() {
105 let bbs
= body
.basic_blocks
.as_mut_preserves_cfg();
106 for Location { block, statement_index }
in patch
{
107 bbs
[block
].statements
[statement_index
].make_nop();
109 for (block
, argument_index
) in call_operands_to_move
{
110 let TerminatorKind
::Call { ref mut args, .. }
= bbs
[block
].terminator_mut().kind
else {
113 let arg
= &mut args
[argument_index
];
114 let Operand
::Copy(place
) = *arg
else { bug!() }
;
115 *arg
= Operand
::Move(place
);
118 crate::simplify
::simplify_locals(body
, tcx
)
121 pub struct DeadStoreElimination
;
123 impl<'tcx
> MirPass
<'tcx
> for DeadStoreElimination
{
124 fn is_enabled(&self, sess
: &rustc_session
::Session
) -> bool
{
125 sess
.mir_opt_level() >= 2
128 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>) {
129 let borrowed
= borrowed_locals(body
);
130 eliminate(tcx
, body
, &borrowed
);