about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src')
-rw-r--r--compiler/rustc_mir_transform/src/abort_unwinding_calls.rs37
-rw-r--r--compiler/rustc_mir_transform/src/patch.rs30
-rw-r--r--compiler/rustc_mir_transform/src/simplify_branches.rs19
3 files changed, 66 insertions, 20 deletions
diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
index 5bd6fdcf485..35a21a2a834 100644
--- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
+++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
@@ -3,6 +3,7 @@ use rustc_ast::InlineAsmOptions;
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, TyCtxt, layout};
+use rustc_span::sym;
 use rustc_target::spec::PanicStrategy;
 
 /// A pass that runs which is targeted at ensuring that codegen guarantees about
@@ -33,6 +34,19 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
             return;
         }
 
+        // Represent whether this compilation target fundamentally doesn't
+        // support unwinding at all at an ABI level. If this the target has no
+        // support for unwinding then cleanup actions, for example, are all
+        // unnecessary and can be considered unreachable.
+        //
+        // Currently this is only true for wasm targets on panic=abort when the
+        // `exception-handling` target feature is disabled. In such a
+        // configuration it's illegal to emit exception-related instructions so
+        // it's not possible to unwind.
+        let target_supports_unwinding = !(tcx.sess.target.is_like_wasm
+            && tcx.sess.panic_strategy() == PanicStrategy::Abort
+            && !tcx.asm_target_features(def_id).contains(&sym::exception_handling));
+
         // Here we test for this function itself whether its ABI allows
         // unwinding or not.
         let body_ty = tcx.type_of(def_id).skip_binder();
@@ -54,12 +68,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
             let Some(terminator) = &mut block.terminator else { continue };
             let span = terminator.source_info.span;
 
-            // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need
-            // to replace it with `UnwindTerminate`.
-            if let TerminatorKind::UnwindResume = &terminator.kind
-                && !body_can_unwind
-            {
-                terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi);
+            // If we see an `UnwindResume` terminator inside a function then:
+            //
+            // * If the target doesn't support unwinding at all, then this is an
+            //   unreachable block.
+            // * If the body cannot unwind, we need to replace it with
+            //   `UnwindTerminate`.
+            if let TerminatorKind::UnwindResume = &terminator.kind {
+                if !target_supports_unwinding {
+                    terminator.kind = TerminatorKind::Unreachable;
+                } else if !body_can_unwind {
+                    terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi);
+                }
             }
 
             if block.is_cleanup {
@@ -93,8 +113,9 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
                 _ => continue,
             };
 
