about summary refs log tree commit diff
path: root/src/librustc_codegen_llvm
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-07-13 22:06:38 +0000
committerbors <bors@rust-lang.org>2018-07-13 22:06:38 +0000
commita14a361c2c80fdcd0270766e0bd57104e608988e (patch)
treeb99e7d5c2a0ea280dd37ed069907b86e3ca05af9 /src/librustc_codegen_llvm
parent254f8796b729810846e2b97620032ecaf103db33 (diff)
parente045a6cd8c0235a26ef11e6cd9a13ebd817f1265 (diff)
Auto merge of #52266 - michaelwoerister:incr-thinlto-preliminaries, r=alexcrichton
Preliminary work for incremental ThinLTO.

Since implementing incremental ThinLTO is a bit more involved than I initially thought, I'm splitting out some of the things that already work. This PR (1) adds a way accessing some ThinLTO information in `rustc` and (2) does some cleanup around CGU/object file naming (which makes things quite a bit nicer).

This is probably best reviewed one commit at a time.
Diffstat (limited to 'src/librustc_codegen_llvm')
-rw-r--r--src/librustc_codegen_llvm/back/link.rs7
-rw-r--r--src/librustc_codegen_llvm/back/lto.rs151
-rw-r--r--src/librustc_codegen_llvm/back/write.rs3
-rw-r--r--src/librustc_codegen_llvm/base.rs102
-rw-r--r--src/librustc_codegen_llvm/context.rs5
-rw-r--r--src/librustc_codegen_llvm/lib.rs6
6 files changed, 200 insertions, 74 deletions
diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs
index a7f0910a6fc..9e45da4cb68 100644
--- a/src/librustc_codegen_llvm/back/link.rs
+++ b/src/librustc_codegen_llvm/back/link.rs
@@ -45,13 +45,6 @@ use std::process::{Output, Stdio};
 use std::str;
 use syntax::attr;
 
-/// The LLVM module name containing crate-metadata. This includes a `.` on
-/// purpose, so it cannot clash with the name of a user-defined module.
-pub const METADATA_MODULE_NAME: &'static str = "crate.metadata";
-
-// same as for metadata above, but for allocator shim
-pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator";
-
 pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target,
                                   invalid_output_for_target, build_link_meta, out_filename,
                                   check_file_is_writeable};
diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs
index a33f8b569d0..55499552f5c 100644
--- a/src/librustc_codegen_llvm/back/lto.rs
+++ b/src/librustc_codegen_llvm/back/lto.rs
@@ -20,16 +20,23 @@ use rustc::hir::def_id::LOCAL_CRATE;
 use rustc::middle::exported_symbols::SymbolExportLevel;
 use rustc::session::config::{self, Lto};
 use rustc::util::common::time_ext;
+use rustc_data_structures::fx::FxHashMap;
 use time_graph::Timeline;
 use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource};
 
 use libc;
 
-use std::ffi::CString;
+use std::ffi::{CString, CStr};
+use std::fs::File;
+use std::io;
+use std::mem;
+use std::path::Path;
 use std::ptr;
 use std::slice;
 use std::sync::Arc;
 
+pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-imports.bin";
+
 pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
     match crate_type {
         config::CrateTypeExecutable |
@@ -193,7 +200,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
         }
         Lto::Thin |
         Lto::ThinLocal => {
-            thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline)
+            thin_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline)
         }
         Lto::No => unreachable!(),
     }
