11
11
use back:: bytecode:: { DecodedBytecode , RLIB_BYTECODE_EXTENSION } ;
12
12
use back:: symbol_export;
13
13
use back:: write:: { ModuleConfig , with_llvm_pmb, CodegenContext } ;
14
- use back:: write:: { self , DiagnosticHandlers } ;
14
+ use back:: write:: { self , DiagnosticHandlers , pre_lto_bitcode_filename } ;
15
15
use errors:: { FatalError , Handler } ;
16
16
use llvm:: archive_ro:: ArchiveRO ;
17
17
use llvm:: { True , False } ;
18
18
use llvm;
19
19
use memmap;
20
+ use rustc:: dep_graph:: WorkProduct ;
20
21
use rustc:: hir:: def_id:: LOCAL_CRATE ;
21
22
use rustc:: middle:: exported_symbols:: SymbolExportLevel ;
22
23
use rustc:: session:: config:: { self , Lto } ;
23
24
use rustc:: util:: common:: time_ext;
24
- use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
25
+ use rustc_data_structures:: fx:: FxHashMap ;
25
26
use time_graph:: Timeline ;
26
27
use { ModuleCodegen , ModuleLlvm , ModuleKind } ;
27
28
28
29
use libc;
29
30
30
31
use std:: ffi:: { CStr , CString } ;
31
32
use std:: fs:: File ;
32
- use std:: io;
33
- use std:: mem;
34
- use std:: path:: Path ;
35
33
use std:: ptr;
36
34
use std:: slice;
37
35
use std:: sync:: Arc ;
38
36
39
- pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME : & str = "thin-lto-imports.bin" ;
40
-
41
37
pub fn crate_type_allows_lto ( crate_type : config:: CrateType ) -> bool {
42
38
match crate_type {
43
39
config:: CrateType :: Executable |
@@ -105,11 +101,16 @@ impl LtoModuleCodegen {
105
101
}
106
102
}
107
103
104
+ /// Performs LTO, which in the case of full LTO means merging all modules into
105
+ /// a single one and returning it for further optimizing. For ThinLTO, it will
106
+ /// do the global analysis necessary and return two lists, one of the modules
107
+ /// the need optimization and another for modules that can simply be copied over
108
+ /// from the incr. comp. cache.
108
109
pub ( crate ) fn run ( cgcx : & CodegenContext ,
109
110
modules : Vec < ModuleCodegen > ,
110
- import_only_modules : Vec < ( SerializedModule , CString ) > ,
111
+ cached_modules : Vec < ( SerializedModule , WorkProduct ) > ,
111
112
timeline : & mut Timeline )
112
- -> Result < Vec < LtoModuleCodegen > , FatalError >
113
+ -> Result < ( Vec < LtoModuleCodegen > , Vec < WorkProduct > ) , FatalError >
113
114
{
114
115
let diag_handler = cgcx. create_diag_handler ( ) ;
115
116
let export_threshold = match cgcx. lto {
@@ -202,13 +203,14 @@ pub(crate) fn run(cgcx: &CodegenContext,
202
203
match cgcx. lto {
203
204
Lto :: Yes | // `-C lto` == fat LTO by default
204
205
Lto :: Fat => {
205
- assert ! ( import_only_modules . is_empty( ) ) ;
206
- fat_lto ( cgcx,
206
+ assert ! ( cached_modules . is_empty( ) ) ;
207
+ let opt_jobs = fat_lto ( cgcx,
207
208
& diag_handler,
208
209
modules,
209
210
upstream_modules,
210
211
& symbol_white_list,
211
- timeline)
212
+ timeline) ;
213
+ opt_jobs. map ( |opt_jobs| ( opt_jobs, vec ! [ ] ) )
212
214
}
213
215
Lto :: Thin |
214
216
Lto :: ThinLocal => {
@@ -220,7 +222,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
220
222
& diag_handler,
221
223
modules,
222
224
upstream_modules,
223
- import_only_modules ,
225
+ cached_modules ,
224
226
& symbol_white_list,
225
227
timeline)
226
228
}
@@ -388,14 +390,19 @@ fn thin_lto(cgcx: &CodegenContext,
388
390
diag_handler : & Handler ,
389
391
modules : Vec < ModuleCodegen > ,
390
392
serialized_modules : Vec < ( SerializedModule , CString ) > ,
391
- import_only_modules : Vec < ( SerializedModule , CString ) > ,
393
+ cached_modules : Vec < ( SerializedModule , WorkProduct ) > ,
392
394
symbol_white_list : & [ * const libc:: c_char ] ,
393
395
timeline : & mut Timeline )
394
- -> Result < Vec < LtoModuleCodegen > , FatalError >
396
+ -> Result < ( Vec < LtoModuleCodegen > , Vec < WorkProduct > ) , FatalError >
395
397
{
396
398
unsafe {
397
399
info ! ( "going for that thin, thin LTO" ) ;
398
400
401
+ let green_modules: FxHashMap < _ , _ > = cached_modules
402
+ . iter ( )
403
+ . map ( |& ( _, ref wp) | ( wp. cgu_name . clone ( ) , wp. clone ( ) ) )
404
+ . collect ( ) ;
405
+
399
406
let mut thin_buffers = Vec :: new ( ) ;
400
407
let mut module_names = Vec :: new ( ) ;
401
408
let mut thin_modules = Vec :: new ( ) ;
@@ -411,6 +418,28 @@ fn thin_lto(cgcx: &CodegenContext,
411
418
info ! ( "local module: {} - {}" , i, module. name) ;
412
419
let name = CString :: new ( module. name . clone ( ) ) . unwrap ( ) ;
413
420
let buffer = ThinBuffer :: new ( module. module_llvm . llmod ( ) ) ;
421
+
422
+ // We emit the module after having serialized it into a ThinBuffer
423
+ // because only then it will contain the ThinLTO module summary.
424
+ if let Some ( ref incr_comp_session_dir) = cgcx. incr_comp_session_dir {
425
+ if cgcx. config ( module. kind ) . emit_pre_thin_lto_bc {
426
+ use std:: io:: Write ;
427
+
428
+ let path = incr_comp_session_dir
429
+ . join ( pre_lto_bitcode_filename ( & module. name ) ) ;
430
+ let mut file = File :: create ( & path) . unwrap_or_else ( |e| {
431
+ panic ! ( "Failed to create pre-lto-bitcode file `{}`: {}" ,
432
+ path. display( ) ,
433
+ e) ;
434
+ } ) ;
435
+ file. write_all ( buffer. data ( ) ) . unwrap_or_else ( |e| {
436
+ panic ! ( "Error writing pre-lto-bitcode file `{}`: {}" ,
437
+ path. display( ) ,
438
+ e) ;
439
+ } ) ;
440
+ }
441
+ }
442
+
414
443
thin_modules. push ( llvm:: ThinLTOModule {
415
444
identifier : name. as_ptr ( ) ,
416
445
data : buffer. data ( ) . as_ptr ( ) ,
@@ -438,24 +467,13 @@ fn thin_lto(cgcx: &CodegenContext,
438
467
// looking at upstream modules entirely sometimes (the contents,
439
468
// we must always unconditionally look at the index).
440
469
let mut serialized = Vec :: new ( ) ;
441
- for ( module, name) in serialized_modules {
442
- info ! ( "foreign module {:?}" , name) ;
443
- thin_modules. push ( llvm:: ThinLTOModule {
444
- identifier : name. as_ptr ( ) ,
445
- data : module. data ( ) . as_ptr ( ) ,
446
- len : module. data ( ) . len ( ) ,
470
+
471
+ let cached_modules = cached_modules. into_iter ( ) . map ( |( sm, wp) | {
472
+ ( sm, CString :: new ( wp. cgu_name ) . unwrap ( ) )
447
473
} ) ;
448
- serialized. push ( module) ;
449
- module_names. push ( name) ;
450
- }
451
474
452
- // All the modules collected up to this point we actually want to
453
- // optimize. The `import_only_modules` below need to be in the list of
454
- // available modules but we don't need to run optimizations for them
455
- // since we already have their optimized version cached.
456
- let modules_to_optimize = module_names. len ( ) ;
457
- for ( module, name) in import_only_modules {
458
- info ! ( "foreign module {:?}" , name) ;
475
+ for ( module, name) in serialized_modules. into_iter ( ) . chain ( cached_modules) {
476
+ info ! ( "upstream or cached module {:?}" , name) ;
459
477
thin_modules. push ( llvm:: ThinLTOModule {
460
478
identifier : name. as_ptr ( ) ,
461
479
data : module. data ( ) . as_ptr ( ) ,
@@ -465,6 +483,9 @@ fn thin_lto(cgcx: &CodegenContext,
465
483
module_names. push ( name) ;
466
484
}
467
485
486
+ // Sanity check
487
+ assert_eq ! ( thin_modules. len( ) , module_names. len( ) ) ;
488
+
468
489
// Delegate to the C++ bindings to create some data here. Once this is a
469
490
// tried-and-true interface we may wish to try to upstream some of this
470
491
// to LLVM itself, right now we reimplement a lot of what they do
@@ -478,30 +499,7 @@ fn thin_lto(cgcx: &CodegenContext,
478
499
write:: llvm_err ( & diag_handler, "failed to prepare thin LTO context" . to_string ( ) )
479
500
} ) ?;
480
501
481
- // Save the ThinLTO import information for incremental compilation.
482
- if let Some ( ref incr_comp_session_dir) = cgcx. incr_comp_session_dir {
483
- let path = incr_comp_session_dir. join ( THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME ) ;
484
-
485
- // The import information from the current compilation session. It
486
- // does not contain info about modules that have been loaded from
487
- // the cache instead of having been recompiled...
488
- let current_imports = ThinLTOImports :: from_thin_lto_data ( data) ;
489
-
490
- // ... so we load this additional information from the previous
491
- // cache file if necessary.
492
- let imports = if path. exists ( ) {
493
- let prev_imports = ThinLTOImports :: load_from_file ( & path) . unwrap ( ) ;
494
- prev_imports. update ( current_imports, & module_names)
495
- } else {
496
- current_imports
497
- } ;
498
-
499
- if let Err ( err) = imports. save_to_file ( & path) {
500
- let msg = format ! ( "Error while writing ThinLTO import data: {}" ,
501
- err) ;
502
- return Err ( write:: llvm_err ( & diag_handler, msg) ) ;
503
- }
504
- }
502
+ let import_map = ThinLTOImports :: from_thin_lto_data ( data) ;
505
503
506
504
let data = ThinData ( data) ;
507
505
info ! ( "thin LTO data created" ) ;
@@ -517,12 +515,36 @@ fn thin_lto(cgcx: &CodegenContext,
517
515
serialized_modules : serialized,
518
516
module_names,
519
517
} ) ;
520
- Ok ( ( 0 ..modules_to_optimize) . map ( |i| {
521
- LtoModuleCodegen :: Thin ( ThinModule {
518
+
519
+ let mut copy_jobs = vec ! [ ] ;
520
+ let mut opt_jobs = vec ! [ ] ;
521
+
522
+ for ( module_index, module_name) in shared. module_names . iter ( ) . enumerate ( ) {
523
+ let module_name = module_name_to_str ( module_name) ;
524
+
525
+ if green_modules. contains_key ( module_name) {
526
+ let mut imports_all_green = true ;
527
+ for imported_module in import_map. modules_imported_by ( module_name) {
528
+ if !green_modules. contains_key ( imported_module) {
529
+ imports_all_green = false ;
530
+ break
531
+ }
532
+ }
533
+
534
+ if imports_all_green {
535
+ let work_product = green_modules[ module_name] . clone ( ) ;
536
+ copy_jobs. push ( work_product) ;
537
+ continue
538
+ }
539
+ }
540
+
541
+ opt_jobs. push ( LtoModuleCodegen :: Thin ( ThinModule {
522
542
shared : shared. clone ( ) ,
523
- idx : i,
524
- } )
525
- } ) . collect ( ) )
543
+ idx : module_index,
544
+ } ) ) ;
545
+ }
546
+
547
+ Ok ( ( opt_jobs, copy_jobs) )
526
548
}
527
549
}
528
550
@@ -850,44 +872,12 @@ pub struct ThinLTOImports {
850
872
}
851
873
852
874
impl ThinLTOImports {
853
- pub fn new ( ) -> ThinLTOImports {
854
- ThinLTOImports {
855
- imports : FxHashMap ( ) ,
856
- }
857
- }
858
-
859
875
pub fn modules_imported_by ( & self , llvm_module_name : & str ) -> & [ String ] {
860
876
self . imports . get ( llvm_module_name) . map ( |v| & v[ ..] ) . unwrap_or ( & [ ] )
861
877
}
862
878
863
- pub fn update ( mut self , new : ThinLTOImports , module_names : & [ CString ] ) -> ThinLTOImports {
864
- let module_names: FxHashSet < _ > = module_names. iter ( ) . map ( |name| {
865
- name. clone ( ) . into_string ( ) . unwrap ( )
866
- } ) . collect ( ) ;
867
-
868
- // Remove all modules that don't exist anymore.
869
- self . imports . retain ( |k, _| module_names. contains ( k) ) ;
870
-
871
- // Overwrite old values
872
- for ( importing_module, imported_modules) in new. imports {
873
- self . imports . insert ( importing_module, imported_modules) ;
874
- }
875
-
876
- self
877
- }
878
-
879
879
/// Load the ThinLTO import map from ThinLTOData.
880
880
unsafe fn from_thin_lto_data ( data : * const llvm:: ThinLTOData ) -> ThinLTOImports {
881
- fn module_name_to_str ( c_str : & CStr ) -> & str {
882
- match c_str. to_str ( ) {
883
- Ok ( s) => s,
884
- Err ( e) => {
885
- bug ! ( "Encountered non-utf8 LLVM module name `{}`: {}" ,
886
- c_str. to_string_lossy( ) ,
887
- e)
888
- }
889
- }
890
- }
891
881
unsafe extern "C" fn imported_module_callback ( payload : * mut libc:: c_void ,
892
882
importing_module_name : * const libc:: c_char ,
893
883
imported_module_name : * const libc:: c_char ) {
@@ -896,6 +886,7 @@ impl ThinLTOImports {
896
886
let importing_module_name = module_name_to_str ( & importing_module_name) ;
897
887
let imported_module_name = CStr :: from_ptr ( imported_module_name) ;
898
888
let imported_module_name = module_name_to_str ( & imported_module_name) ;
889
+
899
890
if !map. imports . contains_key ( importing_module_name) {
900
891
map. imports . insert ( importing_module_name. to_owned ( ) , vec ! [ ] ) ;
901
892
}
@@ -913,47 +904,15 @@ impl ThinLTOImports {
913
904
& mut map as * mut _ as * mut libc:: c_void ) ;
914
905
map
915
906
}
916
-
917
- pub fn save_to_file ( & self , path : & Path ) -> io:: Result < ( ) > {
918
- use std:: io:: Write ;
919
- let file = File :: create ( path) ?;
920
- let mut writer = io:: BufWriter :: new ( file) ;
921
- for ( importing_module_name, imported_modules) in & self . imports {
922
- writeln ! ( writer, "{}" , importing_module_name) ?;
923
- for imported_module in imported_modules {
924
- writeln ! ( writer, " {}" , imported_module) ?;
925
- }
926
- writeln ! ( writer) ?;
927
- }
928
- Ok ( ( ) )
929
907
}
930
908
931
- pub fn load_from_file ( path : & Path ) -> io:: Result < ThinLTOImports > {
932
- use std:: io:: BufRead ;
933
- let mut imports = FxHashMap ( ) ;
934
- let mut current_module = None ;
935
- let mut current_imports = vec ! [ ] ;
936
- let file = File :: open ( path) ?;
937
- for line in io:: BufReader :: new ( file) . lines ( ) {
938
- let line = line?;
939
- if line. is_empty ( ) {
940
- let importing_module = current_module
941
- . take ( )
942
- . expect ( "Importing module not set" ) ;
943
- imports. insert ( importing_module,
944
- mem:: replace ( & mut current_imports, vec ! [ ] ) ) ;
945
- } else if line. starts_with ( " " ) {
946
- // This is an imported module
947
- assert_ne ! ( current_module, None ) ;
948
- current_imports. push ( line. trim ( ) . to_string ( ) ) ;
949
- } else {
950
- // This is the beginning of a new module
951
- assert_eq ! ( current_module, None ) ;
952
- current_module = Some ( line. trim ( ) . to_string ( ) ) ;
953
- }
909
+ fn module_name_to_str ( c_str : & CStr ) -> & str {
910
+ match c_str. to_str ( ) {
911
+ Ok ( s) => s,
912
+ Err ( e) => {
913
+ bug ! ( "Encountered non-utf8 LLVM module name `{}`: {}" ,
914
+ c_str. to_string_lossy( ) ,
915
+ e)
954
916
}
955
- Ok ( ThinLTOImports {
956
- imports
957
- } )
958
917
}
959
918
}
0 commit comments