diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5ce4e606fd2..b1a76b80002 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -477,6 +477,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup); } + fn codegen_abort_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + ) { + let span = terminator.source_info.span; + self.set_debug_loc(&mut bx, terminator.source_info); + + // Get the location information. + let location = self.get_caller_location(&mut bx, terminator.source_info).immediate(); + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); + let llfn = bx.get_fn_addr(instance); + + // Codegen the actual panic invoke/call. + helper.do_call(self, &mut bx, fn_abi, llfn, &[location], None, None); + } + /// Returns `true` if this is indeed a panic intrinsic and codegen is done. fn codegen_panic_intrinsic( &mut self, @@ -1014,10 +1036,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::TerminatorKind::Resume => self.codegen_resume_terminator(helper, bx), mir::TerminatorKind::Abort => { - bx.abort(); - // `abort` does not terminate the block, so we still need to generate - // an `unreachable` terminator after it. - bx.unreachable(); + self.codegen_abort_terminator(helper, bx, terminator); } mir::TerminatorKind::Goto { target } => { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index def0c1d0687..be4849d0b84 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -283,6 +283,7 @@ language_item_table! { PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; + PanicNoUnwind, sym::panic_no_unwind, panic_no_unwind, Target::Fn, GenericRequirement::Exact(0); /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 7e7f6938706..7f13da5d38f 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -807,10 +807,18 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output.push(create_fn_mono_item(tcx, instance, source)); } } + mir::TerminatorKind::Abort { .. } => { + let instance = Instance::mono( + tcx, + tcx.require_lang_item(LangItem::PanicNoUnwind, Some(source)), + ); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(tcx, instance, source)); + } + } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Resume - | mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::GeneratorDrop diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 14ac286476c..1e9c8eab2a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -983,6 +983,7 @@ symbols! { panic_implementation, panic_info, panic_location, + panic_no_unwind, panic_runtime, panic_str, panic_unwind, diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index f260f19127b..5078eea07a1 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -77,6 +77,31 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { panic!("index out of bounds: the len is {} but the index is {}", len, index) } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function +fn panic_no_unwind() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call + // that gets resolved to the `#[panic_handler]` function. + extern "Rust" { + #[lang = "panic_impl"] + fn panic_impl(pi: &PanicInfo<'_>) -> !; + } + + // PanicInfo with the `can_unwind` flag set to false forces an abort. + let fmt = format_args!("panic in a function that cannot unwind"); + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false); + + // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. + unsafe { panic_impl(&pi) } +} + /// The entry point for panicking with a formatted message. /// /// This is designed to reduce the amount of code required at the call diff --git a/src/test/codegen/unwind-and-panic-abort.rs b/src/test/codegen/unwind-and-panic-abort.rs index 05d97f3256a..f238741e599 100644 --- a/src/test/codegen/unwind-and-panic-abort.rs +++ b/src/test/codegen/unwind-and-panic-abort.rs @@ -9,7 +9,7 @@ extern "C-unwind" { // CHECK: Function Attrs:{{.*}}nounwind // CHECK-NEXT: define{{.*}}void @foo -// CHECK: call void @llvm.trap() +// CHECK: call void @_ZN4core9panicking15panic_no_unwind #[no_mangle] pub unsafe extern "C" fn foo() { bar();