// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use rustc::ty::TyCtxt; use rustc::mir::*; use rustc_data_structures::bit_set::BitSet; use transform::{MirPass, MirSource}; use util::patch::MirPatch; /// A pass that removes no-op 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<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &mut Mir<'tcx>) { if tcx.sess.no_landing_pads() { return } debug!("remove_noop_landing_pads({:?})", mir); RemoveNoopLandingPads.remove_nop_landing_pads(mir) } impl MirPass for RemoveNoopLandingPads { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) { remove_noop_landing_pads(tcx, mir); } } impl RemoveNoopLandingPads { fn is_nop_landing_pad( &self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitSet, ) -> bool { for stmt in &mir[bb].statements { match stmt.kind { StatementKind::FakeRead(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::EndRegion(_) | StatementKind::AscribeUserType(..) | StatementKind::Nop => { // These are all nops in a landing pad (there's some // borrowck interaction between EndRegion and storage // instructions, but this should all run after borrowck). } StatementKind::Assign(Place::Local(_), Rvalue::Use(_)) => { // Writing to a local (e.g. a drop flag) does not // turn a landing pad to a non-nop } StatementKind::Assign(_, _) | StatementKind::SetDiscriminant { .. } | StatementKind::InlineAsm { .. } | StatementKind::Validate { .. } => { return false; } } } let terminator = mir[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, mir: &mut Mir) { // make sure there's a single resume block let resume_block = { let patch = MirPatch::new(mir); let resume_block = patch.resume_block(); patch.apply(mir); 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(mir.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(mir).map(|(bb, _)| bb).collect(); for bb in postorder { debug!(" processing {:?}", bb); for target in mir[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 mir[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, mir, &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); } }