@@ -18,6 +18,7 @@ use smallvec::{smallvec, SmallVec};
18
18
use tracing:: { debug, trace} ;
19
19
20
20
use rustc_ast:: LitKind ;
21
+ use rustc_attr:: InlineAttr ;
21
22
use rustc_data_structures:: fx:: FxHashMap ;
22
23
use rustc_data_structures:: sync:: { HashMapExt , Lock } ;
23
24
use rustc_errors:: ErrorGuaranteed ;
@@ -134,10 +135,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
134
135
AllocDiscriminant :: Alloc . encode ( encoder) ;
135
136
alloc. encode ( encoder) ;
136
137
}
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 ) ;
139
140
AllocDiscriminant :: Fn . encode ( encoder) ;
140
- fn_instance. encode ( encoder) ;
141
+ instance. encode ( encoder) ;
142
+ unique. encode ( encoder) ;
141
143
}
142
144
GlobalAlloc :: VTable ( ty, poly_trait_ref) => {
143
145
trace ! ( "encoding {:?} with {ty:#?}, {poly_trait_ref:#?}" , alloc_id) ;
@@ -285,7 +287,12 @@ impl<'s> AllocDecodingSession<'s> {
285
287
trace ! ( "creating fn alloc ID" ) ;
286
288
let instance = ty:: Instance :: decode ( decoder) ;
287
289
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) ;
289
296
alloc_id
290
297
}
291
298
AllocDiscriminant :: VTable => {
@@ -323,7 +330,12 @@ impl<'s> AllocDecodingSession<'s> {
323
330
#[ derive( Debug , Clone , Eq , PartialEq , Hash , TyDecodable , TyEncodable , HashStable ) ]
324
331
pub enum GlobalAlloc < ' tcx > {
325
332
/// 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
+ } ,
327
339
/// This alloc ID points to a symbolic (not-reified) vtable.
328
340
VTable ( Ty < ' tcx > , Option < ty:: PolyExistentialTraitRef < ' tcx > > ) ,
329
341
/// The alloc ID points to a "lazy" static variable that did not get computed (yet).
@@ -349,7 +361,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
349
361
#[ inline]
350
362
pub fn unwrap_fn ( & self ) -> Instance < ' tcx > {
351
363
match * self {
352
- GlobalAlloc :: Function ( instance) => instance,
364
+ GlobalAlloc :: Function { instance, .. } => instance,
353
365
_ => bug ! ( "expected function, got {:?}" , self ) ,
354
366
}
355
367
}
@@ -368,7 +380,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
368
380
#[ inline]
369
381
pub fn address_space ( & self , cx : & impl HasDataLayout ) -> AddressSpace {
370
382
match self {
371
- GlobalAlloc :: Function ( .. ) => cx. data_layout ( ) . instruction_address_space ,
383
+ GlobalAlloc :: Function { .. } => cx. data_layout ( ) . instruction_address_space ,
372
384
GlobalAlloc :: Static ( ..) | GlobalAlloc :: Memory ( ..) | GlobalAlloc :: VTable ( ..) => {
373
385
AddressSpace :: DATA
374
386
}
@@ -426,7 +438,7 @@ impl<'tcx> TyCtxt<'tcx> {
426
438
fn reserve_and_set_dedup ( self , alloc : GlobalAlloc < ' tcx > ) -> AllocId {
427
439
let mut alloc_map = self . alloc_map . lock ( ) ;
428
440
match alloc {
429
- GlobalAlloc :: Function ( .. ) | GlobalAlloc :: Static ( ..) | GlobalAlloc :: VTable ( ..) => { }
441
+ GlobalAlloc :: Function { .. } | GlobalAlloc :: Static ( ..) | GlobalAlloc :: VTable ( ..) => { }
430
442
GlobalAlloc :: Memory ( ..) => bug ! ( "Trying to dedup-reserve memory with real data!" ) ,
431
443
}
432
444
if let Some ( & alloc_id) = alloc_map. dedup . get ( & alloc) {
@@ -445,30 +457,45 @@ impl<'tcx> TyCtxt<'tcx> {
445
457
self . reserve_and_set_dedup ( GlobalAlloc :: Static ( static_id) )
446
458
}
447
459
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
+
448
476
/// Generates an `AllocId` for a function. Depending on the function type,
449
477
/// this might get deduplicated or assigned a new ID each time.
450
478
pub fn reserve_and_set_fn_alloc ( self , instance : Instance < ' tcx > ) -> AllocId {
451
479
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
452
480
// 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.
458
489
let is_generic = instance
459
490
. args
460
491
. into_iter ( )
461
492
. 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)
472
499
}
473
500
474
501
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
0 commit comments