about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_transform/src/abort_unwinding_calls.rs37
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs9
-rw-r--r--tests/codegen-llvm/unwind-and-panic-abort.rs9
-rw-r--r--tests/codegen-llvm/wasm_exceptions.rs57
5 files changed, 86 insertions, 27 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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 63b0de377c9..b34a64108e3 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -940,6 +940,7 @@ symbols! {
         ermsb_target_feature,
         exact_div,
         except,
+        exception_handling: "exception-handling",
         exchange_malloc,
         exclusive_range_pattern,
         exhaustive_integer_patterns,
diff --git a/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs b/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs
index 8d2745ba2f7..2ce1d1b2e00 100644
--- a/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs
+++ b/tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs
@@ -1,4 +1,9 @@
 //@ compile-flags: -C panic=abort
+//@ revisions: NONWASM WASM WASMEXN
+//@ [NONWASM] ignore-wasm32
+//@ [WASM] only-wasm32
+//@ [WASMEXN] only-wasm32
+//@ [WASMEXN] compile-flags: -Ctarget-feature=+exception-handling
 
 // Test that `nounwind` attributes are also applied to extern `C-unwind` Rust functions
 // when the code is compiled with `panic=abort`.
@@ -9,7 +14,9 @@
 #[no_mangle]
 pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
     // Handle both legacy and v0 symbol mangling.
-    // CHECK: call void @{{.*core9panicking19panic_cannot_unwind}}
+    // NONWASM: call void @{{.*core9panicking19panic_cannot_unwind}}
+    // WASMEXN: call void @{{.*core9panicking19panic_cannot_unwind}}
+    // WASM-NOT: call void @{{.*core9panicking19panic_cannot_unwind}}
     may_unwind();
 }
 
diff --git a/tests/codegen-llvm/unwind-and-panic-abort.rs b/tests/codegen-llvm/unwind-and-panic-abort.rs
index 8efa140058a..c2838be00af 100644
--- a/tests/codegen-llvm/unwind-and-panic-abort.rs
+++ b/tests/codegen-llvm/unwind-and-panic-abort.rs
@@ -1,4 +1,9 @@
 //@ compile-flags: -C panic=abort
+//@ revisions: NONWASM WASM WASMEXN
+//@ [NONWASM] ignore-wasm32
+//@ [WASM] only-wasm32
+//@ [WASMEXN] only-wasm32
+//@ [WASMEXN] compile-flags: -Ctarget-feature=+exception-handling
 
 #![crate_type = "lib"]
 
@@ -9,7 +14,9 @@ extern "C-unwind" {
 // CHECK: Function Attrs:{{.*}}nounwind
 // CHECK-NEXT: define{{.*}}void @foo
 // Handle both legacy and v0 symbol mangling.
-// CHECK: call void @{{.*core9panicking19panic_cannot_unwind}}
+// NONWASM: call void @{{.*core9panicking19panic_cannot_unwind}}
+// WASMEXN: call void @{{.*core9panicking19panic_cannot_unwind}}
+// WASM-NOT: call void @{{.*core9panicking19panic_cannot_unwind}}
 #[no_mangle]
 pub unsafe extern "C" fn foo() {
     bar();
diff --git a/tests/codegen-llvm/wasm_exceptions.rs b/tests/codegen-llvm/wasm_exceptions.rs
index 796b69b722b..e718f599a3c 100644
--- a/tests/codegen-llvm/wasm_exceptions.rs
+++ b/tests/codegen-llvm/wasm_exceptions.rs
@@ -1,8 +1,9 @@
 //@ only-wasm32
-//@ compile-flags: -C panic=unwind -Z emscripten-wasm-eh
+//@ revisions: WASM WASMEXN
+//@ [WASMEXN] compile-flags: -C panic=unwind -Z emscripten-wasm-eh
 
 #![crate_type = "lib"]
-#![feature(core_intrinsics, wasm_exception_handling_intrinsics)]
+#![feature(core_intrinsics, wasm_exception_handling_intrinsics, link_llvm_intrinsics)]
 
 extern "C-unwind" {
     fn may_panic();
@@ -22,7 +23,8 @@ impl Drop for LogOnDrop {
     }
 }
 
-// CHECK-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0
+// WASM-LABEL: @test_cleanup() {{.*}}
+// WASMEXN-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0
 #[no_mangle]
 pub fn test_cleanup() {
     let _log_on_drop = LogOnDrop;
@@ -30,12 +32,16 @@ pub fn test_cleanup() {
         may_panic();
     }
 
-    // CHECK-NOT: call
-    // CHECK: invoke void @may_panic()
-    // CHECK: %cleanuppad = cleanuppad within none []
+    // WASMEXN-NOT: call
+    // WASMEXN: invoke void @may_panic()
+    // WASMEXN: %cleanuppad = cleanuppad within none []
+    //
+    // WASM: call void @may_panic()
+    // WASM-NOT: invoke void @may_panic()
 }
 
-// CHECK-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0
+// WASM-LABEL: @test_rtry() {{.*}}
+// WASMEXN-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0
 #[no_mangle]
 pub fn test_rtry() {
     unsafe {
@@ -51,23 +57,40 @@ pub fn test_rtry() {
         );
     }
 
-    // CHECK-NOT: call
-    // CHECK: invoke void @may_panic()
-    // CHECK: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller
-    // CHECK: {{.*}} = catchpad within {{.*}} [ptr null]
-    // CHECK: catchret
+    // WASMEXN-NOT: call
+    // WASMEXN: invoke void @may_panic()
+    // WASMEXN: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller
+    // WASMEXN: {{.*}} = catchpad within {{.*}} [ptr null]
+    // WASMEXN: catchret
+
+    // WASM: call void @may_panic()
+    // WASM-NOT: invoke void @may_panic()
+    // WASM-NOT: catchswitch
+    // WASM-NOT: catchpad
+    // WASM-NOT: catchret
 }
 
 // Make sure the intrinsic is not inferred as nounwind. This is a regression test for #132416.
-// CHECK-LABEL: @test_intrinsic() {{.*}} @__gxx_wasm_personality_v0
+//
+// Note that this test uses the raw `wasm_throw` intrinsic because the one from
+// libstd was built with `-Cpanic=abort` and it's technically not valid to use
+// when this crate is compiled with `-Cpanic=unwind`.
+//
+// WASMEXN-LABEL: @test_intrinsic() {{.*}} @__gxx_wasm_personality_v0
 #[no_mangle]
+#[cfg(wasmexn)]
 pub fn test_intrinsic() {
     let _log_on_drop = LogOnDrop;
+
+    unsafe extern "C-unwind" {
+        #[link_name = "llvm.wasm.throw"]
+        fn wasm_throw(tag: i32, ptr: *mut u8) -> !;
+    }
     unsafe {
-        core::arch::wasm32::throw::<0>(core::ptr::null_mut());
+        wasm_throw(0, core::ptr::null_mut());
     }
 
-    // CHECK-NOT: call
-    // CHECK: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null)
-    // CHECK: %cleanuppad = cleanuppad within none []
+    // WASMEXN-NOT: call
+    // WASMEXN: invoke void @llvm.wasm.throw(i32 noundef 0, ptr noundef null)
+    // WASMEXN: %cleanuppad = cleanuppad within none []
 }