@@ -30,20 +30,21 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
30
30
use rustc_index:: vec:: { Idx , IndexVec } ;
31
31
use rustc_middle:: bug;
32
32
use rustc_middle:: mir:: { self , GeneratorLayout } ;
33
- use rustc_middle:: ty:: layout:: LayoutOf ;
34
- use rustc_middle:: ty:: layout:: TyAndLayout ;
33
+ use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
35
34
use rustc_middle:: ty:: subst:: GenericArgKind ;
36
- use rustc_middle:: ty:: { self , AdtKind , Instance , ParamEnv , Ty , TyCtxt } ;
37
- use rustc_session:: config:: { self , DebugInfo } ;
35
+ use rustc_middle:: ty:: {
36
+ self , AdtKind , Instance , ParamEnv , PolyExistentialTraitRef , Ty , TyCtxt , Visibility ,
37
+ } ;
38
+ use rustc_session:: config:: { self , DebugInfo , Lto } ;
38
39
use rustc_span:: symbol:: Symbol ;
39
40
use rustc_span:: FileName ;
40
- use rustc_span:: FileNameDisplayPreference ;
41
- use rustc_span :: { self , SourceFile } ;
41
+ use rustc_span:: { self , FileNameDisplayPreference , SourceFile } ;
42
+ use rustc_symbol_mangling :: typeid_for_trait_ref ;
42
43
use rustc_target:: abi:: { Align , Size } ;
43
44
use smallvec:: smallvec;
44
45
use tracing:: debug;
45
46
46
- use libc:: { c_longlong, c_uint} ;
47
+ use libc:: { c_char , c_longlong, c_uint} ;
47
48
use std:: borrow:: Cow ;
48
49
use std:: fmt:: { self , Write } ;
49
50
use std:: hash:: { Hash , Hasher } ;
@@ -1468,6 +1469,84 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
1468
1469
. di_node
1469
1470
}
1470
1471
1472
+ fn vcall_visibility_metadata < ' ll , ' tcx > (
1473
+ cx : & CodegenCx < ' ll , ' tcx > ,
1474
+ ty : Ty < ' tcx > ,
1475
+ trait_ref : Option < PolyExistentialTraitRef < ' tcx > > ,
1476
+ vtable : & ' ll Value ,
1477
+ ) {
1478
+ enum VCallVisibility {
1479
+ Public = 0 ,
1480
+ LinkageUnit = 1 ,
1481
+ TranslationUnit = 2 ,
1482
+ }
1483
+
1484
+ let Some ( trait_ref) = trait_ref else { return } ;
1485
+
1486
+ let trait_ref_self = trait_ref. with_self_ty ( cx. tcx , ty) ;
1487
+ let trait_ref_self = cx. tcx . erase_regions ( trait_ref_self) ;
1488
+ let trait_def_id = trait_ref_self. def_id ( ) ;
1489
+ let trait_vis = cx. tcx . visibility ( trait_def_id) ;
1490
+
1491
+ let cgus = cx. sess ( ) . codegen_units ( ) ;
1492
+ let single_cgu = cgus == 1 ;
1493
+
1494
+ let lto = cx. sess ( ) . lto ( ) ;
1495
+
1496
+ // Since LLVM requires full LTO for the virtual function elimination optimization to apply,
1497
+ // only the `Lto::Fat` cases are relevant currently.
1498
+ let vcall_visibility = match ( lto, trait_vis, single_cgu) {
1499
+ // If there is not LTO and the visibility in public, we have to assume that the vtable can
1500
+ // be seen from anywhere. With multiple CGUs, the vtable is quasi-public.
1501
+ ( Lto :: No | Lto :: ThinLocal , Visibility :: Public , _)
1502
+ | ( Lto :: No , Visibility :: Restricted ( _) | Visibility :: Invisible , false ) => {
1503
+ VCallVisibility :: Public
1504
+ }
1505
+ // With LTO and a quasi-public visibility, the usages of the functions of the vtable are
1506
+ // all known by the `LinkageUnit`.
1507
+ // FIXME: LLVM only supports this optimization for `Lto::Fat` currently. Once it also
1508
+ // supports `Lto::Thin` the `VCallVisibility` may have to be adjusted for those.
1509
+ ( Lto :: Fat | Lto :: Thin , Visibility :: Public , _)
1510
+ | (
1511
+ Lto :: ThinLocal | Lto :: Thin | Lto :: Fat ,
1512
+ Visibility :: Restricted ( _) | Visibility :: Invisible ,
1513
+ false ,
1514
+ ) => VCallVisibility :: LinkageUnit ,
1515
+ // If there is only one CGU, private vtables can only be seen by that CGU/translation unit
1516
+ // and therefore we know of all usages of functions in the vtable.
1517
+ ( _, Visibility :: Restricted ( _) | Visibility :: Invisible , true ) => {
1518
+ VCallVisibility :: TranslationUnit
1519
+ }
1520
+ } ;
1521
+
1522
+ let trait_ref_typeid = typeid_for_trait_ref ( cx. tcx , trait_ref) ;
1523
+
1524
+ unsafe {
1525
+ let typeid = llvm:: LLVMMDStringInContext (
1526
+ cx. llcx ,
1527
+ trait_ref_typeid. as_ptr ( ) as * const c_char ,
1528
+ trait_ref_typeid. as_bytes ( ) . len ( ) as c_uint ,
1529
+ ) ;
1530
+ let v = [ cx. const_usize ( 0 ) , typeid] ;
1531
+ llvm:: LLVMRustGlobalAddMetadata (
1532
+ vtable,
1533
+ llvm:: MD_type as c_uint ,
1534
+ llvm:: LLVMValueAsMetadata ( llvm:: LLVMMDNodeInContext (
1535
+ cx. llcx ,
1536
+ v. as_ptr ( ) ,
1537
+ v. len ( ) as c_uint ,
1538
+ ) ) ,
1539
+ ) ;
1540
+ let vcall_visibility = llvm:: LLVMValueAsMetadata ( cx. const_u64 ( vcall_visibility as u64 ) ) ;
1541
+ let vcall_visibility_metadata = llvm:: LLVMMDNodeInContext2 ( cx. llcx , & vcall_visibility, 1 ) ;
1542
+ llvm:: LLVMGlobalSetMetadata (
1543
+ vtable,
1544
+ llvm:: MetadataType :: MD_vcall_visibility as c_uint ,
1545
+ vcall_visibility_metadata,
1546
+ ) ;
1547
+ }
1548
+ }
1549
+
1471
1550
/// Creates debug information for the given vtable, which is for the
1472
1551
/// given type.
1473
1552
///
@@ -1478,6 +1557,12 @@ pub fn create_vtable_di_node<'ll, 'tcx>(
1478
1557
poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
1479
1558
vtable : & ' ll Value ,
1480
1559
) {
1560
+ // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
1561
+ // LLVM at the moment.
1562
+ if cx. sess ( ) . opts . debugging_opts . virtual_function_elimination && cx. sess ( ) . lto ( ) == Lto :: Fat {
1563
+ vcall_visibility_metadata ( cx, ty, poly_trait_ref, vtable) ;
1564
+ }
1565
+
1481
1566
if cx. dbg_cx . is_none ( ) {
1482
1567
return ;
1483
1568
}
0 commit comments