do not make function addresses unique with cross_crate_inline_threshold=always (even if that breaks backtraces)

This commit is contained in:
Ralf Jung 2024-08-06 22:45:42 +02:00
parent 5cab8ae4a4
commit dec5b463fb

View File

@ -24,6 +24,7 @@
Instance, Ty, TyCtxt, Instance, Ty, TyCtxt,
}, },
}; };
use rustc_session::config::InliningThreshold;
use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::{Span, SpanData, Symbol}; use rustc_span::{Span, SpanData, Symbol};
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
@ -1525,24 +1526,29 @@ fn get_global_alloc_salt(
instance: Option<ty::Instance<'tcx>>, instance: Option<ty::Instance<'tcx>>,
) -> usize { ) -> usize {
let unique = if let Some(instance) = instance { let unique = if let Some(instance) = instance {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated // Functions cannot be identified by pointers, as asm-equal functions can get
// by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be // deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
// duplicated across crates. We thus generate a new `AllocId` for every mention of a // functions can be duplicated across crates. We thus generate a new `AllocId` for every
// function. This means that `main as fn() == main as fn()` is false, while `let x = main as // mention of a function. This means that `main as fn() == main as fn()` is false, while
// fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify // `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
// certain functions uniquely, e.g. for backtraces. So we identify whether codegen will // be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
// actually emit duplicate functions. It does that when they have non-lifetime generics, or // whether codegen will actually emit duplicate functions. It does that when they have
// when they can be inlined. All other functions are given a unique address. // non-lifetime generics, or when they can be inlined. All other functions are given a
// This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied // unique address. This is not a stable guarantee! The `inline` attribute is a hint and
// upon for anything. But if we don't do this, backtraces look terrible. // cannot be relied upon for anything. But if we don't do this, the
// `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
// backtraces look terrible.
let is_generic = instance let is_generic = instance
.args .args
.into_iter() .into_iter()
.any(|kind| !matches!(kind.unpack(), ty::GenericArgKind::Lifetime(_))); .any(|kind| !matches!(kind.unpack(), ty::GenericArgKind::Lifetime(_)));
let can_be_inlined = match ecx.tcx.codegen_fn_attrs(instance.def_id()).inline { let can_be_inlined = matches!(
InlineAttr::Never => false, ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
_ => true, InliningThreshold::Always
}; ) || !matches!(
ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
InlineAttr::Never
);
!is_generic && !can_be_inlined !is_generic && !can_be_inlined
} else { } else {
// Non-functions are never unique. // Non-functions are never unique.