Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ symbols! {
ermsb_target_feature,
exact_div,
except,
exception_handling: "exception-handling",
exchange_malloc,
exclusive_range_pattern,
exhaustive_integer_patterns,
Expand Down
9 changes: 8 additions & 1 deletion tests/codegen-llvm/unwind-abis/c-unwind-abi-panic-abort.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand All @@ -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();
}

Expand Down
9 changes: 8 additions & 1 deletion tests/codegen-llvm/unwind-and-panic-abort.rs
Original file line number Diff line number Diff line change
@@ -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"]

Expand All @@ -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();
Expand Down
57 changes: 40 additions & 17 deletions tests/codegen-llvm/wasm_exceptions.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -22,20 +23,25 @@ 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;
unsafe {
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 {
Expand All @@ -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 []
}
Loading