Skip to content

Commit d6da4ea

Browse files
committed
Implement core::arch::return_address and tests
Fix typo Apply suggestions from code review Wording/docs changes. Co-authored-by: Ralf Jung <post@ralfj.de> Change signature according to Ralf's comment Fix call to `core::intrinsics::return_address()` according to the new signature Add cranelift implementation for intrinsic Change wording on `return_address!()` to be clear that returning a null pointer is best-effort. Fix formatting of doc comment Fix mistake in cranelift codegen
1 parent b2f1ccf commit d6da4ea

8 files changed

Lines changed: 67 additions & 1 deletion

File tree

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
15291529
fx.bcx.set_cold_block(fx.bcx.current_block().unwrap());
15301530
}
15311531

1532+
sym::return_address => {
1533+
let val = fx.bcx.ins().get_return_address(fx.pointer_type);
1534+
let val = CValue::by_val(val, ret.layout());
1535+
ret.write_cvalue(fx, val);
1536+
}
1537+
15321538
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained
15331539
// by converting the `InstanceKind::Intrinsic` to an `InstanceKind::Item`.
15341540
_ => {

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
789789
}
790790
}
791791

792+
sym::return_address => {
793+
let ty = self.type_ix(32);
794+
let val = self.const_int(ty, 0);
795+
self.call_intrinsic("llvm.returnaddress", &[], &[val])
796+
}
797+
792798
_ => {
793799
debug!("unknown intrinsic '{}' -- falling back to default body", name);
794800
// Call the fallback body instead of generating the intrinsic code

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
120120
| sym::contract_checks
121121
| sym::atomic_fence
122122
| sym::atomic_singlethreadfence
123-
| sym::caller_location => {}
123+
| sym::caller_location
124+
| sym::return_address => {}
124125
_ => {
125126
span_bug!(
126127
span,

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
180180
| sym::ptr_guaranteed_cmp
181181
| sym::ptr_mask
182182
| sym::ptr_metadata
183+
| sym::return_address
183184
| sym::rotate_left
184185
| sym::rotate_right
185186
| sym::round_ties_even_f16
@@ -812,6 +813,8 @@ pub(crate) fn check_intrinsic_type(
812813
| sym::atomic_xor => (2, 1, vec![Ty::new_mut_ptr(tcx, param(0)), param(1)], param(0)),
813814
sym::atomic_fence | sym::atomic_singlethreadfence => (0, 1, Vec::new(), tcx.types.unit),
814815

816+
sym::return_address => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)),
817+
815818
other => {
816819
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
817820
return;

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,7 @@ symbols! {
16571657
residual,
16581658
result,
16591659
result_ffi_guarantees,
1660+
return_address,
16601661
return_position_impl_trait_in_trait,
16611662
return_type_notation,
16621663
riscv32,

library/core/src/arch.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,29 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?
7676
pub fn breakpoint() {
7777
core::intrinsics::breakpoint();
7878
}
79+
80+
/// 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.
81+
/// The pointer has no provenance, as if created by `core::ptr::without_provenance`. It cannot be used to read memory (other than ZSTs).
82+
///
83+
/// The value returned by the macro depends highly on the architecture and compiler (including any options set).
84+
/// In particular, it is allowed to be wrong (particularly if inlining is involved), or even contain a nonsense value.
85+
/// The result of this macro must not be relied upon for soundness or correctness, only for debugging purposes.
86+
///
87+
/// As a best effort, if a useful value cannot be determined (for example, due to limitations on the current codegen),
88+
/// this macro tries to return a null pointer instead of nonsense (this cannot be relied upon for correctness, however).
89+
///
90+
/// Formally, this function returns a pointer with a non-deterministic address and no provenance.
91+
///
92+
/// This is equivalent to the gcc `__builtin_return_address(0)` intrinsic (other forms of the intrinsic are not supported).
93+
/// Because the operation can be always performed by the compiler without crashing or causing undefined behaviour, invoking the macro is a safe operation.
94+
///
95+
/// ## Example
96+
/// ```
97+
/// #![feature(return_address)]
98+
///
99+
/// let addr = core::arch::return_address!();
100+
/// println!("Caller is {addr:p}");
101+
/// ```
102+
#[unstable(feature = "return_address", issue = "154966")]
103+
#[allow_internal_unstable(core_intrinsics)]
104+
pub macro return_address() {{ core::intrinsics::return_address() }}

library/core/src/intrinsics/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3589,3 +3589,16 @@ pub const fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> {
35893589
pub const unsafe fn va_end(ap: &mut VaList<'_>) {
35903590
/* deliberately does nothing */
35913591
}
3592+
3593+
/// 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.
3594+
/// Returning an accurate value is a quality-of-implementation concern, but no hard guarantees are
3595+
/// made about the return value: formally, the intrinsic non-deterministically returns
3596+
/// an arbitrary pointer without provenance.
3597+
///
3598+
/// 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.
3599+
/// Other forms of the corresponding gcc or llvm intrinsic (which can have wildly unpredictable results or even crash at runtime) are not exposed.
3600+
#[rustc_intrinsic]
3601+
#[rustc_nounwind]
3602+
pub fn return_address() -> *const () {
3603+
core::ptr::null()
3604+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![crate_type = "lib"]
2+
#![feature(core_intrinsics, return_address)]
3+
4+
// CHECK-LABEL: @call_return_address_intrinsic
5+
#[no_mangle]
6+
#[inline(never)]
7+
pub fn call_return_address_intrinsic() -> *const () {
8+
// CHECK: call ptr @llvm.returnaddress(i32 0)
9+
core::intrinsics::return_address()
10+
}

0 commit comments

Comments
 (0)