@@ -231,7 +238,7 @@ fn fat_lto(cgcx: &CodegenContext,
         .expect("must be codegen'ing at least one module");
     let module = modules.remove(costliest_module);
     let llmod = module.llvm().expect("can't lto pre-codegened modules").llmod;
-    info!("using {:?} as a base module", module.llmod_id);
+    info!("using {:?} as a base module", module.name);
 
     // For all other modules we codegened we'll need to link them into our own
     // bitcode. All modules were codegened in their own LLVM context, however,
@@ -241,7 +248,7 @@ fn fat_lto(cgcx: &CodegenContext,
     for module in modules {
         let llvm = module.llvm().expect("can't lto pre-codegened modules");
         let buffer = ModuleBuffer::new(llvm.llmod);
-        let llmod_id = CString::new(&module.llmod_id[..]).unwrap();
+        let llmod_id = CString::new(&module.name[..]).unwrap();
         serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
     }
 
@@ -346,7 +353,8 @@ impl Drop for Linker {
 /// calculating the *index* for ThinLTO. This index will then be shared amongst
 /// all of the `LtoModuleCodegen` units returned below and destroyed once
 /// they all go out of scope.
-fn thin_lto(diag_handler: &Handler,
+fn thin_lto(cgcx: &CodegenContext,
+            diag_handler: &Handler,
             modules: Vec<ModuleCodegen>,
             serialized_modules: Vec<(SerializedModule, CString)>,
             symbol_white_list: &[*const libc::c_char],
@@ -368,9 +376,9 @@ fn thin_lto(diag_handler: &Handler,
         //        the most expensive portion of this small bit of global
         //        analysis!
         for (i, module) in modules.iter().enumerate() {
-            info!("local module: {} - {}", i, module.llmod_id);
+            info!("local module: {} - {}", i, module.name);
             let llvm = module.llvm().expect("can't lto precodegened module");
-            let name = CString::new(module.llmod_id.clone()).unwrap();
+            let name = CString::new(module.name.clone()).unwrap();
             let buffer = ThinBuffer::new(llvm.llmod);
             thin_modules.push(llvm::ThinLTOModule {
                 identifier: name.as_ptr(),
@@ -379,7 +387,7 @@ fn thin_lto(diag_handler: &Handler,
             });
             thin_buffers.push(buffer);
             module_names.push(name);
-            timeline.record(&module.llmod_id);
+            timeline.record(&module.name);
         }
 
         // FIXME: All upstream crates are deserialized internally in the
@@ -424,6 +432,18 @@ fn thin_lto(diag_handler: &Handler,
             let msg = format!("failed to prepare thin LTO context");
             return Err(write::llvm_err(&diag_handler, msg))
         }
+
+        // Save the ThinLTO import information for incremental compilation.
+        if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir {
+            let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME);
+            let imports = ThinLTOImports::from_thin_lto_data(data);
+            if let Err(err) = imports.save_to_file(&path) {
+                let msg = format!("Error while writing ThinLTO import data: {}",
+                                  err);
+                return Err(write::llvm_err(&diag_handler, msg));
+            }
+        }
+
         let data = ThinData(data);
         info!("thin LTO data created");
         timeline.record("data");
@@ -656,7 +676,6 @@ impl ThinModule {
                 llcx,
                 tm,
             }),
-            llmod_id: self.name().to_string(),
             name: self.name().to_string(),
             kind: ModuleKind::Regular,
         };
@@ -776,3 +795,117 @@ impl ThinModule {
         Ok(module)
     }
 }
+
+
+#[derive(Debug)]
+pub struct ThinLTOImports {
+    // key = llvm name of importing module, value = list of modules it imports from
+    imports: FxHashMap<String, Vec<String>>,
+}
+
+impl ThinLTOImports {
+
+    pub fn new() -> ThinLTOImports {
+        ThinLTOImports {
+            imports: FxHashMap(),
+        }
+    }
+
+    /// Load the ThinLTO import map from ThinLTOData.
+    unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports {
+
+        fn module_name_to_str(c_str: &CStr) -> &str {
+            match c_str.to_str() {
+                Ok(s) => s,
+                Err(e) => {
+                    bug!("Encountered non-utf8 LLVM module name `{}`: {}",
+                        c_str.to_string_lossy(),
+                        e)
+                }
+            }
+        }
+
+        unsafe extern "C" fn imported_module_callback(payload: *mut libc::c_void,
+                                                      importing_module_name: *const libc::c_char,
+                                                      imported_module_name: *const libc::c_char) {
+            let map = &mut* (payload as *mut ThinLTOImports);
+
+            let importing_module_name = CStr::from_ptr(importing_module_name);
+            let importing_module_name = module_name_to_str(&importing_module_name);
+            let imported_module_name = CStr::from_ptr(imported_module_name);
+            let imported_module_name = module_name_to_str(&imported_module_name);
+
+            if !map.imports.contains_key(importing_module_name) {
+                map.imports.insert(importing_module_name.to_owned(), vec![]);
+            }
+
+            map.imports
+               .get_mut(importing_module_name)
+               .unwrap()
+               .push(imported_module_name.to_owned());
+        }
+
+        let mut map = ThinLTOImports {
+            imports: FxHashMap(),
+        };
+
+        llvm::LLVMRustGetThinLTOModuleImports(data,
+                                              imported_module_callback,
+                                              &mut map as *mut _ as *mut libc::c_void);
+        map
+    }
+
+    pub fn save_to_file(&self, path: &Path) -> io::Result<()> {
+        use std::io::Write;
+
+        let file = File::create(path)?;
+        let mut writer = io::BufWriter::new(file);
+
+        for (importing_module_name, imported_modules) in &self.imports {
+            writeln!(writer, "{}", importing_module_name)?;
+
+            for imported_module in imported_modules {
+                writeln!(writer, "  {}", imported_module)?;
+            }
+
+            writeln!(writer)?;
+        }
+
+        Ok(())
+    }
+
+    pub fn load_from_file(path: &Path) -> io::Result<ThinLTOImports> {
+        use std::io::BufRead;
+
+        let mut imports = FxHashMap();
+        let mut current_module = None;
+        let mut current_imports = vec![];
+
+        let file = File::open(path)?;
+
+        for line in io::BufReader::new(file).lines() {
+            let line = line?;
+
+            if line.is_empty() {
+                let importing_module = current_module
+                    .take()
+                    .expect("Importing module not set");
+
+                imports.insert(importing_module,
+                               mem::replace(&mut current_imports, vec![]));
+            } else if line.starts_with(" ") {
+                // This is an imported module
+                assert_ne!(current_module, None);
+                current_imports.push(line.trim().to_string());
+            } else {
+                // This is the beginning of a new module
+                assert_eq!(current_module, None);
+                current_module = Some(line.trim().to_string());
+            }
+        }
+
+        Ok(ThinLTOImports {
+            imports
+        })
+    }
+}
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index 467782518f6..99daf4b0ed6 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -696,7 +696,7 @@ unsafe fn codegen(cgcx: &CodegenContext,
 
         if config.emit_bc_compressed {
             let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
-            let data = bytecode::encode(&module.llmod_id, data);
+            let data = bytecode::encode(&module.name, data);
             if let Err(e) = fs::write(&dst, data) {
                 diag_handler.err(&format!("failed to write bytecode: {}", e));
             }
@@ -1308,7 +1308,6 @@ fn execute_work_item(cgcx: &CodegenContext,
         assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed);
 
         Ok(WorkItemResult::Compiled(CompiledModule {
-            llmod_id: module.llmod_id.clone(),
             name: module_name,
             kind: ModuleKind::Regular,
             pre_existing: true,
diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs
index ea26e271c9b..451e5cf9a60 100644
--- a/src/librustc_codegen_llvm/base.rs
+++ b/src/librustc_codegen_llvm/base.rs
@@ -29,7 +29,7 @@ use super::ModuleCodegen;
 use super::ModuleKind;
 
 use abi;
-use back::link;
+use back::{link, lto};
 use back::write::{self, OngoingCodegen, create_target_machine};
 use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param};
 use llvm;
@@ -739,15 +739,18 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let link_meta = link::build_link_meta(crate_hash);
 
     // Codegen the metadata.
-    let llmod_id = "metadata";
+    let metadata_cgu_name = CodegenUnit::build_cgu_name(tcx,
+                                                        LOCAL_CRATE,
+                                                        &["crate"],
+                                                        Some("metadata")).as_str()
+                                                                         .to_string();
     let (metadata_llcx, metadata_llmod, metadata) =
         time(tcx.sess, "write metadata", || {
-            write_metadata(tcx, llmod_id, &link_meta)
+            write_metadata(tcx, &metadata_cgu_name, &link_meta)
         });
 
     let metadata_module = ModuleCodegen {
-        name: link::METADATA_MODULE_NAME.to_string(),
-        llmod_id: llmod_id.to_string(),
+        name: metadata_cgu_name,
         source: ModuleSource::Codegened(ModuleLlvm {
             llcx: metadata_llcx,
             llmod: metadata_llmod,
@@ -810,26 +813,30 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     // Codegen an allocator shim, if any
     let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() {
-        unsafe {
-            let llmod_id = "allocator";
-            let (llcx, llmod) =
-                context::create_context_and_module(tcx.sess, llmod_id);
-            let modules = ModuleLlvm {
-                llmod,
-                llcx,
-                tm: create_target_machine(tcx.sess, false),
-            };
-            time(tcx.sess, "write allocator module", || {
+        let llmod_id = CodegenUnit::build_cgu_name(tcx,
+                                                   LOCAL_CRATE,
+                                                   &["crate"],
+                                                   Some("allocator")).as_str()
+                                                                     .to_string();
+        let (llcx, llmod) = unsafe {
+            context::create_context_and_module(tcx.sess, &llmod_id)
+        };
+        let modules = ModuleLlvm {
+            llmod,
+            llcx,
+            tm: create_target_machine(tcx.sess, false),
+        };
+        time(tcx.sess, "write allocator module", || {
+            unsafe {
                 allocator::codegen(tcx, &modules, kind)
-            });
+            }
+        });
 
-            Some(ModuleCodegen {
-                name: link::ALLOCATOR_MODULE_NAME.to_string(),
-                llmod_id: llmod_id.to_string(),
-                source: ModuleSource::Codegened(modules),
-                kind: ModuleKind::Allocator,
-            })
-        }
+        Some(ModuleCodegen {
+            name: llmod_id,
+            source: ModuleSource::Codegened(modules),
+            kind: ModuleKind::Allocator,
+        })
     } else {
         None
     };
@@ -872,21 +879,10 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 // succeed it means that none of the dependencies has changed
                 // and we can safely re-use.
                 if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) {
-                    // Append ".rs" to LLVM module identifier.
-                    //
-                    // LLVM code generator emits a ".file filename" directive
-                    // for ELF backends. Value of the "filename" is set as the
-                    // LLVM module identifier.  Due to a LLVM MC bug[1], LLVM
-                    // crashes if the module identifier is same as other symbols
-                    // such as a function name in the module.
-                    // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
-                    let llmod_id = format!("{}.rs", cgu.name());
-
                     let module = ModuleCodegen {
                         name: cgu.name().to_string(),
                         source: ModuleSource::Preexisting(buf),
                         kind: ModuleKind::Regular,
-                        llmod_id,
                     };
                     tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true);
                     write::submit_codegened_module_to_llvm(tcx, module, 0);
@@ -1195,21 +1191,8 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     {
         let cgu_name = cgu.name().to_string();
 
-        // Append ".rs" to LLVM module identifier.
-        //
-        // LLVM code generator emits a ".file filename" directive
-        // for ELF backends. Value of the "filename" is set as the
-        // LLVM module identifier.  Due to a LLVM MC bug[1], LLVM
-        // crashes if the module identifier is same as other symbols
-        // such as a function name in the module.
-        // 1. http://llvm.org/bugs/show_bug.cgi?id=11479
-        let llmod_id = format!("{}-{}.rs",
-                               cgu.name(),
-                               tcx.crate_disambiguator(LOCAL_CRATE)
-                                   .to_fingerprint().to_hex());
-
         // Instantiate monomorphizations without filling out definitions yet...
-        let cx = CodegenCx::new(tcx, cgu, &llmod_id);
+        let cx = CodegenCx::new(tcx, cgu);
         let module = {
             let mono_items = cx.codegen_unit
                                  .items_in_deterministic_order(cx.tcx);
@@ -1267,7 +1250,6 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 name: cgu_name,
                 source: ModuleSource::Codegened(llvm_module),
                 kind: ModuleKind::Regular,
-                llmod_id,
             }
         };
 
@@ -1370,6 +1352,27 @@ mod temp_stable_hash_impls {
     }
 }
 
+#[allow(unused)]
+fn load_thin_lto_imports(sess: &Session) -> lto::ThinLTOImports {
+    let path = rustc_incremental::in_incr_comp_dir_sess(
+        sess,
+        lto::THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME
+    );
+
+    if !path.exists() {
+        return lto::ThinLTOImports::new();
+    }
+
+    match lto::ThinLTOImports::load_from_file(&path) {
+        Ok(imports) => imports,
+        Err(e) => {
+            let msg = format!("Error while trying to load ThinLTO import data \
+                               for incremental compilation: {}", e);
+            sess.fatal(&msg)
+        }
+    }
+}
+
 pub fn define_custom_section(cx: &CodegenCx, def_id: DefId) {
     use rustc::mir::interpret::GlobalId;
 
@@ -1408,3 +1411,4 @@ pub fn define_custom_section(cx: &CodegenCx, def_id: DefId) {
         );
     }
 }
+
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index b774d7c5def..80187f939b4 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -214,8 +214,7 @@ pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (Cont
 
 impl<'a, 'tcx> CodegenCx<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-               codegen_unit: Arc<CodegenUnit<'tcx>>,
-               llmod_id: &str)
+               codegen_unit: Arc<CodegenUnit<'tcx>>)
                -> CodegenCx<'a, 'tcx> {
         // An interesting part of Windows which MSVC forces our hand on (and
         // apparently MinGW didn't) is the usage of `dllimport` and `dllexport`
@@ -268,7 +267,7 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> {
 
         unsafe {
             let (llcx, llmod) = create_context_and_module(&tcx.sess,
-                                                          &llmod_id[..]);
+                                                          &codegen_unit.name().as_str());
 
             let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo {
                 let dctx = debuginfo::CrateDebugContext::new(llmod);
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index 8aa7902021f..af3a512c376 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -90,7 +90,7 @@ mod back {
     mod command;
     pub mod linker;
     pub mod link;
-    mod lto;
+    pub mod lto;
     pub mod symbol_export;
     pub mod write;
     mod rpath;
@@ -258,8 +258,8 @@ struct ModuleCodegen {
     /// unique amongst **all** crates.  Therefore, it should contain
     /// something unique to this crate (e.g., a module path) as well
     /// as the crate name and disambiguator.
+    /// We currently generate these names via CodegenUnit::build_cgu_name().
     name: String,
-    llmod_id: String,
     source: ModuleSource,
     kind: ModuleKind,
 }
@@ -306,7 +306,6 @@ impl ModuleCodegen {
         };
 
         CompiledModule {
-            llmod_id: self.llmod_id,
             name: self.name.clone(),
             kind: self.kind,
             pre_existing,
@@ -320,7 +319,6 @@ impl ModuleCodegen {
 #[derive(Debug)]
 struct CompiledModule {
     name: String,
-    llmod_id: String,
     kind: ModuleKind,
     pre_existing: bool,
     object: Option<PathBuf>,