Skip to content

Commit abd5cc3

Browse files
Always add all modules to the global ThinLTO module analysis when compiling incrementally.
1 parent 64a738d commit abd5cc3

File tree

6 files changed

+153
-344
lines changed

6 files changed

+153
-344
lines changed

src/Cargo.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,15 @@ dependencies = [
11981198
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
11991199
]
12001200

1201+
[[package]]
1202+
name = "memmap"
1203+
version = "0.6.2"
1204+
source = "registry+https://github.com/rust-lang/crates.io-index"
1205+
dependencies = [
1206+
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
1207+
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
1208+
]
1209+
12011210
[[package]]
12021211
name = "memoffset"
12031212
version = "0.2.1"
@@ -2029,6 +2038,7 @@ name = "rustc_codegen_llvm"
20292038
version = "0.0.0"
20302039
dependencies = [
20312040
"cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
2041+
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
20322042
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
20332043
"rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
20342044
"rustc_llvm 0.0.0",
@@ -3151,6 +3161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
31513161
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
31523162
"checksum mdbook 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "90b5a8d7e341ceee5db3882a06078d42661ddcfa2b3687319cc5da76ec4e782f"
31533163
"checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d"
3164+
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
31543165
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
31553166
"checksum minifier 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "9908ed7c62f990c21ab41fdca53a864a3ada0da69d8729c4de727b397e27bc11"
31563167
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"

src/librustc/dep_graph/graph.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,6 @@ pub enum WorkProductFileKind {
883883
Object,
884884
Bytecode,
885885
BytecodeCompressed,
886-
PreThinLtoBytecode,
887886
}
888887

889888
pub(super) struct CurrentDepGraph {

src/librustc_codegen_llvm/back/lto.rs

Lines changed: 90 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,29 @@
1111
use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION};
1212
use back::symbol_export;
1313
use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext};
14-
use back::write::{self, DiagnosticHandlers};
14+
use back::write::{self, DiagnosticHandlers, pre_lto_bitcode_filename};
1515
use errors::{FatalError, Handler};
1616
use llvm::archive_ro::ArchiveRO;
1717
use llvm::{True, False};
1818
use llvm;
1919
use memmap;
20+
use rustc::dep_graph::WorkProduct;
2021
use rustc::hir::def_id::LOCAL_CRATE;
2122
use rustc::middle::exported_symbols::SymbolExportLevel;
2223
use rustc::session::config::{self, Lto};
2324
use rustc::util::common::time_ext;
24-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
25+
use rustc_data_structures::fx::FxHashMap;
2526
use time_graph::Timeline;
2627
use {ModuleCodegen, ModuleLlvm, ModuleKind};
2728

2829
use libc;
2930

3031
use std::ffi::{CStr, CString};
3132
use std::fs::File;
32-
use std::io;
33-
use std::mem;
34-
use std::path::Path;
3533
use std::ptr;
3634
use std::slice;
3735
use std::sync::Arc;
3836

