Skip to content

Commit

Permalink
Miri function identity hack: account for possible inlining
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Apr 23, 2024
1 parent aca749e commit 02a1532
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 7 deletions.
21 changes: 15 additions & 6 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ use std::num::NonZero;
use std::sync::atomic::{AtomicU32, Ordering};

use rustc_ast::LitKind;
use rustc_attr::InlineAttr;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{HashMapExt, Lock};
use rustc_data_structures::tiny_list::TinyList;
Expand Down Expand Up @@ -555,16 +556,24 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
// by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
// duplicated across crates.
// We thus generate a new `AllocId` for every mention of a function. This means that
// `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
// However, formatting code relies on function identity (see #58320), so we only do
// this for generic functions. Lifetime parameters are ignored.
// duplicated across crates. We thus generate a new `AllocId` for every mention of a
// function. This means that `main as fn() == main as fn()` is false, while `let x = main as
// fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify
// certain functions uniquely, e.g. for backtraces. So we identify whether codegen will
// actually emit duplicate functions. It does that when they have non-lifetime generics, or
// when they can be inlined. All other functions are given a unique address.
// This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied
// upon for anything. But if we don't do this, backtraces look terrible and we risk breaking
// the `USIZE_MARKER` hack in the formatting machinery.
let is_generic = instance
.args
.into_iter()
.any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_)));
if is_generic {
let can_be_inlined = match self.codegen_fn_attrs(instance.def_id()).inline {
InlineAttr::Never => false,
_ => true,
};
if is_generic || can_be_inlined {
// Get a fresh ID.
let mut alloc_map = self.alloc_map.lock();
let id = alloc_map.reserve();
Expand Down
3 changes: 2 additions & 1 deletion src/tools/miri/tests/pass/function_pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fn h(i: i32, j: i32) -> i32 {
j * i * 7
}

#[inline(never)]
fn i() -> i32 {
73
}
Expand Down Expand Up @@ -77,7 +78,7 @@ fn main() {
assert_eq!(indirect_mut3(h), 210);
assert_eq!(indirect_once3(h), 210);
// Check that `i` always has the same address. This is not guaranteed
// but Miri currently uses a fixed address for monomorphic functions.
// but Miri currently uses a fixed address for non-inlineable monomorphic functions.
assert!(return_fn_ptr(i) == i);
assert!(return_fn_ptr(i) as unsafe fn() -> i32 == i as fn() -> i32 as unsafe fn() -> i32);
// Miri gives different addresses to different reifications of a generic function.
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/tests/pass/issues/issue-91636.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ impl Function {
}
}

#[inline(never)]
fn dummy(_: &str) {}

fn main() {
Expand Down

0 comments on commit 02a1532

Please sign in to comment.