blob: c636aba9fc607fd4b6bf405deec4065341e6cf2e [file] [log] [blame]
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
/// 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 BodyCache<'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 BodyCache<'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 BodyCache<'_>) {
// 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);
}
}