@@ -30,20 +30,21 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
3030use rustc_index:: vec:: { Idx , IndexVec } ;
3131use rustc_middle:: bug;
3232use 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 } ;
3534use 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 } ;
3839use rustc_span:: symbol:: Symbol ;
3940use 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 ;
4243use rustc_target:: abi:: { Align , Size } ;
4344use smallvec:: smallvec;
4445use tracing:: debug;
4546
46- use libc:: { c_longlong, c_uint} ;
47+ use libc:: { c_char , c_longlong, c_uint} ;
4748use std:: borrow:: Cow ;
4849use std:: fmt:: { self , Write } ;
4950use std:: hash:: { Hash , Hasher } ;
@@ -1468,6 +1469,84 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
14681469 . di_node
14691470}
14701471
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+
14711550/// Creates debug information for the given vtable, which is for the
14721551/// given type.
14731552///
@@ -1478,6 +1557,12 @@ pub fn create_vtable_di_node<'ll, 'tcx>(
14781557 poly_trait_ref : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
14791558 vtable : & ' ll Value ,
14801559) {
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+
14811566 if cx. dbg_cx . is_none ( ) {
14821567 return ;
14831568 }
0 commit comments