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, drop, async_fut } =
87        terminator.kind
88    else {
89        unreachable!();
90    };
91
92    let source_info = terminator.source_info;
93    let ty = place.ty(body, tcx).ty;
94    let temp = patch.new_temp(ty, source_info.span);
95
96    let storage_dead_block = patch.new_block(BasicBlockData {
97        statements: vec![Statement { source_info, kind: StatementKind::StorageDead(temp) }],
98        terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }),
99        is_cleanup,
100    });
101
102    patch.add_statement(loc, StatementKind::StorageLive(temp));
103    patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place)));
104    patch.patch_terminator(
105        loc.block,
106        TerminatorKind::Drop {
107            place: Place::from(temp),
108            target: storage_dead_block,
109            unwind,
110            replace,
111            drop,
112            async_fut,
113        },
114    );
115}