@@ -20,16 +20,23 @@ use rustc::hir::def_id::LOCAL_CRATE;
20
20
use rustc:: middle:: exported_symbols:: SymbolExportLevel ;
21
21
use rustc:: session:: config:: { self , Lto } ;
22
22
use rustc:: util:: common:: time_ext;
23
+ use rustc_data_structures:: fx:: FxHashMap ;
23
24
use time_graph:: Timeline ;
24
25
use { ModuleCodegen , ModuleLlvm , ModuleKind , ModuleSource } ;
25
26
26
27
use libc;
27
28
28
- use std:: ffi:: CString ;
29
+ use std:: ffi:: { CString , CStr } ;
30
+ use std:: fs:: File ;
31
+ use std:: io;
32
+ use std:: mem;
33
+ use std:: path:: Path ;
29
34
use std:: ptr;
30
35
use std:: slice;
31
36
use std:: sync:: Arc ;
32
37
38
+ pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME : & str = "thin-lto-imports.bin" ;
39
+
33
40
pub fn crate_type_allows_lto ( crate_type : config:: CrateType ) -> bool {
34
41
match crate_type {
35
42
config:: CrateTypeExecutable |
@@ -193,7 +200,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
193
200
}
194
201
Lto :: Thin |
195
202
Lto :: ThinLocal => {
196
- thin_lto ( & diag_handler, modules, upstream_modules, & arr, timeline)
203
+ thin_lto ( cgcx , & diag_handler, modules, upstream_modules, & arr, timeline)
197
204
}
198
205
Lto :: No => unreachable ! ( ) ,
199
206
}
@@ -231,7 +238,7 @@ fn fat_lto(cgcx: &CodegenContext,
231
238
. expect ( "must be codegen'ing at least one module" ) ;
232
239
let module = modules. remove ( costliest_module) ;
233
240
let llmod = module. llvm ( ) . expect ( "can't lto pre-codegened modules" ) . llmod ;
234
- info ! ( "using {:?} as a base module" , module. llmod_id ) ;
241
+ info ! ( "using {:?} as a base module" , module. name ) ;
235
242
236
243
// For all other modules we codegened we'll need to link them into our own
237
244
// bitcode. All modules were codegened in their own LLVM context, however,
@@ -241,7 +248,7 @@ fn fat_lto(cgcx: &CodegenContext,
241
248
for module in modules {
242
249
let llvm = module. llvm ( ) . expect ( "can't lto pre-codegened modules" ) ;
243
250
let buffer = ModuleBuffer :: new ( llvm. llmod ) ;
244
- let llmod_id = CString :: new ( & module. llmod_id [ ..] ) . unwrap ( ) ;
251
+ let llmod_id = CString :: new ( & module. name [ ..] ) . unwrap ( ) ;
245
252
serialized_modules. push ( ( SerializedModule :: Local ( buffer) , llmod_id) ) ;
246
253
}
247
254
@@ -346,7 +353,8 @@ impl Drop for Linker {
346
353
/// calculating the *index* for ThinLTO. This index will then be shared amongst
347
354
/// all of the `LtoModuleCodegen` units returned below and destroyed once
348
355
/// they all go out of scope.
349
- fn thin_lto ( diag_handler : & Handler ,
356
+ fn thin_lto ( cgcx : & CodegenContext ,
357
+ diag_handler : & Handler ,
350
358
modules : Vec < ModuleCodegen > ,
351
359
serialized_modules : Vec < ( SerializedModule , CString ) > ,
352
360
symbol_white_list : & [ * const libc:: c_char ] ,
@@ -368,9 +376,9 @@ fn thin_lto(diag_handler: &Handler,
368
376
// the most expensive portion of this small bit of global
369
377
// analysis!
370
378
for ( i, module) in modules. iter ( ) . enumerate ( ) {
371
- info ! ( "local module: {} - {}" , i, module. llmod_id ) ;
379
+ info ! ( "local module: {} - {}" , i, module. name ) ;
372
380
let llvm = module. llvm ( ) . expect ( "can't lto precodegened module" ) ;
373
- let name = CString :: new ( module. llmod_id . clone ( ) ) . unwrap ( ) ;
381
+ let name = CString :: new ( module. name . clone ( ) ) . unwrap ( ) ;
374
382
let buffer = ThinBuffer :: new ( llvm. llmod ) ;
375
383
thin_modules. push ( llvm:: ThinLTOModule {
376
384
identifier : name. as_ptr ( ) ,
@@ -379,7 +387,7 @@ fn thin_lto(diag_handler: &Handler,
379
387
} ) ;
380
388
thin_buffers. push ( buffer) ;
381
389
module_names. push ( name) ;
382
- timeline. record ( & module. llmod_id ) ;
390
+ timeline. record ( & module. name ) ;
383
391
}
384
392
385
393
// FIXME: All upstream crates are deserialized internally in the
@@ -424,6 +432,18 @@ fn thin_lto(diag_handler: &Handler,
424
432
let msg = format ! ( "failed to prepare thin LTO context" ) ;
425
433
return Err ( write:: llvm_err ( & diag_handler, msg) )
426
434
}
435
+
436
+ // Save the ThinLTO import information for incremental compilation.
437
+ if let Some ( ref incr_comp_session_dir) = cgcx. incr_comp_session_dir {
438
+ let path = incr_comp_session_dir. join ( THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME ) ;
439
+ let imports = ThinLTOImports :: from_thin_lto_data ( data) ;
440
+ if let Err ( err) = imports. save_to_file ( & path) {
441
+ let msg = format ! ( "Error while writing ThinLTO import data: {}" ,
442
+ err) ;
443
+ return Err ( write:: llvm_err ( & diag_handler, msg) ) ;
444
+ }
445
+ }
446
+
427
447
let data = ThinData ( data) ;
428
448
info ! ( "thin LTO data created" ) ;
429
449
timeline. record ( "data" ) ;
@@ -656,7 +676,6 @@ impl ThinModule {
656
676
llcx,
657
677
tm,
658
678
} ) ,
659
- llmod_id : self . name ( ) . to_string ( ) ,
660
679
name : self . name ( ) . to_string ( ) ,
661
680
kind : ModuleKind :: Regular ,
662
681
} ;
@@ -776,3 +795,117 @@ impl ThinModule {
776
795
Ok ( module)
777
796
}
778
797
}
798
+
799
+
800
+ #[ derive( Debug ) ]
801
+ pub struct ThinLTOImports {
802
+ // key = llvm name of importing module, value = list of modules it imports from
803
+ imports : FxHashMap < String , Vec < String > > ,
804
+ }
805
+
806
+ impl ThinLTOImports {
807
+
808
+ pub fn new ( ) -> ThinLTOImports {
809
+ ThinLTOImports {
810
+ imports : FxHashMap ( ) ,
811
+ }
812
+ }
813
+
814
+ /// Load the ThinLTO import map from ThinLTOData.
815
+ unsafe fn from_thin_lto_data ( data : * const llvm:: ThinLTOData ) -> ThinLTOImports {
816
+
817
+ fn module_name_to_str ( c_str : & CStr ) -> & str {
818
+ match c_str. to_str ( ) {
819
+ Ok ( s) => s,
820
+ Err ( e) => {
821
+ bug ! ( "Encountered non-utf8 LLVM module name `{}`: {}" ,
822
+ c_str. to_string_lossy( ) ,
823
+ e)
824
+ }
825
+ }
826
+ }
827
+
828
+ unsafe extern "C" fn imported_module_callback ( payload : * mut libc:: c_void ,
829
+ importing_module_name : * const libc:: c_char ,
830
+ imported_module_name : * const libc:: c_char ) {
831
+ let map = & mut * ( payload as * mut ThinLTOImports ) ;
832
+
833
+ let importing_module_name = CStr :: from_ptr ( importing_module_name) ;
834
+ let importing_module_name = module_name_to_str ( & importing_module_name) ;
835
+ let imported_module_name = CStr :: from_ptr ( imported_module_name) ;
836
+ let imported_module_name = module_name_to_str ( & imported_module_name) ;
837
+
838
+ if !map. imports . contains_key ( importing_module_name) {
839
+ map. imports . insert ( importing_module_name. to_owned ( ) , vec ! [ ] ) ;
840
+ }
841
+
842
+ map. imports
843
+ . get_mut ( importing_module_name)
844
+ . unwrap ( )
845
+ . push ( imported_module_name. to_owned ( ) ) ;
846
+ }
847
+
848
+ let mut map = ThinLTOImports {
849
+ imports : FxHashMap ( ) ,
850
+ } ;
851
+
852
+ llvm:: LLVMRustGetThinLTOModuleImports ( data,
853
+ imported_module_callback,
854
+ & mut map as * mut _ as * mut libc:: c_void ) ;
855
+ map
856
+ }
857
+
858
+ pub fn save_to_file ( & self , path : & Path ) -> io:: Result < ( ) > {
859
+ use std:: io:: Write ;
860
+
861
+ let file = File :: create ( path) ?;
862
+ let mut writer = io:: BufWriter :: new ( file) ;
863
+
864
+ for ( importing_module_name, imported_modules) in & self . imports {
865
+ writeln ! ( writer, "{}" , importing_module_name) ?;
866
+
867
+ for imported_module in imported_modules {
868
+ writeln ! ( writer, " {}" , imported_module) ?;
869
+ }
870
+
871
+ writeln ! ( writer) ?;
872
+ }
873
+
874
+ Ok ( ( ) )
875
+ }
876
+
877
+ pub fn load_from_file ( path : & Path ) -> io:: Result < ThinLTOImports > {
878
+ use std:: io:: BufRead ;
879
+
880
+ let mut imports = FxHashMap ( ) ;
881
+ let mut current_module = None ;
882
+ let mut current_imports = vec ! [ ] ;
883
+
884
+ let file = File :: open ( path) ?;
885
+
886
+ for line in io:: BufReader :: new ( file) . lines ( ) {
887
+ let line = line?;
888
+
889
+ if line. is_empty ( ) {
890
+ let importing_module = current_module
891
+ . take ( )
892
+ . expect ( "Importing module not set" ) ;
893
+
894
+ imports. insert ( importing_module,
895
+ mem:: replace ( & mut current_imports, vec ! [ ] ) ) ;
896
+ } else if line. starts_with ( " " ) {
897
+ // This is an imported module
898
+ assert_ne ! ( current_module, None ) ;
899
+ current_imports. push ( line. trim ( ) . to_string ( ) ) ;
900
+ } else {
901
+ // This is the beginning of a new module
902
+ assert_eq ! ( current_module, None ) ;
903
+ current_module = Some ( line. trim ( ) . to_string ( ) ) ;
904
+ }
905
+ }
906
+
907
+ Ok ( ThinLTOImports {
908
+ imports
909
+ } )
910
+ }
911
+ }
0 commit comments