use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use crate::util; use crate::MirPass; use rustc_middle::mir::patch::MirPatch; /// This pass moves values being dropped that are within a packed /// struct to a separate local before dropping them, to ensure that /// they are dropped from an aligned address. /// /// For example, if we have something like /// ```ignore (ilustrative) /// #[repr(packed)] /// struct Foo { /// dealign: u8, /// data: Vec /// } /// /// let foo = ...; /// ``` /// /// We want to call `drop_in_place::>` on `data` from an aligned /// address. This means we can't simply drop `foo.data` directly, because /// its address is not aligned. /// /// Instead, we move `foo.data` to a local and drop that: /// ```ignore (ilustrative) /// storage.live(drop_temp) /// drop_temp = foo.data; /// drop(drop_temp) -> next /// next: /// storage.dead(drop_temp) /// ``` /// /// The storage instructions are required to avoid stack space /// blowup. pub struct AddMovesForPackedDrops; impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("add_moves_for_packed_drops({:?} @ {:?})", body.source, body.span); add_moves_for_packed_drops(tcx, body); } } pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let patch = add_moves_for_packed_drops_patch(tcx, body); patch.apply(body); } fn add_moves_for_packed_drops_patch<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> MirPatch<'tcx> { let def_id = body.source.def_id(); let mut patch = MirPatch::new(body); let param_env = tcx.param_env(def_id); for (bb, data) in body.basic_blocks.iter_enumerated() { let loc = Location { block: bb, statement_index: data.statements.len() }; let terminator = data.terminator(); match terminator.kind { TerminatorKind::Drop { place, .. } if util::is_disaligned(tcx, body, param_env, place) => { add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup); } _ => {} } } patch } fn add_move_for_packed_drop<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, patch: &mut MirPatch<'tcx>, terminator: &Terminator<'tcx>, loc: Location, is_cleanup: bool, ) { debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); let TerminatorKind::Drop { ref place, target, unwind } = terminator.kind else { unreachable!(); }; let source_info = terminator.source_info; let ty = place.ty(body, tcx).ty; let temp = patch.new_temp(ty, terminator.source_info.span); let storage_dead_block = patch.new_block(BasicBlockData { statements: vec![Statement { source_info, kind: StatementKind::StorageDead(temp) }], terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), is_cleanup, }); patch.add_statement(loc, StatementKind::StorageLive(temp)); patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place))); patch.patch_terminator( loc.block, TerminatorKind::Drop { place: Place::from(temp), target: storage_dead_block, unwind }, ); }