@@ -2,14 +2,14 @@ use crate::back::write::{
22    self ,  save_temp_bitcode,  to_llvm_opt_settings,  with_llvm_pmb,  DiagnosticHandlers , 
33} ; 
44use  crate :: llvm:: archive_ro:: ArchiveRO ; 
5- use  crate :: llvm:: { self ,  False ,  True } ; 
5+ use  crate :: llvm:: { self ,  build_string ,   False ,  True } ; 
66use  crate :: { LlvmCodegenBackend ,  ModuleLlvm } ; 
77use  rustc_codegen_ssa:: back:: lto:: { LtoModuleCodegen ,  SerializedModule ,  ThinModule ,  ThinShared } ; 
88use  rustc_codegen_ssa:: back:: symbol_export; 
99use  rustc_codegen_ssa:: back:: write:: { CodegenContext ,  FatLTOInput ,  ModuleConfig } ; 
1010use  rustc_codegen_ssa:: traits:: * ; 
1111use  rustc_codegen_ssa:: { looks_like_rust_object_file,  ModuleCodegen ,  ModuleKind } ; 
12- use  rustc_data_structures:: fx:: { FxHashMap ,   FxHashSet } ; 
12+ use  rustc_data_structures:: fx:: FxHashMap ; 
1313use  rustc_errors:: { FatalError ,  Handler } ; 
1414use  rustc_hir:: def_id:: LOCAL_CRATE ; 
1515use  rustc_middle:: bug; 
@@ -22,16 +22,14 @@ use tracing::{debug, info};
2222use  std:: ffi:: { CStr ,  CString } ; 
2323use  std:: fs:: File ; 
2424use  std:: io; 
25- use  std:: mem; 
2625use  std:: path:: Path ; 
2726use  std:: ptr; 
2827use  std:: slice; 
2928use  std:: sync:: Arc ; 
3029
31- /// We keep track of past LTO imports that were used to produce the current set 
32- /// of compiled object files that we might choose to reuse during this 
33- /// compilation session. 
34- pub  const  THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME :  & str  = "thin-lto-past-imports.bin" ; 
30+ /// We keep track of the computed LTO cache keys from the previous 
31+ /// session to determine which CGUs we can reuse. 
32+ pub  const  THIN_LTO_KEYS_INCR_COMP_FILE_NAME :  & str  = "thin-lto-past-keys.bin" ; 
3533
3634pub  fn  crate_type_allows_lto ( crate_type :  CrateType )  -> bool  { 
3735    match  crate_type { 
@@ -485,31 +483,31 @@ fn thin_lto(
485483        ) 
486484        . ok_or_else ( || write:: llvm_err ( & diag_handler,  "failed to prepare thin LTO context" ) ) ?; 
487485
488-         info ! ( "thin LTO  data created" ) ; 
486+         let   data =  ThinData ( data ) ; 
489487
490-         let  ( import_map_path,  prev_import_map,  curr_import_map)  =
491-             if  let  Some ( ref  incr_comp_session_dir)  = cgcx. incr_comp_session_dir  { 
492-                 let  path = incr_comp_session_dir. join ( THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME ) ; 
493-                 // If previous imports have been deleted, or we get an IO error 
494-                 // reading the file storing them, then we'll just use `None` as the 
495-                 // prev_import_map, which will force the code to be recompiled. 
496-                 let  prev = if  path. exists ( )  { 
497-                     ThinLTOImportMaps :: load_from_file ( & path) . ok ( ) 
498-                 }  else  { 
499-                     None 
500-                 } ; 
501-                 let  curr = ThinLTOImportMaps :: from_thin_lto_data ( data) ; 
502-                 ( Some ( path) ,  prev,  curr) 
503-             }  else  { 
504-                 // If we don't compile incrementally, we don't need to load the 
505-                 // import data from LLVM. 
506-                 assert ! ( green_modules. is_empty( ) ) ; 
507-                 let  curr = ThinLTOImportMaps :: default ( ) ; 
508-                 ( None ,  None ,  curr) 
509-             } ; 
510-         info ! ( "thin LTO import map loaded" ) ; 
488+         info ! ( "thin LTO data created" ) ; 
511489
512-         let  data = ThinData ( data) ; 
490+         let  ( key_map_path,  prev_key_map,  curr_key_map)  = if  let  Some ( ref  incr_comp_session_dir)  =
491+             cgcx. incr_comp_session_dir 
492+         { 
493+             let  path = incr_comp_session_dir. join ( THIN_LTO_KEYS_INCR_COMP_FILE_NAME ) ; 
494+             // If the previous file was deleted, or we get an IO error 
495+             // reading the file, then we'll just use `None` as the 
496+             // prev_key_map, which will force the code to be recompiled. 
497+             let  prev =
498+                 if  path. exists ( )  {  ThinLTOKeysMap :: load_from_file ( & path) . ok ( )  }  else  {  None  } ; 
499+             let  curr = ThinLTOKeysMap :: from_thin_lto_modules ( & data,  & thin_modules,  & module_names) ; 
500+             ( Some ( path) ,  prev,  curr) 
501+         }  else  { 
502+             // If we don't compile incrementally, we don't need to load the 
503+             // import data from LLVM. 
504+             assert ! ( green_modules. is_empty( ) ) ; 
505+             let  curr = ThinLTOKeysMap :: default ( ) ; 
506+             ( None ,  None ,  curr) 
507+         } ; 
508+         info ! ( "thin LTO cache key map loaded" ) ; 
509+         info ! ( "prev_key_map: {:#?}" ,  prev_key_map) ; 
510+         info ! ( "curr_key_map: {:#?}" ,  curr_key_map) ; 
513511
514512        // Throw our data in an `Arc` as we'll be sharing it across threads. We 
515513        // also put all memory referenced by the C++ data (buffers, ids, etc) 
@@ -528,60 +526,14 @@ fn thin_lto(
528526        info ! ( "checking which modules can be-reused and which have to be re-optimized." ) ; 
529527        for  ( module_index,  module_name)  in  shared. module_names . iter ( ) . enumerate ( )  { 
530528            let  module_name = module_name_to_str ( module_name) ; 
531- 
532-             // If (1.) the module hasn't changed, and (2.) none of the modules 
533-             // it imports from have changed, *and* (3.) the import and export 
534-             // sets themselves have not changed from the previous compile when 
535-             // it was last ThinLTO'ed, then we can re-use the post-ThinLTO 
536-             // version of the module. Otherwise, freshly perform LTO 
537-             // optimization. 
538-             // 
539-             // (Note that globally, the export set is just the inverse of the 
540-             // import set.) 
541-             // 
542-             // For further justification of why the above is necessary and sufficient, 
543-             // see the LLVM blog post on ThinLTO: 
544-             // 
545-             // http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html 
546-             // 
547-             // which states the following: 
548-             // 
549-             // ```quote 
550-             // any particular ThinLTO backend must be redone iff: 
551-             // 
552-             // 1. The corresponding (primary) module’s bitcode changed 
553-             // 2. The list of imports into or exports from the module changed 
554-             // 3. The bitcode for any module being imported from has changed 
555-             // 4. Any global analysis result affecting either the primary module 
556-             //    or anything it imports has changed. 
557-             // ``` 
558-             // 
559-             // This strategy means we can always save the computed imports as 
560-             // canon: when we reuse the post-ThinLTO version, condition (3.) 
561-             // ensures that the current import set is the same as the previous 
562-             // one. (And of course, when we don't reuse the post-ThinLTO 
563-             // version, the current import set *is* the correct one, since we 
564-             // are doing the ThinLTO in this current compilation cycle.) 
565-             // 
566-             // For more discussion, see rust-lang/rust#59535 (where the import 
567-             // issue was discovered) and rust-lang/rust#69798 (where the 
568-             // analogous export issue was discovered). 
569-             if  let  ( Some ( prev_import_map) ,  true )  =
570-                 ( prev_import_map. as_ref ( ) ,  green_modules. contains_key ( module_name) ) 
529+             if  let  ( Some ( prev_key_map) ,  true )  =
530+                 ( prev_key_map. as_ref ( ) ,  green_modules. contains_key ( module_name) ) 
571531            { 
572532                assert ! ( cgcx. incr_comp_session_dir. is_some( ) ) ; 
573533
574-                 let  prev_imports = prev_import_map. imports_of ( module_name) ; 
575-                 let  curr_imports = curr_import_map. imports_of ( module_name) ; 
576-                 let  prev_exports = prev_import_map. exports_of ( module_name) ; 
577-                 let  curr_exports = curr_import_map. exports_of ( module_name) ; 
578-                 let  imports_all_green = curr_imports
579-                     . iter ( ) 
580-                     . all ( |imported_module| green_modules. contains_key ( imported_module) ) ; 
581-                 if  imports_all_green
582-                     && equivalent_as_sets ( prev_imports,  curr_imports) 
583-                     && equivalent_as_sets ( prev_exports,  curr_exports) 
584-                 { 
534+                 // If a module exists in both the current and the previous session, 
535+                 // and has the same LTO cache key in both sessions, then we can re-use it 
536+                 if  prev_key_map. keys . get ( module_name)  == curr_key_map. keys . get ( module_name)  { 
585537                    let  work_product = green_modules[ module_name] . clone ( ) ; 
586538                    copy_jobs. push ( work_product) ; 
587539                    info ! ( " - {}: re-used" ,  module_name) ; 
@@ -599,10 +551,10 @@ fn thin_lto(
599551        } 
600552
601553        // Save the current ThinLTO import information for the next compilation 
602-         // session, overwriting the previous serialized imports  (if any). 
603-         if  let  Some ( path)  = import_map_path  { 
604-             if  let  Err ( err)  = curr_import_map . save_to_file ( & path)  { 
605-                 let  msg = format ! ( "Error while writing ThinLTO import  data: {}" ,  err) ; 
554+         // session, overwriting the previous serialized data  (if any). 
555+         if  let  Some ( path)  = key_map_path  { 
556+             if  let  Err ( err)  = curr_key_map . save_to_file ( & path)  { 
557+                 let  msg = format ! ( "Error while writing ThinLTO key  data: {}" ,  err) ; 
606558                return  Err ( write:: llvm_err ( & diag_handler,  & msg) ) ; 
607559            } 
608560        } 
@@ -611,24 +563,6 @@ fn thin_lto(
611563    } 
612564} 
613565
614- /// Given two slices, each with no repeat elements. returns true if and only if 
615- /// the two slices have the same contents when considered as sets (i.e. when 
616- /// element order is disregarded). 
617- fn  equivalent_as_sets ( a :  & [ String ] ,  b :  & [ String ] )  -> bool  { 
618-     // cheap path: unequal lengths means cannot possibly be set equivalent. 
619-     if  a. len ( )  != b. len ( )  { 
620-         return  false ; 
621-     } 
622-     // fast path: before building new things, check if inputs are equivalent as is. 
623-     if  a == b { 
624-         return  true ; 
625-     } 
626-     // slow path: general set comparison. 
627-     let  a:  FxHashSet < & str >  = a. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ; 
628-     let  b:  FxHashSet < & str >  = b. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ; 
629-     a == b
630- } 
631- 
632566pub ( crate )  fn  run_pass_manager ( 
633567    cgcx :  & CodegenContext < LlvmCodegenBackend > , 
634568    module :  & ModuleCodegen < ModuleLlvm > , 
@@ -942,113 +876,56 @@ pub unsafe fn optimize_thin_module(
942876    Ok ( module) 
943877} 
944878
945- /// Summarizes module import/export relationships used by LLVM's ThinLTO pass. 
946- /// 
947- /// Note that we tend to have two such instances of `ThinLTOImportMaps` in use: 
948- /// one loaded from a file that represents the relationships used during the 
949- /// compilation associated with the incremetnal build artifacts we are 
950- /// attempting to reuse, and another constructed via `from_thin_lto_data`, which 
951- /// captures the relationships of ThinLTO in the current compilation. 
879+ /// Maps LLVM module identifiers to their corresponding LLVM LTO cache keys 
952880#[ derive( Debug ,  Default ) ]  
953- pub  struct  ThinLTOImportMaps  { 
954-     // key = llvm name of importing module, value = list of modules it imports from 
955-     imports :  FxHashMap < String ,  Vec < String > > , 
956-     // key = llvm name of exporting module, value = list of modules it exports to 
957-     exports :  FxHashMap < String ,  Vec < String > > , 
881+ pub  struct  ThinLTOKeysMap  { 
882+     // key = llvm name of importing module, value = LLVM cache key 
883+     keys :  FxHashMap < String ,  String > , 
958884} 
959885
960- impl  ThinLTOImportMaps  { 
961-     /// Returns modules imported by `llvm_module_name` during some ThinLTO pass. 
962- fn  imports_of ( & self ,  llvm_module_name :  & str )  -> & [ String ]  { 
963-         self . imports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] ) 
964-     } 
965- 
966-     /// Returns modules exported by `llvm_module_name` during some ThinLTO pass. 
967- fn  exports_of ( & self ,  llvm_module_name :  & str )  -> & [ String ]  { 
968-         self . exports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] ) 
969-     } 
970- 
886+ impl  ThinLTOKeysMap  { 
971887    fn  save_to_file ( & self ,  path :  & Path )  -> io:: Result < ( ) >  { 
972888        use  std:: io:: Write ; 
973889        let  file = File :: create ( path) ?; 
974890        let  mut  writer = io:: BufWriter :: new ( file) ; 
975-         for  ( importing_module_name,  imported_modules)  in  & self . imports  { 
976-             writeln ! ( writer,  "{}" ,  importing_module_name) ?; 
977-             for  imported_module in  imported_modules { 
978-                 writeln ! ( writer,  " {}" ,  imported_module) ?; 
979-             } 
980-             writeln ! ( writer) ?; 
891+         for  ( module,  key)  in  & self . keys  { 
892+             writeln ! ( writer,  "{} {}" ,  module,  key) ?; 
981893        } 
982894        Ok ( ( ) ) 
983895    } 
984896
985-     fn  load_from_file ( path :  & Path )  -> io:: Result < ThinLTOImportMaps >  { 
897+     fn  load_from_file ( path :  & Path )  -> io:: Result < Self >  { 
986898        use  std:: io:: BufRead ; 
987-         let  mut  imports = FxHashMap :: default ( ) ; 
988-         let  mut  exports:  FxHashMap < _ ,  Vec < _ > >  = FxHashMap :: default ( ) ; 
989-         let  mut  current_module:  Option < String >  = None ; 
990-         let  mut  current_imports:  Vec < String >  = vec ! [ ] ; 
899+         let  mut  keys = FxHashMap :: default ( ) ; 
991900        let  file = File :: open ( path) ?; 
992901        for  line in  io:: BufReader :: new ( file) . lines ( )  { 
993902            let  line = line?; 
994-             if  line. is_empty ( )  { 
995-                 let  importing_module = current_module. take ( ) . expect ( "Importing module not set" ) ; 
996-                 for  imported in  & current_imports { 
997-                     exports. entry ( imported. clone ( ) ) . or_default ( ) . push ( importing_module. clone ( ) ) ; 
998-                 } 
999-                 imports. insert ( importing_module,  mem:: replace ( & mut  current_imports,  vec ! [ ] ) ) ; 
1000-             }  else  if  line. starts_with ( ' ' )  { 
1001-                 // Space marks an imported module 
1002-                 assert_ne ! ( current_module,  None ) ; 
1003-                 current_imports. push ( line. trim ( ) . to_string ( ) ) ; 
1004-             }  else  { 
1005-                 // Otherwise, beginning of a new module (must be start or follow empty line) 
1006-                 assert_eq ! ( current_module,  None ) ; 
1007-                 current_module = Some ( line. trim ( ) . to_string ( ) ) ; 
1008-             } 
903+             let  mut  split = line. split ( " " ) ; 
904+             let  module = split. next ( ) . unwrap ( ) ; 
905+             let  key = split. next ( ) . unwrap ( ) ; 
906+             assert_eq ! ( split. next( ) ,  None ,  "Expected two space-separated values, found {:?}" ,  line) ; 
907+             keys. insert ( module. to_string ( ) ,  key. to_string ( ) ) ; 
1009908        } 
1010-         Ok ( ThinLTOImportMaps  {  imports ,  exports  } ) 
909+         Ok ( Self  {  keys  } ) 
1011910    } 
1012911
1013-     /// Loads the ThinLTO import map from ThinLTOData. 
1014- unsafe  fn  from_thin_lto_data ( data :  * const  llvm:: ThinLTOData )  -> ThinLTOImportMaps  { 
1015-         unsafe  extern  "C"  fn  imported_module_callback ( 
1016-             payload :  * mut  libc:: c_void , 
1017-             importing_module_name :  * const  libc:: c_char , 
1018-             imported_module_name :  * const  libc:: c_char , 
1019-         )  { 
1020-             let  map = & mut  * ( payload as  * mut  ThinLTOImportMaps ) ; 
1021-             let  importing_module_name = CStr :: from_ptr ( importing_module_name) ; 
1022-             let  importing_module_name = module_name_to_str ( & importing_module_name) ; 
1023-             let  imported_module_name = CStr :: from_ptr ( imported_module_name) ; 
1024-             let  imported_module_name = module_name_to_str ( & imported_module_name) ; 
1025- 
1026-             if  !map. imports . contains_key ( importing_module_name)  { 
1027-                 map. imports . insert ( importing_module_name. to_owned ( ) ,  vec ! [ ] ) ; 
1028-             } 
1029- 
1030-             map. imports 
1031-                 . get_mut ( importing_module_name) 
1032-                 . unwrap ( ) 
1033-                 . push ( imported_module_name. to_owned ( ) ) ; 
1034- 
1035-             if  !map. exports . contains_key ( imported_module_name)  { 
1036-                 map. exports . insert ( imported_module_name. to_owned ( ) ,  vec ! [ ] ) ; 
1037-             } 
1038- 
1039-             map. exports 
1040-                 . get_mut ( imported_module_name) 
1041-                 . unwrap ( ) 
1042-                 . push ( importing_module_name. to_owned ( ) ) ; 
1043-         } 
1044- 
1045-         let  mut  map = ThinLTOImportMaps :: default ( ) ; 
1046-         llvm:: LLVMRustGetThinLTOModuleImports ( 
1047-             data, 
1048-             imported_module_callback, 
1049-             & mut  map as  * mut  _  as  * mut  libc:: c_void , 
1050-         ) ; 
1051-         map
912+     fn  from_thin_lto_modules ( 
913+         data :  & ThinData , 
914+         modules :  & [ llvm:: ThinLTOModule ] , 
915+         names :  & [ CString ] , 
916+     )  -> Self  { 
917+         let  keys = modules
918+             . iter ( ) 
919+             . zip ( names. iter ( ) ) 
920+             . map ( |( module,  name) | { 
921+                 let  key = build_string ( |rust_str| unsafe  { 
922+                     llvm:: LLVMRustComputeLTOCacheKey ( rust_str,  module. identifier ,  data. 0 ) ; 
923+                 } ) 
924+                 . expect ( "Invalid ThinLTO module key" ) ; 
925+                 ( name. clone ( ) . into_string ( ) . unwrap ( ) ,  key) 
926+             } ) 
927+             . collect ( ) ; 
928+         Self  {  keys } 
1052929    } 
1053930} 
1054931
0 commit comments