diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 780550fc4cc74..23263284d57a6 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1529,6 +1529,12 @@ fn codegen_regular_intrinsic_call<'tcx>( fx.bcx.set_cold_block(fx.bcx.current_block().unwrap()); } + sym::return_address => { + let val = fx.bcx.ins().get_return_address(fx.pointer_type); + let val = CValue::by_val(val, ret.layout()); + ret.write_cvalue(fx, val); + } + // Unimplemented intrinsics must have a fallback body. The fallback body is obtained // by converting the `InstanceKind::Intrinsic` to an `InstanceKind::Item`. _ => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 9742f9fb3e42e..7d58ac4d46dba 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -789,6 +789,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } + sym::return_address => { + match self.sess().target.arch { + // Expand this list as needed + | Arch::Wasm32 + | Arch::Wasm64 => { + let ty = self.type_ptr(); + self.const_null(ty) + } + _ => { + let ty = self.type_ix(32); + let val = self.const_int(ty, 0); + self.call_intrinsic("llvm.returnaddress", &[], &[val]) + } + } + } + _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index fd0c7c656ac21..209116bab01ce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -120,7 +120,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::contract_checks | sym::atomic_fence | sym::atomic_singlethreadfence - | sym::caller_location => {} + | sym::caller_location + | sym::return_address => {} _ => { span_bug!( span, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 58454cfc489c6..efcd8b354bd36 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -180,6 +180,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ptr_guaranteed_cmp | sym::ptr_mask | sym::ptr_metadata + | sym::return_address | sym::rotate_left | sym::rotate_right | sym::round_ties_even_f16 @@ -812,6 +813,8 @@ pub(crate) fn check_intrinsic_type( | sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)), sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit), + sym::return_address => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), + other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..e47ec956e8de7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1657,6 +1657,7 @@ symbols! { residual, result, result_ffi_guarantees, + return_address, return_position_impl_trait_in_trait, return_type_notation, riscv32, diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index e5078a45c6d9c..916e99b338640 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -76,3 +76,30 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? pub fn breakpoint() { core::intrinsics::breakpoint(); } + +/// The `core::arch::return_address!()` macro returns a pointer with an address that corresponds to the caller of the function that invoked the `return_address!()` macro. +/// The pointer has no provenance, as if created by `core::ptr::without_provenance`. It cannot be used to read memory (other than ZSTs). +/// +/// The value returned by the macro depends highly on the architecture and compiler (including any options set). +/// In particular, it is allowed to be wrong (particularly if inlining is involved), or even contain a nonsense value. +/// The result of this macro must not be relied upon for soundness or correctness, only for debugging purposes. +/// +/// As a best effort, if a useful value cannot be determined (for example, due to limitations on the current codegen), +/// this macro tries to return a null pointer instead of nonsense (this cannot be relied upon for correctness, however). +/// +/// Formally, this function returns a pointer with a non-deterministic address and no provenance. +/// +/// This is equivalent to the gcc `__builtin_return_address(0)` intrinsic (other forms of the intrinsic are not supported). +/// Because the operation can be always performed by the compiler without crashing or causing undefined behaviour, invoking the macro is a safe operation. +/// +/// ## Example +/// ``` +/// # #![cfg(not(miri))] // FIXME: Figure out how to make miri work before stabilizing this macro +/// #![feature(return_address)] +/// +/// let addr = core::arch::return_address!(); +/// println!("Caller is {addr:p}"); +/// ``` +#[unstable(feature = "return_address", issue = "154966")] +#[allow_internal_unstable(core_intrinsics)] +pub macro return_address() {{ core::intrinsics::return_address() }} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 94d0c7eab9227..69fdb93dcf93f 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3589,3 +3589,16 @@ pub const fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { pub const unsafe fn va_end(ap: &mut VaList<'_>) { /* deliberately does nothing */ } + +/// Returns the return address of the caller function (after inlining) in a best-effort manner or a null pointer if it is not supported on the current backend. +/// Returning an accurate value is a quality-of-implementation concern, but no hard guarantees are +/// made about the return value: formally, the intrinsic non-deterministically returns +/// an arbitrary pointer without provenance. +/// +/// Note that unlike most intrinsics, this is safe to call. This is because it only finds the return address of the immediate caller, which is guaranteed to be possible. +/// Other forms of the corresponding gcc or llvm intrinsic (which can have wildly unpredictable results or even crash at runtime) are not exposed. +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn return_address() -> *const () { + core::ptr::null() +} diff --git a/tests/codegen-llvm/intrinsics/return_address.rs b/tests/codegen-llvm/intrinsics/return_address.rs new file mode 100644 index 0000000000000..5aa731d6383f5 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/return_address.rs @@ -0,0 +1,12 @@ +//@ ignore-wasm + +#![crate_type = "lib"] +#![feature(core_intrinsics, return_address)] + +// CHECK-LABEL: @call_return_address_intrinsic +#[no_mangle] +#[inline(never)] +pub fn call_return_address_intrinsic() -> *const () { + // CHECK: call ptr @llvm.returnaddress(i32 0) + core::intrinsics::return_address() +}