diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 570427f508554..31df982824ea1 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -78,7 +78,7 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? /// The precise instruction is guaranteed only on the following targets: /// - On x86 targets, this produces an `int3` instruction. /// - On aarch64 targets, this produces a `brk #0xf000` instruction. -// When adding more items above, also add cases to the test in `tests/assembly-llvm/breakpoint.rs`. +// When adding more items above, also add cases to the test in `tests/assembly-llvm/intrinsics.rs`. // When stabilizing this, update the comment on `core::intrinsics::breakpoint`. // NOTE: there is an alternative in `process::resumable_breakpoint`. We may or may not // actually want to stabilize both of them. diff --git a/library/core/src/process.rs b/library/core/src/process.rs index e92339f34d68e..d03e7cba04bef 100644 --- a/library/core/src/process.rs +++ b/library/core/src/process.rs @@ -22,7 +22,7 @@ /// /// `abort_immediate` lowers to a trap instruction on *most* architectures; on /// some architectures it simply lowers to call the unmangled `abort` function. -/// The exact behavior is architecture and system dependent. +/// The exact behavior is architecture- and system-dependent. /// /// On bare-metal (no OS) systems the trap instruction usually causes a /// *hardware* exception to be raised in a *synchronous* fashion; hardware @@ -34,11 +34,52 @@ /// corresponds to `SIGILL` or equivalent, *unless* this signal is handled. /// Other signals such as `SIGABRT`, `SIGTRAP`, `SIGSEGV`, and `SIGBUS` may be /// produced instead, depending on specifics. This is not an exhaustive list. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(abort_immediate)] +/// +/// use core::process; +/// +/// fn main() { +/// println!("aborting"); +/// +/// process::abort_immediate(); +/// +/// // execution never gets here +/// } +/// ``` +/// +/// The `abort_immediate` function terminates the process, so the destructor +/// will not get run on the example below: +/// +/// ```no_run +/// #![feature(abort_immediate)] +/// +/// use core::process; +/// +/// struct HasDrop; +/// +/// impl Drop for HasDrop { +/// fn drop(&mut self) { +/// println!("This will never be printed!"); +/// } +/// } +/// +/// fn main() { +/// let _x = HasDrop; +/// process::abort_immediate(); +/// // the destructor implemented for HasDrop will never get run +/// } +/// ``` +/// #[unstable(feature = "abort_immediate", issue = "154601")] #[rustc_const_unstable(feature = "abort_immediate", issue = "154601")] #[cold] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[doc(alias = "halt")] +#[doc(alias = "trap")] pub const fn abort_immediate() -> ! { crate::intrinsics::abort_immediate() } diff --git a/tests/assembly-llvm/breakpoint.rs b/tests/assembly-llvm/breakpoint.rs deleted file mode 100644 index 39ee26a4cf5d5..0000000000000 --- a/tests/assembly-llvm/breakpoint.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! Verify that the breakpoint operation emits the instructions we guarantee. - -//@ add-minicore -//@ assembly-output: emit-asm -//@ revisions: AARCH64 I686 X86_64 -//@ [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu -//@ [AARCH64] needs-llvm-components: aarch64 -//@ [I686] compile-flags: --target i686-unknown-linux-gnu -//@ [I686] needs-llvm-components: x86 -//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu -//@ [X86_64] needs-llvm-components: x86 - -#![crate_type = "lib"] -#![feature(no_core, lang_items, intrinsics, rustc_attrs)] -#![no_core] - -extern crate minicore; - -#[rustc_intrinsic] -#[rustc_nounwind] -fn breakpoint(); - -// CHECK-LABEL: call_breakpoint -// AARCH64: brk #0xf000 -// I686: int3 -// X86_64: int3 -#[unsafe(no_mangle)] -pub fn call_breakpoint() { - breakpoint(); -} diff --git a/tests/assembly-llvm/intrinsics.rs b/tests/assembly-llvm/intrinsics.rs new file mode 100644 index 0000000000000..9d89c04f51fb5 --- /dev/null +++ b/tests/assembly-llvm/intrinsics.rs @@ -0,0 +1,167 @@ +//! Verify that simple intrinsics emit the instructions we expect. + +//@ add-minicore +//@ assembly-output: emit-asm +//@ revisions: AARCH64 ARM64EC ARM32 AVR BPF LOONGARCH32 LOONGARCH64 I686 M68K MIPS32 MIPS64 MSP430 PPC32 PPC64 RISCV32 RISCV64 S390X SPARC32 SPARC64 WASM32 WASI32 X86_64 XTENSA +// +//@ [AARCH64] compile-flags: --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [ARM64EC] compile-flags: --target=arm64ec-pc-windows-msvc +//@ [ARM64EC] filecheck-flags: --check-prefixes AARCH64 +//@ [ARM64EC] needs-llvm-components: aarch64 +// +//@ [BPF] compile-flags: --target=bpfel-unknown-none +//@ [BPF] needs-llvm-components: bpf +// +//@ [AVR] compile-flags: --target=avr-none -Ctarget-cpu=atmega328p +//@ [AVR] needs-llvm-components: avr +// +//@ [ARM32] compile-flags: --target=armv7-unknown-linux-gnueabi +//@ [ARM32] needs-llvm-components: arm +// +//@ [LOONGARCH32] compile-flags: --target=loongarch32-unknown-none +//@ [LOONGARCH32] filecheck-flags: --check-prefixes LOONGARCH +//@ [LOONGARCH32] needs-llvm-components: loongarch +//@ [LOONGARCH64] compile-flags: --target=loongarch64-unknown-linux-gnu +//@ [LOONGARCH64] filecheck-flags: --check-prefixes LOONGARCH +//@ [LOONGARCH64] needs-llvm-components: loongarch +//@ [LOONGARCH32] min-llvm-version: 22 +//@ [LOONGARCH64] min-llvm-version: 22 +// +//@ [M68K] compile-flags: --target=m68k-unknown-linux-gnu +//@ [M68K] needs-llvm-components: m68k +// +//@ [MIPS32] compile-flags: --target=mips-unknown-linux-gnu +//@ [MIPS32] filecheck-flags: --check-prefixes MIPS +//@ [MIPS32] needs-llvm-components: mips +//@ [MIPS64] compile-flags: --target=mips64-unknown-linux-gnuabi64 +//@ [MIPS64] filecheck-flags: --check-prefixes MIPS +//@ [MIPS64] needs-llvm-components: mips +// +//@ [MSP430] compile-flags: --target=msp430-none-elf +//@ [MSP430] needs-llvm-components: msp430 +// +//@ [PPC32] compile-flags: --target=powerpc-unknown-linux-gnu +//@ [PPC32] filecheck-flags: --check-prefixes PPC +//@ [PPC32] needs-llvm-components: powerpc +//@ [PPC64] compile-flags: --target=powerpc64-unknown-linux-gnu +//@ [PPC64] filecheck-flags: --check-prefixes PPC +//@ [PPC64] needs-llvm-components: powerpc +// +//@ [RISCV32] compile-flags: --target=riscv32im-unknown-none-elf +//@ [RISCV32] filecheck-flags: --check-prefixes RISCV +//@ [RISCV32] needs-llvm-components: riscv +//@ [RISCV64] compile-flags: --target=riscv64gc-unknown-linux-gnu +//@ [RISCV64] filecheck-flags: --check-prefixes RISCV +//@ [RISCV64] needs-llvm-components: riscv +// +//@ [S390X] compile-flags: --target=s390x-unknown-linux-gnu +//@ [S390X] needs-llvm-components: systemz +// +//@ [SPARC32] compile-flags: --target=sparc-unknown-linux-gnu +//@ [SPARC32] filecheck-flags: --check-prefixes SPARC +//@ [SPARC32] needs-llvm-components: sparc +//@ [SPARC64] compile-flags: --target=sparc64-unknown-linux-gnu +//@ [SPARC64] filecheck-flags: --check-prefixes SPARC +//@ [SPARC64] needs-llvm-components: sparc +// +//@ [WASM32] compile-flags: --target=wasm32-unknown-unknown +//@ [WASM32] filecheck-flags: --check-prefixes WASM +//@ [WASM32] needs-llvm-components: webassembly +//@ [WASI32] compile-flags: --target=wasm32-wasip3 +//@ [WASI32] filecheck-flags: --check-prefixes WASM +//@ [WASI32] needs-llvm-components: webassembly +// +//@ [I686] compile-flags: --target=i686-unknown-linux-gnu +//@ [I686] filecheck-flags: --check-prefixes X86 +//@ [I686] needs-llvm-components: x86 +//@ [X86_64] compile-flags: --target=x86_64-unknown-linux-gnu +//@ [X86_64] filecheck-flags: --check-prefixes X86 +//@ [X86_64] needs-llvm-components: x86 +// +//@ [XTENSA] compile-flags: --target=xtensa-esp32-espidf +//@ [XTENSA] needs-llvm-components: xtensa +//@ [XTENSA] min-llvm-version: 23 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] + +extern crate minicore; + +mod intrinsics { + #[rustc_intrinsic] + #[rustc_nounwind] + pub fn abort_immediate() -> !; + + #[rustc_intrinsic] + #[rustc_nounwind] + pub fn breakpoint(); +} + +// Optional `"` since labels are quoted on arm64ec. Forbid calls by default since most platforms +// shouldn't be calling out to libc for this intrinsic. +// +// Note that some platforms (MSP430, AVR) do require an abort builtin, but this is typically a +// loop or hardware reset. +// +// We don't make a user-facing guarantee about what exactly `abort` will do but it also shouldn't +// change. +// +// CHECK-LABEL: do_abort{{"?}}: +// CHECK-NOT: {{ (call|b|bl) }} +// +// AARCH64: brk #0x1 +// AVR: call abort +// BPF: call __bpf_trap +// ARM32: .inst 0xe7ffdefe +// LOONGARCH: ud 0 +// M68K: jsr (abort@PLT,%pc) +// MIPS: break +// MSP430: call #abort +// PPC: trap +// RISCV: unimp +// // S390x lowers to a loop +// S390X: .Ltmp{{.*}}: +// S390X: j .Ltmp{{.*}} +// SPARC: ta 5 +// WASM: unreachable +// X86: ud2 +// XTENSA: ill.n +#[unsafe(no_mangle)] +pub fn do_abort() { + intrinsics::abort_immediate(); +} + +// Optional `"` since labels are quoted on arm64ec. Similar to `abort`, there typically shouldn't +// be any libcalls here. +// +// CHECK-LABEL: do_breakpoint{{"?}}: +// CHECK-NOT: {{ (call|b|bl|j) }} +// +// Documentation guarantees these instructions: +// +// AARCH64: brk #0xf000 +// X86: int3 +// +// We do not guarantee exact output on the following platforms but still check them: +// +// ARM32: bkpt #0 +// AVR: call abort +// BPF: call __bpf_trap +// LOONGARCH: break 0 +// M68K: jsr (abort@PLT,%pc) +// MIPS: break +// MSP430: call #abort +// PPC: trap +// RISCV: ebreak +// // S390x lowers to a loop +// S390X: .Ltmp{{.*}}: +// S390X: j .Ltmp{{.*}} +// SPARC: ta 1 +// WASM: unreachable +// XTENSA: break.n +#[unsafe(no_mangle)] +pub fn do_breakpoint() { + intrinsics::breakpoint(); +}