Rollup merge of #154972 - chorman0773:return_address, r=Mark-Simulacrum

Implement `core::arch::return_address` and tests

Tracking issue: rust-lang/rust#154966

Implements libs-team#768
This commit is contained in:
Jonathan Brouwer
2026-05-10 19:05:42 +02:00
committed by GitHub
8 changed files with 83 additions and 1 deletions
@@ -1523,6 +1523,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`.
_ => {
@@ -849,6 +849,22 @@ fn codegen_intrinsic_call(
}
}
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
@@ -121,7 +121,8 @@ pub fn codegen_intrinsic_call(
| sym::contract_checks
| sym::atomic_fence
| sym::atomic_singlethreadfence
| sym::caller_location => {}
| sym::caller_location
| sym::return_address => {}
_ => {
span_bug!(
span,
@@ -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
@@ -793,6 +794,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;
+1
View File
@@ -1663,6 +1663,7 @@
residual,
result,
result_ffi_guarantees,
return_address,
return_position_impl_trait_in_trait,
return_type_notation,
riscv32,
+30
View File
@@ -76,3 +76,33 @@
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
/// ```
/// #![feature(return_address)]
///
/// # fn run_test() {
/// let addr = core::arch::return_address!();
/// println!("Caller is {addr:p}");
/// # }
/// # #[cfg(not(miri))] // FIXME: Figure out how to make miri work before stabilizing this macro
/// # run_test()
/// ```
#[unstable(feature = "return_address", issue = "154966")]
#[allow_internal_unstable(core_intrinsics)]
pub macro return_address() {{ core::intrinsics::return_address() }}
+13
View File
@@ -3598,3 +3598,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()
}
@@ -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()
}