@@ -18,6 +18,7 @@ use smallvec::{smallvec, SmallVec};
1818use tracing:: { debug, trace} ;
1919
2020use rustc_ast:: LitKind ;
21+ use rustc_attr:: InlineAttr ;
2122use rustc_data_structures:: fx:: FxHashMap ;
2223use rustc_data_structures:: sync:: { HashMapExt , Lock } ;
2324use rustc_errors:: ErrorGuaranteed ;
@@ -134,10 +135,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
134135 AllocDiscriminant :: Alloc . encode ( encoder) ;
135136 alloc. encode ( encoder) ;
136137 }
137- GlobalAlloc :: Function ( fn_instance ) => {
138- trace ! ( "encoding {:?} with {:#?}" , alloc_id, fn_instance ) ;
138+ GlobalAlloc :: Function { instance , unique } => {
139+ trace ! ( "encoding {:?} with {:#?}" , alloc_id, instance ) ;
139140 AllocDiscriminant :: Fn . encode ( encoder) ;
140- fn_instance. encode ( encoder) ;
141+ instance. encode ( encoder) ;
142+ unique. encode ( encoder) ;
141143 }
142144 GlobalAlloc :: VTable ( ty, poly_trait_ref) => {
143145 trace ! ( "encoding {:?} with {ty:#?}, {poly_trait_ref:#?}" , alloc_id) ;
@@ -285,7 +287,12 @@ impl<'s> AllocDecodingSession<'s> {
285287 trace ! ( "creating fn alloc ID" ) ;
286288 let instance = ty:: Instance :: decode ( decoder) ;
287289 trace ! ( "decoded fn alloc instance: {:?}" , instance) ;
288- let alloc_id = decoder. interner ( ) . reserve_and_set_fn_alloc ( instance) ;
290+ let unique = bool:: decode ( decoder) ;
291+ // Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which
292+ // is not possible in this context. That's why the allocation stores
293+ // whether it is unique or not.
294+ let alloc_id =
295+ decoder. interner ( ) . reserve_and_set_fn_alloc_internal ( instance, unique) ;
289296 alloc_id
290297 }
291298 AllocDiscriminant :: VTable => {
@@ -323,7 +330,12 @@ impl<'s> AllocDecodingSession<'s> {
323330#[ derive( Debug , Clone , Eq , PartialEq , Hash , TyDecodable , TyEncodable , HashStable ) ]
324331pub enum GlobalAlloc < ' tcx > {
325332 /// The alloc ID is used as a function pointer.
326- Function ( Instance < ' tcx > ) ,
333+ Function {
334+ instance : Instance < ' tcx > ,
335+ /// Stores whether this instance is unique, i.e. all pointers to this function use the same
336+ /// alloc ID.
337+ unique : bool ,
338+ } ,
327339 /// This alloc ID points to a symbolic (not-reified) vtable.
328340 VTable ( Ty < ' tcx > , Option < ty:: PolyExistentialTraitRef < ' tcx > > ) ,
329341 /// The alloc ID points to a "lazy" static variable that did not get computed (yet).
@@ -349,7 +361,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
349361 #[ inline]
350362 pub fn unwrap_fn ( & self ) -> Instance < ' tcx > {
351363 match * self {
352- GlobalAlloc :: Function ( instance) => instance,
364+ GlobalAlloc :: Function { instance, .. } => instance,
353365 _ => bug ! ( "expected function, got {:?}" , self ) ,
354366 }
355367 }
@@ -368,7 +380,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
368380 #[ inline]
369381 pub fn address_space ( & self , cx : & impl HasDataLayout ) -> AddressSpace {
370382 match self {
371- GlobalAlloc :: Function ( .. ) => cx. data_layout ( ) . instruction_address_space ,
383+ GlobalAlloc :: Function { .. } => cx. data_layout ( ) . instruction_address_space ,
372384 GlobalAlloc :: Static ( ..) | GlobalAlloc :: Memory ( ..) | GlobalAlloc :: VTable ( ..) => {
373385 AddressSpace :: DATA
374386 }
@@ -426,7 +438,7 @@ impl<'tcx> TyCtxt<'tcx> {
426438 fn reserve_and_set_dedup ( self , alloc : GlobalAlloc < ' tcx > ) -> AllocId {
427439 let mut alloc_map = self . alloc_map . lock ( ) ;
428440 match alloc {
429- GlobalAlloc :: Function ( .. ) | GlobalAlloc :: Static ( ..) | GlobalAlloc :: VTable ( ..) => { }
441+ GlobalAlloc :: Function { .. } | GlobalAlloc :: Static ( ..) | GlobalAlloc :: VTable ( ..) => { }
430442 GlobalAlloc :: Memory ( ..) => bug ! ( "Trying to dedup-reserve memory with real data!" ) ,
431443 }
432444 if let Some ( & alloc_id) = alloc_map. dedup . get ( & alloc) {
@@ -445,30 +457,45 @@ impl<'tcx> TyCtxt<'tcx> {
445457 self . reserve_and_set_dedup ( GlobalAlloc :: Static ( static_id) )
446458 }
447459
460+ /// Generates an `AllocId` for a function. The caller must already have decided whether this
461+ /// function obtains a unique AllocId or gets de-duplicated via the cache.
462+ fn reserve_and_set_fn_alloc_internal ( self , instance : Instance < ' tcx > , unique : bool ) -> AllocId {
463+ let alloc = GlobalAlloc :: Function { instance, unique } ;
464+ if unique {
465+ // Deduplicate.
466+ self . reserve_and_set_dedup ( alloc)
467+ } else {
468+ // Get a fresh ID.
469+ let mut alloc_map = self . alloc_map . lock ( ) ;
470+ let id = alloc_map. reserve ( ) ;
471+ alloc_map. alloc_map . insert ( id, alloc) ;
472+ id
473+ }
474+ }
475+
448476 /// Generates an `AllocId` for a function. Depending on the function type,
449477 /// this might get deduplicated or assigned a new ID each time.
450478 pub fn reserve_and_set_fn_alloc ( self , instance : Instance < ' tcx > ) -> AllocId {
451479 // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
452480 // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
453- // duplicated across crates.
454- // We thus generate a new `AllocId` for every mention of a function. This means that
455- // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
456- // However, formatting code relies on function identity (see #58320), so we only do
457- // this for generic functions. Lifetime parameters are ignored.
481+ // duplicated across crates. We thus generate a new `AllocId` for every mention of a
482+ // function. This means that `main as fn() == main as fn()` is false, while `let x = main as
483+ // fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify
484+ // certain functions uniquely, e.g. for backtraces. So we identify whether codegen will
485+ // actually emit duplicate functions. It does that when they have non-lifetime generics, or
486+ // when they can be inlined. All other functions are given a unique address.
487+ // This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied
488+ // upon for anything. But if we don't do this, backtraces look terrible.
458489 let is_generic = instance
459490 . args
460491 . into_iter ( )
461492 . any ( |kind| !matches ! ( kind. unpack( ) , GenericArgKind :: Lifetime ( _) ) ) ;
462- if is_generic {
463- // Get a fresh ID.
464- let mut alloc_map = self . alloc_map . lock ( ) ;
465- let id = alloc_map. reserve ( ) ;
466- alloc_map. alloc_map . insert ( id, GlobalAlloc :: Function ( instance) ) ;
467- id
468- } else {
469- // Deduplicate.
470- self . reserve_and_set_dedup ( GlobalAlloc :: Function ( instance) )
471- }
493+ let can_be_inlined = match self . codegen_fn_attrs ( instance. def_id ( ) ) . inline {
494+ InlineAttr :: Never => false ,
495+ _ => true ,
496+ } ;
497+ let unique = !is_generic && !can_be_inlined;
498+ self . reserve_and_set_fn_alloc_internal ( instance, unique)
472499 }
473500
474501 /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
0 commit comments