rustc_mir_transform/
add_moves_for_packed_drops.rs

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