@@ -20,16 +20,23 @@ use rustc::hir::def_id::LOCAL_CRATE;
2020use rustc:: middle:: exported_symbols:: SymbolExportLevel ;
2121use rustc:: session:: config:: { self , Lto } ;
2222use rustc:: util:: common:: time_ext;
23+ use rustc_data_structures:: fx:: FxHashMap ;
2324use time_graph:: Timeline ;
2425use { ModuleCodegen , ModuleLlvm , ModuleKind , ModuleSource } ;
2526
2627use libc;
2728
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 ;
2934use std:: ptr;
3035use std:: slice;
3136use std:: sync:: Arc ;
3237
38+ pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME : & str = "thin-lto-imports.bin" ;
39+
3340pub fn crate_type_allows_lto ( crate_type : config:: CrateType ) -> bool {
3441 match crate_type {
3542 config:: CrateTypeExecutable |
@@ -193,7 +200,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
193200 }
194201 Lto :: Thin |
195202 Lto :: ThinLocal => {
196- thin_lto ( & diag_handler, modules, upstream_modules, & arr, timeline)
203+ thin_lto ( cgcx , & diag_handler, modules, upstream_modules, & arr, timeline)
197204 }
198205 Lto :: No => unreachable ! ( ) ,
199206 }
@@ -231,7 +238,7 @@ fn fat_lto(cgcx: &CodegenContext,
231238 . expect ( "must be codegen'ing at least one module" ) ;
232239 let module = modules. remove ( costliest_module) ;
233240 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 ) ;
235242
236243 // For all other modules we codegened we'll need to link them into our own
237244 // bitcode. All modules were codegened in their own LLVM context, however,
@@ -241,7 +248,7 @@ fn fat_lto(cgcx: &CodegenContext,
241248 for module in modules {
242249 let llvm = module. llvm ( ) . expect ( "can't lto pre-codegened modules" ) ;
243250 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 ( ) ;
245252 serialized_modules. push ( ( SerializedModule :: Local ( buffer) , llmod_id) ) ;
246253 }
247254
@@ -346,7 +353,8 @@ impl Drop for Linker {
346353/// calculating the *index* for ThinLTO. This index will then be shared amongst
347354/// all of the `LtoModuleCodegen` units returned below and destroyed once
348355/// they all go out of scope.
349- fn thin_lto ( diag_handler : & Handler ,
356+ fn thin_lto ( cgcx : & CodegenContext ,
357+ diag_handler : & Handler ,
350358 modules : Vec < ModuleCodegen > ,
351359 serialized_modules : Vec < ( SerializedModule , CString ) > ,
352360 symbol_white_list : & [ * const libc:: c_char ] ,
@@ -368,9 +376,9 @@ fn thin_lto(diag_handler: &Handler,
368376 // the most expensive portion of this small bit of global
369377 // analysis!
370378 for ( i, module) in modules. iter ( ) . enumerate ( ) {
371- info ! ( "local module: {} - {}" , i, module. llmod_id ) ;
379+ info ! ( "local module: {} - {}" , i, module. name ) ;
372380 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 ( ) ;
374382 let buffer = ThinBuffer :: new ( llvm. llmod ) ;
375383 thin_modules. push ( llvm:: ThinLTOModule {
376384 identifier : name. as_ptr ( ) ,
@@ -379,7 +387,7 @@ fn thin_lto(diag_handler: &Handler,
379387 } ) ;
380388 thin_buffers. push ( buffer) ;
381389 module_names. push ( name) ;
382- timeline. record ( & module. llmod_id ) ;
390+ timeline. record ( & module. name ) ;
383391 }
384392
385393 // FIXME: All upstream crates are deserialized internally in the
@@ -424,6 +432,18 @@ fn thin_lto(diag_handler: &Handler,
424432 let msg = format ! ( "failed to prepare thin LTO context" ) ;
425433 return Err ( write:: llvm_err ( & diag_handler, msg) )
426434 }
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+
427447 let data = ThinData ( data) ;
428448 info ! ( "thin LTO data created" ) ;
429449 timeline. record ( "data" ) ;
@@ -656,7 +676,6 @@ impl ThinModule {
656676 llcx,
657677 tm,
658678 } ) ,
659- llmod_id : self . name ( ) . to_string ( ) ,
660679 name : self . name ( ) . to_string ( ) ,
661680 kind : ModuleKind :: Regular ,
662681 } ;
@@ -776,3 +795,117 @@ impl ThinModule {
776795 Ok ( module)
777796 }
778797}
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