@@ -463,15 +463,18 @@ fn thin_lto(
463463 // If previous imports have been deleted, or we get an IO error
464464 // reading the file storing them, then we'll just use `None` as the
465465 // prev_import_map, which will force the code to be recompiled.
466- let prev =
467- if path. exists ( ) { ThinLTOImports :: load_from_file ( & path) . ok ( ) } else { None } ;
468- let curr = ThinLTOImports :: from_thin_lto_data ( data) ;
466+ let prev = if path. exists ( ) {
467+ ThinLTOImportMaps :: load_from_file ( & path) . ok ( )
468+ } else {
469+ None
470+ } ;
471+ let curr = ThinLTOImportMaps :: from_thin_lto_data ( data) ;
469472 ( Some ( path) , prev, curr)
470473 } else {
471474 // If we don't compile incrementally, we don't need to load the
472475 // import data from LLVM.
473476 assert ! ( green_modules. is_empty( ) ) ;
474- let curr = ThinLTOImports :: default ( ) ;
477+ let curr = ThinLTOImportMaps :: default ( ) ;
475478 ( None , None , curr)
476479 } ;
477480 info ! ( "thin LTO import map loaded" ) ;
@@ -497,10 +500,14 @@ fn thin_lto(
497500 let module_name = module_name_to_str ( module_name) ;
498501
499502 // If (1.) the module hasn't changed, and (2.) none of the modules
500- // it imports from has changed, *and* (3.) the import-set itself has
501- // not changed from the previous compile when it was last
502- // ThinLTO'ed, then we can re-use the post-ThinLTO version of the
503- // module. Otherwise, freshly perform LTO optimization.
503+ // it imports from nor exports to have changed, *and* (3.) the
504+ // import and export sets themselves have not changed from the
505+ // previous compile when it was last ThinLTO'ed, then we can re-use
506+ // the post-ThinLTO version of the module. Otherwise, freshly
507+ // perform LTO optimization.
508+ //
509+ // (Note that globally, the export set is just the inverse of the
510+ // import set.)
504511 //
505512 // This strategy means we can always save the computed imports as
506513 // canon: when we reuse the post-ThinLTO version, condition (3.)
@@ -509,19 +516,30 @@ fn thin_lto(
509516 // version, the current import set *is* the correct one, since we
510517 // are doing the ThinLTO in this current compilation cycle.)
511518 //
512- // See rust-lang/rust#59535.
519+ // For more discussion, see rust-lang/rust#59535 (where the import
520+ // issue was discovered) and rust-lang/rust#69798 (where the
521+ // analogous export issue was discovered).
513522 if let ( Some ( prev_import_map) , true ) =
514523 ( prev_import_map. as_ref ( ) , green_modules. contains_key ( module_name) )
515524 {
516525 assert ! ( cgcx. incr_comp_session_dir. is_some( ) ) ;
517526
518- let prev_imports = prev_import_map. modules_imported_by ( module_name) ;
519- let curr_imports = curr_import_map. modules_imported_by ( module_name) ;
527+ let prev_imports = prev_import_map. imports_of ( module_name) ;
528+ let curr_imports = curr_import_map. imports_of ( module_name) ;
529+ let prev_exports = prev_import_map. exports_of ( module_name) ;
530+ let curr_exports = curr_import_map. exports_of ( module_name) ;
520531 let imports_all_green = curr_imports
521532 . iter ( )
522533 . all ( |imported_module| green_modules. contains_key ( imported_module) ) ;
534+ let exports_all_green = curr_exports
535+ . iter ( )
536+ . all ( |exported_module| green_modules. contains_key ( exported_module) ) ;
523537
524- if imports_all_green && equivalent_as_sets ( prev_imports, curr_imports) {
538+ if imports_all_green
539+ && equivalent_as_sets ( prev_imports, curr_imports)
540+ && exports_all_green
541+ && equivalent_as_sets ( prev_exports, curr_exports)
542+ {
525543 let work_product = green_modules[ module_name] . clone ( ) ;
526544 copy_jobs. push ( work_product) ;
527545 info ! ( " - {}: re-used" , module_name) ;
@@ -881,17 +899,32 @@ pub unsafe fn optimize_thin_module(
881899 Ok ( module)
882900}
883901
902+ /// Summarizes module import/export relationships used by LLVM's ThinLTO pass.
903+ ///
904+ /// Note that we tend to have two such instances of `ThinLTOImportMaps` in use:
905+ /// one loaded from a file that represents the relationships used during the
906+ /// compilation associated with the incremetnal build artifacts we are
907+ /// attempting to reuse, and another constructed via `from_thin_lto_data`, which
908+ /// captures the relationships of ThinLTO in the current compilation.
884909#[ derive( Debug , Default ) ]
885- pub struct ThinLTOImports {
910+ pub struct ThinLTOImportMaps {
886911 // key = llvm name of importing module, value = list of modules it imports from
887912 imports : FxHashMap < String , Vec < String > > ,
913+ // key = llvm name of exporting module, value = list of modules it exports to
914+ exports : FxHashMap < String , Vec < String > > ,
888915}
889916
890- impl ThinLTOImports {
891- fn modules_imported_by ( & self , llvm_module_name : & str ) -> & [ String ] {
917+ impl ThinLTOImportMaps {
918+ /// Returns modules imported by `llvm_module_name` during some ThinLTO pass.
919+ fn imports_of ( & self , llvm_module_name : & str ) -> & [ String ] {
892920 self . imports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] )
893921 }
894922
923+ /// Returns modules exported by `llvm_module_name` during some ThinLTO pass.
924+ fn exports_of ( & self , llvm_module_name : & str ) -> & [ String ] {
925+ self . exports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] )
926+ }
927+
895928 fn save_to_file ( & self , path : & Path ) -> io:: Result < ( ) > {
896929 use std:: io:: Write ;
897930 let file = File :: create ( path) ?;
@@ -906,16 +939,20 @@ impl ThinLTOImports {
906939 Ok ( ( ) )
907940 }
908941
909- fn load_from_file ( path : & Path ) -> io:: Result < ThinLTOImports > {
942+ fn load_from_file ( path : & Path ) -> io:: Result < ThinLTOImportMaps > {
910943 use std:: io:: BufRead ;
911944 let mut imports = FxHashMap :: default ( ) ;
912- let mut current_module = None ;
913- let mut current_imports = vec ! [ ] ;
945+ let mut exports: FxHashMap < _ , Vec < _ > > = FxHashMap :: default ( ) ;
946+ let mut current_module: Option < String > = None ;
947+ let mut current_imports: Vec < String > = vec ! [ ] ;
914948 let file = File :: open ( path) ?;
915949 for line in io:: BufReader :: new ( file) . lines ( ) {
916950 let line = line?;
917951 if line. is_empty ( ) {
918952 let importing_module = current_module. take ( ) . expect ( "Importing module not set" ) ;
953+ for imported in & current_imports {
954+ exports. entry ( imported. clone ( ) ) . or_default ( ) . push ( importing_module. clone ( ) ) ;
955+ }
919956 imports. insert ( importing_module, mem:: replace ( & mut current_imports, vec ! [ ] ) ) ;
920957 } else if line. starts_with ( ' ' ) {
921958 // Space marks an imported module
@@ -927,17 +964,17 @@ impl ThinLTOImports {
927964 current_module = Some ( line. trim ( ) . to_string ( ) ) ;
928965 }
929966 }
930- Ok ( ThinLTOImports { imports } )
967+ Ok ( ThinLTOImportMaps { imports, exports } )
931968 }
932969
933970 /// Loads the ThinLTO import map from ThinLTOData.
934- unsafe fn from_thin_lto_data ( data : * const llvm:: ThinLTOData ) -> ThinLTOImports {
971+ unsafe fn from_thin_lto_data ( data : * const llvm:: ThinLTOData ) -> ThinLTOImportMaps {
935972 unsafe extern "C" fn imported_module_callback (
936973 payload : * mut libc:: c_void ,
937974 importing_module_name : * const libc:: c_char ,
938975 imported_module_name : * const libc:: c_char ,
939976 ) {
940- let map = & mut * ( payload as * mut ThinLTOImports ) ;
977+ let map = & mut * ( payload as * mut ThinLTOImportMaps ) ;
941978 let importing_module_name = CStr :: from_ptr ( importing_module_name) ;
942979 let importing_module_name = module_name_to_str ( & importing_module_name) ;
943980 let imported_module_name = CStr :: from_ptr ( imported_module_name) ;
@@ -951,8 +988,18 @@ impl ThinLTOImports {
951988 . get_mut ( importing_module_name)
952989 . unwrap ( )
953990 . push ( imported_module_name. to_owned ( ) ) ;
991+
992+ if !map. exports . contains_key ( imported_module_name) {
993+ map. exports . insert ( imported_module_name. to_owned ( ) , vec ! [ ] ) ;
994+ }
995+
996+ map. exports
997+ . get_mut ( imported_module_name)
998+ . unwrap ( )
999+ . push ( importing_module_name. to_owned ( ) ) ;
9541000 }
955- let mut map = ThinLTOImports :: default ( ) ;
1001+
1002+ let mut map = ThinLTOImportMaps :: default ( ) ;
9561003 llvm:: LLVMRustGetThinLTOModuleImports (
9571004 data,
9581005 imported_module_callback,
0 commit comments