39-
pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-imports.bin";
40-
4137
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
4238
match crate_type {
4339
config::CrateType::Executable |
@@ -105,11 +101,16 @@ impl LtoModuleCodegen {
105101
}
106102
}
107103

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.
108109
pub(crate) fn run(cgcx: &CodegenContext,
109110
modules: Vec<ModuleCodegen>,
110-
import_only_modules: Vec<(SerializedModule, CString)>,
111+
cached_modules: Vec<(SerializedModule, WorkProduct)>,
111112
timeline: &mut Timeline)
112-
-> Result<Vec<LtoModuleCodegen>, FatalError>
113+
-> Result<(Vec<LtoModuleCodegen>, Vec<WorkProduct>), FatalError>
113114
{
114115
let diag_handler = cgcx.create_diag_handler();
115116
let export_threshold = match cgcx.lto {
@@ -202,13 +203,14 @@ pub(crate) fn run(cgcx: &CodegenContext,
202203
match cgcx.lto {
203204
Lto::Yes | // `-C lto` == fat LTO by default
204205
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,
207208
&diag_handler,
208209
modules,
209210
upstream_modules,
210211
&symbol_white_list,
211-
timeline)
212+
timeline);
213+
opt_jobs.map(|opt_jobs| (opt_jobs, vec![]))
212214
}
213215
Lto::Thin |
214216
Lto::ThinLocal => {
@@ -220,7 +222,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
220222
&diag_handler,
221223
modules,
222224
upstream_modules,
223-
import_only_modules,
225+
cached_modules,
224226
&symbol_white_list,
225227
timeline)
226228
}
@@ -388,14 +390,19 @@ fn thin_lto(cgcx: &CodegenContext,
388390
diag_handler: &Handler,
389391
modules: Vec<ModuleCodegen>,
390392
serialized_modules: Vec<(SerializedModule, CString)>,
391-
import_only_modules: Vec<(SerializedModule, CString)>,
393+
cached_modules: Vec<(SerializedModule, WorkProduct)>,
392394
symbol_white_list: &[*const libc::c_char],
393395
timeline: &mut Timeline)
394-
-> Result<Vec<LtoModuleCodegen>, FatalError>
396+
-> Result<(Vec<LtoModuleCodegen>, Vec<WorkProduct>), FatalError>
395397
{
396398
unsafe {
397399
info!("going for that thin, thin LTO");
398400

401+
let green_modules: FxHashMap<_, _> = cached_modules
402+
.iter()
403+
.map(|&(_, ref wp)| (wp.cgu_name.clone(), wp.clone()))
404+
.collect();
405+
399406
let mut thin_buffers = Vec::new();
400407
let mut module_names = Vec::new();
401408
let mut thin_modules = Vec::new();
@@ -411,6 +418,28 @@ fn thin_lto(cgcx: &CodegenContext,
411418
info!("local module: {} - {}", i, module.name);
412419
let name = CString::new(module.name.clone()).unwrap();
413420
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+
414443
thin_modules.push(llvm::ThinLTOModule {
415444
identifier: name.as_ptr(),
416445
data: buffer.data().as_ptr(),
@@ -438,24 +467,13 @@ fn thin_lto(cgcx: &CodegenContext,
438467
// looking at upstream modules entirely sometimes (the contents,
439468
// we must always unconditionally look at the index).
440469
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())
447473
});
448-
serialized.push(module);
449-
module_names.push(name);
450-
}
451474

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);
459477
thin_modules.push(llvm::ThinLTOModule {
460478
identifier: name.as_ptr(),
461479
data: module.data().as_ptr(),
@@ -465,6 +483,9 @@ fn thin_lto(cgcx: &CodegenContext,
465483
module_names.push(name);
466484
}
467485

486+
// Sanity check
487+
assert_eq!(thin_modules.len(), module_names.len());
488+
468489
// Delegate to the C++ bindings to create some data here. Once this is a
469490
// tried-and-true interface we may wish to try to upstream some of this
470491
// to LLVM itself, right now we reimplement a lot of what they do
@@ -478,30 +499,7 @@ fn thin_lto(cgcx: &CodegenContext,
478499
write::llvm_err(&diag_handler, "failed to prepare thin LTO context".to_string())
479500
})?;
480501

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);
505503

506504
let data = ThinData(data);
507505
info!("thin LTO data created");
@@ -517,12 +515,36 @@ fn thin_lto(cgcx: &CodegenContext,
517515
serialized_modules: serialized,
518516
module_names,
519517
});
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 {
522542
shared: shared.clone(),
523-
idx: i,
524-
})
525-
}).collect())
543+
idx: module_index,
544+
}));
545+
}
546+
547+
Ok((opt_jobs, copy_jobs))
526548
}
527549
}
528550

@@ -850,44 +872,12 @@ pub struct ThinLTOImports {
850872
}
851873

852874
impl ThinLTOImports {
853-
pub fn new() -> ThinLTOImports {
854-
ThinLTOImports {
855-
imports: FxHashMap(),
856-
}
857-
}
858-
859875
pub fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] {
860876
self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[])
861877
}
862878

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-
879879
/// Load the ThinLTO import map from ThinLTOData.
880880
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-
}
891881
unsafe extern "C" fn imported_module_callback(payload: *mut libc::c_void,
892882
importing_module_name: *const libc::c_char,
893883
imported_module_name: *const libc::c_char) {
@@ -896,6 +886,7 @@ impl ThinLTOImports {
896886
let importing_module_name = module_name_to_str(&importing_module_name);
897887
let imported_module_name = CStr::from_ptr(imported_module_name);
898888
let imported_module_name = module_name_to_str(&imported_module_name);
889+
899890
if !map.imports.contains_key(importing_module_name) {
900891
map.imports.insert(importing_module_name.to_owned(), vec![]);
901892
}
@@ -913,47 +904,15 @@ impl ThinLTOImports {
913904
&mut map as *mut _ as *mut libc::c_void);
914905
map
915906
}
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(())
929907
}
930908

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)
954916
}
955-
Ok(ThinLTOImports {
956-
imports
957-
})
958917
}
959918
}

0 commit comments

Comments
 (0)