summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs')
-rw-r--r--compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
new file mode 100644
index 000000000..5c441c5b1
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -0,0 +1,131 @@
+use crate::MirPass;
+use rustc_index::bit_set::BitSet;
+use rustc_middle::mir::patch::MirPatch;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_target::spec::PanicStrategy;
+
+/// 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;
+
+impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
+ fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+ sess.panic_strategy() != PanicStrategy::Abort
+ }
+
+ fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ debug!("remove_noop_landing_pads({:?})", body);
+ self.remove_nop_landing_pads(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::Coverage(..)
+ | StatementKind::Nop => {
+ // These are all noops in a landing pad
+ }
+
+ StatementKind::Assign(box (place, Rvalue::Use(_) | Rvalue::Discriminant(_))) => {
+ 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::Deinit(..)
+ | StatementKind::CopyNonOverlapping(..)
+ | StatementKind::Retag { .. } => {
+ return false;
+ }
+ }
+ }
+
+ let terminator = body[bb].terminator();
+ match terminator.kind {
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::SwitchInt { .. }
+ | TerminatorKind::FalseEdge { .. }
+ | 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 { .. }
+ | TerminatorKind::InlineAsm { .. } => false,
+ }
+ }
+
+ fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
+ debug!("body: {:#?}", body);
+
+ // make sure there's a resume block
+ let resume_block = {
+ let mut 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);
+ if let Some(unwind) = body[bb].terminator_mut().unwind_mut() {
+ if let Some(unwind_bb) = *unwind {
+ if nop_landing_pads.contains(unwind_bb) {
+ debug!(" removing noop landing pad");
+ landing_pads_removed += 1;
+ *unwind = None;
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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);
+ }
+}