@@ -19,10 +19,19 @@ use rustc_data_structures::fx::FxHashMap;
19
19
use rustc_codegen_ssa:: { RLIB_BYTECODE_EXTENSION , ModuleCodegen , ModuleKind } ;
20
20
21
21
use std:: ffi:: { CStr , CString } ;
22
+ use std:: fs:: File ;
23
+ use std:: io;
24
+ use std:: mem;
25
+ use std:: path:: Path ;
22
26
use std:: ptr;
23
27
use std:: slice;
24
28
use std:: sync:: Arc ;
25
29
30
+ /// We keep track of past LTO imports that were used to produce the current set
31
+ /// of compiled object files that we might choose to reuse during this
32
+ /// compilation session.
33
+ pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME : & str = "thin-lto-past-imports.bin" ;
34
+
26
35
pub fn crate_type_allows_lto ( crate_type : config:: CrateType ) -> bool {
27
36
match crate_type {
28
37
config:: CrateType :: Executable |
@@ -470,13 +479,26 @@ fn thin_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
470
479
471
480
info ! ( "thin LTO data created" ) ;
472
481
473
- let import_map = if cgcx. incr_comp_session_dir . is_some ( ) {
474
- ThinLTOImports :: from_thin_lto_data ( data)
482
+ let ( import_map_path, prev_import_map, mut curr_import_map) =
483
+ if let Some ( ref incr_comp_session_dir) = cgcx. incr_comp_session_dir
484
+ {
485
+ let path = incr_comp_session_dir. join ( THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME ) ;
486
+ let prev = if path. exists ( ) {
487
+ // FIXME: can/should we recover from IO error occuring here
488
+ // (e.g. by clearing green_modules), rather than panicking from
489
+ // unwrap call?
490
+ Some ( ThinLTOImports :: load_from_file ( & path) . unwrap ( ) )
491
+ } else {
492
+ None
493
+ } ;
494
+ let curr = ThinLTOImports :: from_thin_lto_data ( data) ;
495
+ ( Some ( path) , prev, curr)
475
496
} else {
476
497
// If we don't compile incrementally, we don't need to load the
477
498
// import data from LLVM.
478
499
assert ! ( green_modules. is_empty( ) ) ;
479
- ThinLTOImports :: default ( )
500
+ let curr = ThinLTOImports :: default ( ) ;
501
+ ( None , None , curr)
480
502
} ;
481
503
info ! ( "thin LTO import map loaded" ) ;
482
504
@@ -500,20 +522,35 @@ fn thin_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
500
522
for ( module_index, module_name) in shared. module_names . iter ( ) . enumerate ( ) {
501
523
let module_name = module_name_to_str ( module_name) ;
502
524
503
- // If the module hasn't changed and none of the modules it imports
504
- // from has changed, we can re-use the post-ThinLTO version of the
505
- // module.
525
+ // If the module hasn't changed, and none of the modules it imported
526
+ // from (*) has changed, then we can (1.) re-use the post-ThinLTO
527
+ // version of the module, and thus (2.) save those previous ThinLTO
528
+ // imports as its current set of imports.
529
+ //
530
+ // (*): that is, "imported from" at the time it was previously ThinLTO'ed;
531
+ // see rust-lang/rust#59535.
532
+ //
533
+ // If we do *not* re-use the post-ThinLTO version of the module,
534
+ // then we should save the computed imports that LLVM reported to us
535
+ // during this cycle.
506
536
if green_modules. contains_key ( module_name) {
507
- let imports_all_green = import_map. modules_imported_by ( module_name)
537
+ assert ! ( cgcx. incr_comp_session_dir. is_some( ) ) ;
538
+ assert ! ( prev_import_map. is_some( ) ) ;
539
+
540
+ let prev_import_map = prev_import_map. as_ref ( ) . unwrap ( ) ;
541
+ let prev_imports = prev_import_map. modules_imported_by ( module_name) ;
542
+ let imports_all_green = prev_imports
508
543
. iter ( )
509
544
. all ( |imported_module| green_modules. contains_key ( imported_module) ) ;
510
545
511
546
if imports_all_green {
512
547
let work_product = green_modules[ module_name] . clone ( ) ;
513
548
copy_jobs. push ( work_product) ;
514
549
info ! ( " - {}: re-used" , module_name) ;
550
+ assert ! ( cgcx. incr_comp_session_dir. is_some( ) ) ;
515
551
cgcx. cgu_reuse_tracker . set_actual_reuse ( module_name,
516
552
CguReuse :: PostLto ) ;
553
+ curr_import_map. overwrite_with_past_import_state ( module_name, prev_imports) ;
517
554
continue
518
555
}
519
556
}
@@ -525,6 +562,14 @@ fn thin_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
525
562
} ) ) ;
526
563
}
527
564
565
+ // Save the accumulated ThinLTO import information
566
+ if let Some ( path) = import_map_path {
567
+ if let Err ( err) = curr_import_map. save_to_file ( & path) {
568
+ let msg = format ! ( "Error while writing ThinLTO import data: {}" , err) ;
569
+ return Err ( write:: llvm_err ( & diag_handler, & msg) ) ;
570
+ }
571
+ }
572
+
528
573
Ok ( ( opt_jobs, copy_jobs) )
529
574
}
530
575
}
@@ -826,10 +871,76 @@ pub struct ThinLTOImports {
826
871
}
827
872
828
873
impl ThinLTOImports {
874
+ /// Records `llvm_module_name` as importing `prev_imports` rather than what
875
+ /// we have currently computed. Ensures that the previous imports are a
876
+ /// superset of what imports LLVM currently computed.
877
+ fn overwrite_with_past_import_state ( & mut self ,
878
+ llvm_module_name : & str ,
879
+ prev_imports : & [ String ] ) {
880
+ // There were imports used to previous LTO optimize the module; make
881
+ // sure they are a superset of whatever we have in our current state,
882
+ // and then store them as our new current state.
883
+ let curr_imports_for_mod = self . modules_imported_by ( llvm_module_name) ;
884
+ for imported in curr_imports_for_mod {
885
+ assert ! ( prev_imports. contains( imported) ) ;
886
+ }
887
+ // We can avoid doing the insertion entirely if the sets are equivalent,
888
+ // and we can determine equivalence cheaply via a length comparison,
889
+ // since we have already asserted the past state to be a superset of the
890
+ // current state.
891
+ if prev_imports. len ( ) != curr_imports_for_mod. len ( ) {
892
+ debug ! ( "ThinLTOImports::restore_past_import_state \
893
+ mod: {:?} replacing curr state: {:?} with prev state: {:?}",
894
+ llvm_module_name, curr_imports_for_mod, prev_imports) ;
895
+ self . imports . insert ( llvm_module_name. to_owned ( ) , prev_imports. to_vec ( ) ) ;
896
+ }
897
+ }
898
+
829
899
fn modules_imported_by ( & self , llvm_module_name : & str ) -> & [ String ] {
830
900
self . imports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] )
831
901
}
832
902
903
+ fn save_to_file ( & self , path : & Path ) -> io:: Result < ( ) > {
904
+ use std:: io:: Write ;
905
+ let file = File :: create ( path) ?;
906
+ let mut writer = io:: BufWriter :: new ( file) ;
907
+ for ( importing_module_name, imported_modules) in & self . imports {
908
+ writeln ! ( writer, "{}" , importing_module_name) ?;
909
+ for imported_module in imported_modules {
910
+ writeln ! ( writer, " {}" , imported_module) ?;
911
+ }
912
+ writeln ! ( writer) ?;
913
+ }
914
+ Ok ( ( ) )
915
+ }
916
+
917
+ fn load_from_file ( path : & Path ) -> io:: Result < ThinLTOImports > {
918
+ use std:: io:: BufRead ;
919
+ let mut imports = FxHashMap :: default ( ) ;
920
+ let mut current_module = None ;
921
+ let mut current_imports = vec ! [ ] ;
922
+ let file = File :: open ( path) ?;
923
+ for line in io:: BufReader :: new ( file) . lines ( ) {
924
+ let line = line?;
925
+ if line. is_empty ( ) {
926
+ let importing_module = current_module
927
+ . take ( )
928
+ . expect ( "Importing module not set" ) ;
929
+ imports. insert ( importing_module,
930
+ mem:: replace ( & mut current_imports, vec ! [ ] ) ) ;
931
+ } else if line. starts_with ( " " ) {
932
+ // Space marks an imported module
933
+ assert_ne ! ( current_module, None ) ;
934
+ current_imports. push ( line. trim ( ) . to_string ( ) ) ;
935
+ } else {
936
+ // Otherwise, beginning of a new module (must be start or follow empty line)
937
+ assert_eq ! ( current_module, None ) ;
938
+ current_module = Some ( line. trim ( ) . to_string ( ) ) ;
939
+ }
940
+ }
941
+ Ok ( ThinLTOImports { imports } )
942
+ }
943
+
833
944
/// Loads the ThinLTO import map from ThinLTOData.
834
945
unsafe fn from_thin_lto_data ( data : * const llvm:: ThinLTOData ) -> ThinLTOImports {
835
946
unsafe extern "C" fn imported_module_callback ( payload : * mut libc:: c_void ,
0 commit comments