| use crate::transform::{MirPass, MirSource}; |
| use crate::util::patch::MirPatch; |
| use rustc::mir::*; |
| use rustc::ty::TyCtxt; |
| use rustc_index::bit_set::BitSet; |
| |
| /// A pass that removes noop landing pads and replaces jumps to them with |
| /// `None`. This is important because otherwise LLVM generates terrible |
| /// code for these. |
| pub struct RemoveNoopLandingPads; |
| |
| pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) { |
| if tcx.sess.no_landing_pads() { |
| return; |
| } |
| debug!("remove_noop_landing_pads({:?})", body); |
| |
| RemoveNoopLandingPads.remove_nop_landing_pads(body) |
| } |
| |
| impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { |
| fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { |
| remove_noop_landing_pads(tcx, body); |
| } |
| } |
| |
| impl RemoveNoopLandingPads { |
| fn is_nop_landing_pad( |
| &self, |
| bb: BasicBlock, |
| body: &Body<'_>, |
| nop_landing_pads: &BitSet<BasicBlock>, |
| ) -> bool { |
| for stmt in &body[bb].statements { |
| match &stmt.kind { |
| StatementKind::FakeRead(..) |
| | StatementKind::StorageLive(_) |
| | StatementKind::StorageDead(_) |
| | StatementKind::AscribeUserType(..) |
| | StatementKind::Nop => { |
| // These are all nops in a landing pad |
| } |
| |
| StatementKind::Assign(box (place, Rvalue::Use(_))) => { |
| if place.as_local().is_some() { |
| // Writing to a local (e.g., a drop flag) does not |
| // turn a landing pad to a non-nop |
| } else { |
| return false; |
| } |
| } |
| |
| StatementKind::Assign { .. } |
| | StatementKind::SetDiscriminant { .. } |
| | StatementKind::InlineAsm { .. } |
| | StatementKind::Retag { .. } => { |
| return false; |
| } |
| } |
| } |
| |
| let terminator = body[bb].terminator(); |
| match terminator.kind { |
| TerminatorKind::Goto { .. } |
| | TerminatorKind::Resume |
| | TerminatorKind::SwitchInt { .. } |
| | TerminatorKind::FalseEdges { .. } |
| | TerminatorKind::FalseUnwind { .. } => { |
| terminator.successors().all(|&succ| nop_landing_pads.contains(succ)) |
| } |
| TerminatorKind::GeneratorDrop |
| | TerminatorKind::Yield { .. } |
| | TerminatorKind::Return |
| | TerminatorKind::Abort |
| | TerminatorKind::Unreachable |
| | TerminatorKind::Call { .. } |
| | TerminatorKind::Assert { .. } |
| | TerminatorKind::DropAndReplace { .. } |
| | TerminatorKind::Drop { .. } => false, |
| } |
| } |
| |
| fn remove_nop_landing_pads(&self, body: &mut BodyAndCache<'_>) { |
| // make sure there's a single resume block |
| let resume_block = { |
| let patch = MirPatch::new(body); |
| let resume_block = patch.resume_block(); |
| patch.apply(body); |
| resume_block |
| }; |
| debug!("remove_noop_landing_pads: resume block is {:?}", resume_block); |
| |
| let mut jumps_folded = 0; |
| let mut landing_pads_removed = 0; |
| let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks().len()); |
| |
| // This is a post-order traversal, so that if A post-dominates B |
| // then A will be visited before B. |
| let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); |
| for bb in postorder { |
| debug!(" processing {:?}", bb); |
| for target in body[bb].terminator_mut().successors_mut() { |
| if *target != resume_block && nop_landing_pads.contains(*target) { |
| debug!(" folding noop jump to {:?} to resume block", target); |
| *target = resume_block; |
| jumps_folded += 1; |
| } |
| } |
| |
| match body[bb].terminator_mut().unwind_mut() { |
| Some(unwind) => { |
| if *unwind == Some(resume_block) { |
| debug!(" removing noop landing pad"); |
| jumps_folded -= 1; |
| landing_pads_removed += 1; |
| *unwind = None; |
| } |
| } |
| _ => {} |
| } |
| |
| let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); |
| if is_nop_landing_pad { |
| nop_landing_pads.insert(bb); |
| } |
| debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); |
| } |
| |
| debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); |
| } |
| } |