Skip to content
Open
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
6 changes: 6 additions & 0 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
_ => {
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}
}
Comment thread
chorman0773 marked this conversation as resolved.

_ => {
debug!("unknown intrinsic '{}' -- falling back to default body", name);
// Call the fallback body instead of generating the intrinsic code
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
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 @@ -1657,6 +1657,7 @@ symbols! {
residual,
result,
result_ffi_guarantees,
return_address,
return_position_impl_trait_in_trait,
return_type_notation,
riscv32,
Expand Down
27 changes: 27 additions & 0 deletions library/core/src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Member

@RalfJung RalfJung Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Miri should probably just pick an arbitrary 64bit integer and return that? Or is that too adversarial and we should return null?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably should (though I see it doing either). Right now, I'd like something landed though. My plan is to figure out miri (and codegen_gcc) before stabilization.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I suppose we could briefly discuss whether it is "fine" to return just null tomorrow)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's more of a Miri thing than an opsem thing so I'd prefer to discuss that in the PR which implements it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. In any case, the main thing a random implementation would detect is someone doing a null test, then some form of stupid shenanigans like transmuting to a function pointer and calling it (or .read()ing it). Raw .read() or transmute/call would be detected since null pointer would trip anyways.

/// #![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() }}
13 changes: 13 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
12 changes: 12 additions & 0 deletions tests/codegen-llvm/intrinsics/return_address.rs
Original file line number Diff line number Diff line change
@@ -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()
}
Loading