-            if !call_can_unwind {
-                // If this function call can't unwind, then there's no need for it
+            if !call_can_unwind || !target_supports_unwinding {
+                // If this function call can't unwind, or if the target doesn't
+                // support unwinding at all, then there's no need for it
                 // to have a landing pad. This means that we can remove any cleanup
                 // registered for it (and turn it into `UnwindAction::Unreachable`).
                 let cleanup = block.terminator_mut().unwind_mut().unwrap();
diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs
index d831ab50b1a..cc8ea76011b 100644
--- a/compiler/rustc_mir_transform/src/patch.rs
+++ b/compiler/rustc_mir_transform/src/patch.rs
@@ -11,6 +11,8 @@ use tracing::debug;
 /// once with `apply`. This is useful for MIR transformation passes.
 pub(crate) struct MirPatch<'tcx> {
     term_patch_map: FxHashMap<BasicBlock, TerminatorKind<'tcx>>,
+    /// Set of statements that should be replaced by `Nop`.
+    nop_statements: Vec<Location>,
     new_blocks: Vec<BasicBlockData<'tcx>>,
     new_statements: Vec<(Location, StatementKind<'tcx>)>,
     new_locals: Vec<LocalDecl<'tcx>>,
@@ -33,6 +35,7 @@ impl<'tcx> MirPatch<'tcx> {
     pub(crate) fn new(body: &Body<'tcx>) -> Self {
         let mut result = MirPatch {
             term_patch_map: Default::default(),
+            nop_statements: vec![],
             new_blocks: vec![],
             new_statements: vec![],
             new_locals: vec![],
@@ -212,6 +215,15 @@ impl<'tcx> MirPatch<'tcx> {
         self.term_patch_map.insert(block, new);
     }
 
+    /// Mark given statement to be replaced by a `Nop`.
+    ///
+    /// This method only works on statements from the initial body, and cannot be used to remove
+    /// statements from `add_statement` or `add_assign`.
+    #[tracing::instrument(level = "debug", skip(self))]
+    pub(crate) fn nop_statement(&mut self, loc: Location) {
+        self.nop_statements.push(loc);
+    }
+
     /// Queues the insertion of a statement at a given location. The statement
     /// currently at that location, and all statements that follow, are shifted
     /// down. If multiple statements are queued for addition at the same
@@ -257,11 +269,8 @@ impl<'tcx> MirPatch<'tcx> {
         bbs.extend(self.new_blocks);
         body.local_decls.extend(self.new_locals);
 
-        // The order in which we patch terminators does not change the result.
-        #[allow(rustc::potential_query_instability)]
-        for (src, patch) in self.term_patch_map {
-            debug!("MirPatch: patching block {:?}", src);
-            bbs[src].terminator_mut().kind = patch;
+        for loc in self.nop_statements {
+            bbs[loc.block].statements[loc.statement_index].make_nop();
         }
 
         let mut new_statements = self.new_statements;
@@ -285,6 +294,17 @@ impl<'tcx> MirPatch<'tcx> {
                 .insert(loc.statement_index, Statement::new(source_info, stmt));
             delta += 1;
         }
+
+        // The order in which we patch terminators does not change the result.
+        #[allow(rustc::potential_query_instability)]
+        for (src, patch) in self.term_patch_map {
+            debug!("MirPatch: patching block {:?}", src);
+            let bb = &mut bbs[src];
+            if let TerminatorKind::Unreachable = patch {
+                bb.statements.clear();
+            }
+            bb.terminator_mut().kind = patch;
+        }
     }
 
     fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo {
diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs
index 886f4d6e509..ed94a058ec6 100644
--- a/compiler/rustc_mir_transform/src/simplify_branches.rs
+++ b/compiler/rustc_mir_transform/src/simplify_branches.rs
@@ -2,6 +2,8 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use tracing::trace;
 
+use crate::patch::MirPatch;
+
 pub(super) enum SimplifyConstCondition {
     AfterConstProp,
     Final,
@@ -19,8 +21,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         trace!("Running SimplifyConstCondition on {:?}", body.source);
         let typing_env = body.typing_env(tcx);
-        'blocks: for block in body.basic_blocks_mut() {
-            for stmt in block.statements.iter_mut() {
+        let mut patch = MirPatch::new(body);
+
+        'blocks: for (bb, block) in body.basic_blocks.iter_enumerated() {
+            for (statement_index, stmt) in block.statements.iter().enumerate() {
                 // Simplify `assume` of a known value: either a NOP or unreachable.
                 if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind
                     && let NonDivergingIntrinsic::Assume(discr) = intrinsic
@@ -28,17 +32,16 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
                     && let Some(constant) = c.const_.try_eval_bool(tcx, typing_env)
                 {
                     if constant {
-                        stmt.make_nop();
+                        patch.nop_statement(Location { block: bb, statement_index });
                     } else {
-                        block.statements.clear();
-                        block.terminator_mut().kind = TerminatorKind::Unreachable;
+                        patch.patch_terminator(bb, TerminatorKind::Unreachable);
                         continue 'blocks;
                     }
                 }
             }
 
-            let terminator = block.terminator_mut();
-            terminator.kind = match terminator.kind {
+            let terminator = block.terminator();
+            let terminator = match terminator.kind {
                 TerminatorKind::SwitchInt {
                     discr: Operand::Constant(ref c), ref targets, ..
                 } => {
@@ -58,7 +61,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
                 },
                 _ => continue,
             };
+            patch.patch_terminator(bb, terminator);
         }
+        patch.apply(body);
     }
 
     fn is_required(&self) -> bool {