Skip to main content

rustc_mir_transform/
add_moves_for_packed_drops.rs

1use rustc_data_structures::thin_vec::ThinVec;
2use rustc_middle::mir::*;
3use rustc_middle::ty::{self, TyCtxt};
4use tracing::debug;
5
6use crate::patch::MirPatch;
7use crate::util;
8
9/// This pass moves values being dropped that are within a packed
10/// struct to a separate local before dropping them, to ensure that
11/// they are dropped from an aligned address.
12///
13/// For example, if we have something like
14///
15/// ```ignore (illustrative)
16/// #[repr(packed)]
17/// struct Foo {
18///     dealign: u8,
19///     data: Vec<u8>
20/// }
21///
22/// let foo = ...;
23/// ```
24///
25/// We want to call `drop_glue::<Vec<u8>>` with a reference to `data`, which must be aligned.
26/// This means we can't simply drop `foo.data` directly, because its address is not aligned.
27///
28/// Instead, we move `foo.data` to a local and drop that:
29/// ```ignore (illustrative)
30///     storage.live(drop_temp)
31///     drop_temp = foo.data;
32///     drop(drop_temp) -> next
33/// next:
34///     storage.dead(drop_temp)
35/// ```
36///
37/// The storage instructions are required to avoid stack space
38/// blowup.
39pub(super) struct AddMovesForPackedDrops;
40
41impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops {
42    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
43        debug!("add_moves_for_packed_drops({:?} @ {:?})", body.source, body.span);
44        let mut patch = MirPatch::new(body);
45        // FIXME(#132279): This is used during the phase transition from analysis
46        // to runtime, so we have to manually specify the correct typing mode.
47        let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
48
49        for (bb, data) in body.basic_blocks.iter_enumerated() {
50            let loc = Location { block: bb, statement_index: data.statements.len() };
51            let terminator = data.terminator();
52
53            match terminator.kind {
54                TerminatorKind::Drop { place, .. }
55                    if util::place_unalignment(tcx, body, typing_env, place).is_some() =>
56                {
57                    add_move_for_packed_drop(
58                        tcx,
59                        body,
60                        &mut patch,
61                        terminator,
62                        loc,
63                        data.is_cleanup,
64                    );
65                }
66                _ => {}
67            }
68        }
69
70        patch.apply(body);
71    }
72
73    fn is_required(&self) -> bool {
74        true
75    }
76}
77
78fn add_move_for_packed_drop<'tcx>(
79    tcx: TyCtxt<'tcx>,
80    body: &Body<'tcx>,
81    patch: &mut MirPatch<'tcx>,
82    terminator: &Terminator<'tcx>,
83    loc: Location,
84    is_cleanup: bool,
85) {
86    debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc);
87    let TerminatorKind::Drop { ref place, target, unwind, replace, drop } = terminator.kind else {
88        unreachable!();
89    };
90
91    let source_info = terminator.source_info;
92    let ty = place.ty(body, tcx).ty;
93    let temp = patch.new_temp(ty, source_info.span);
94
95    let storage_dead_block = patch.new_block(BasicBlockData::new_stmts(
96        vec![Statement::new(source_info, StatementKind::StorageDead(temp))],
97        Some(Terminator {
98            source_info,
99            kind: TerminatorKind::Goto { target },
100            attributes: ThinVec::new(),
101        }),
102        is_cleanup,
103    ));
104
105    patch.add_statement(loc, StatementKind::StorageLive(temp));
106    patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place), WithRetag::Yes));
107    patch.patch_terminator(
108        loc.block,
109        TerminatorKind::Drop {
110            place: Place::from(temp),
111            target: storage_dead_block,
112            unwind,
113            replace,
114            drop,
115        },
116    );
117}