Skip to content

Commit ee73f80

Browse files
committedSep 3, 2018
Auto merge of #53673 - michaelwoerister:incr-thinlto-2000, r=alexcrichton
Enable ThinLTO with incremental compilation. This is an updated version of #52309. This PR allows `rustc` to use (local) ThinLTO and incremental compilation at the same time. In theory this should allow for getting compile-time improvements for small changes while keeping the runtime performance of the generated code roughly the same as when compiling non-incrementally. The difference to #52309 is that this version also caches the pre-LTO version of LLVM bitcode. This allows for another layer of caching: 1. if the module itself has changed, we have to re-codegen and re-optimize. 2. if the module itself has not changed, but a module it imported from during ThinLTO has, we don't need to re-codegen and don't need to re-run the first optimization phase. Only the second (i.e. ThinLTO-) optimization phase is re-run. 3. if neither the module itself nor any of its imports have changed then we can re-use the final, post-ThinLTO version of the module. (We might have to load its pre-ThinLTO version though so it's available for other modules to import from)
·
1.89.01.30.0
2 parents 591a17d + 21d05f6 commit ee73f80

File tree

17 files changed

+647
-297
lines changed

17 files changed

+647
-297
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ pub struct WorkProduct {
878878
pub saved_files: Vec<(WorkProductFileKind, String)>,
879879
}
880880

881-
#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable)]
881+
#[derive(Clone, Copy, Debug, RustcEncodable, RustcDecodable, PartialEq)]
882882
pub enum WorkProductFileKind {
883883
Object,
884884
Bytecode,

‎src/librustc/session/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub enum OptLevel {
6868
SizeMin, // -Oz
6969
}
7070

71-
#[derive(Clone, Copy, PartialEq, Hash)]
71+
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
7272
pub enum Lto {
7373
/// Don't do any LTO whatsoever
7474
No,

‎src/librustc/session/mod.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,6 @@ impl Session {
580580
return config::Lto::No;
581581
}
582582

583-
// Right now ThinLTO isn't compatible with incremental compilation.
584-
if self.opts.incremental.is_some() {
585-
return config::Lto::No;
586-
}
587-
588583
// Now we're in "defaults" territory. By default we enable ThinLTO for
589584
// optimized compiles (anything greater than O0).
590585
match self.opts.optimize {
@@ -1177,8 +1172,18 @@ pub fn build_session_(
11771172
// commandline argument, you can do so here.
11781173
fn validate_commandline_args_with_session_available(sess: &Session) {
11791174

1180-
if sess.lto() != Lto::No && sess.opts.incremental.is_some() {
1181-
sess.err("can't perform LTO when compiling incrementally");
1175+
if sess.opts.incremental.is_some() {
1176+
match sess.lto() {
1177+
Lto::Yes |
1178+
Lto::Thin |
1179+
Lto::Fat => {
1180+
sess.err("can't perform LTO when compiling incrementally");
1181+
}
1182+
Lto::ThinLocal |
1183+
Lto::No => {
1184+
// This is fine
1185+
}
1186+
}
11821187
}
11831188

11841189
// Since we don't know if code in an rlib will be linked to statically or

‎src/librustc/ty/query/config.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -716,12 +716,6 @@ impl<'tcx> QueryDescription<'tcx> for queries::codegen_unit<'tcx> {
716716
}
717717
}
718718

719-
impl<'tcx> QueryDescription<'tcx> for queries::compile_codegen_unit<'tcx> {
720-
fn describe(_tcx: TyCtxt, _: InternedString) -> String {
721-
"compile_codegen_unit".to_string()
722-
}
723-
}
724-
725719
impl<'tcx> QueryDescription<'tcx> for queries::output_filenames<'tcx> {
726720
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
727721
"output_filenames".to_string()

‎src/librustc/ty/query/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use middle::lib_features::LibFeatures;
2828
use middle::lang_items::{LanguageItems, LangItem};
2929
use middle::exported_symbols::{SymbolExportLevel, ExportedSymbol};
3030
use mir::interpret::ConstEvalResult;
31-
use mir::mono::{CodegenUnit, Stats};
31+
use mir::mono::CodegenUnit;
3232
use mir;
3333
use mir::interpret::GlobalId;
3434
use session::{CompileResult, CrateDisambiguator};
@@ -525,7 +525,6 @@ define_queries! { <'tcx>
525525
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
526526
[] fn is_codegened_item: IsCodegenedItem(DefId) -> bool,
527527
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
528-
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,
529528
},
530529

531530
Other {

‎src/librustc_codegen_llvm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cc = "1.0.1"
1414
num_cpus = "1.0"
1515
rustc-demangle = "0.1.4"
1616
rustc_llvm = { path = "../librustc_llvm" }
17+
memmap = "0.6"
1718

1819
[features]
1920
# This is used to convince Cargo to separately cache builds of `rustc_codegen_llvm`

‎src/librustc_codegen_llvm/back/lto.rs

Lines changed: 181 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,25 @@
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;
19+
use memmap;
20+
use rustc::dep_graph::WorkProduct;
1921
use rustc::hir::def_id::LOCAL_CRATE;
2022
use rustc::middle::exported_symbols::SymbolExportLevel;
2123
use rustc::session::config::{self, Lto};
2224
use rustc::util::common::time_ext;
25+
use rustc_data_structures::fx::FxHashMap;
2326
use time_graph::Timeline;
24-
use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource};
27+
use {ModuleCodegen, ModuleLlvm, ModuleKind};
2528

2629
use libc;
2730

28-
use std::ffi::CString;
31+
use std::ffi::{CStr, CString};
32+
use std::fs::{self, File};
2933
use std::ptr;
3034
use std::slice;
3135
use std::sync::Arc;
@@ -75,8 +79,8 @@ impl LtoModuleCodegen {
7579
let module = module.take().unwrap();
7680
{
7781
let config = cgcx.config(module.kind);
78-
let llmod = module.llvm().unwrap().llmod();
79-
let tm = &*module.llvm().unwrap().tm;
82+
let llmod = module.module_llvm.llmod();
83+
let tm = &*module.module_llvm.tm;
8084
run_pass_manager(cgcx, tm, llmod, config, false);
8185
timeline.record("fat-done");
8286
}
@@ -97,10 +101,16 @@ impl LtoModuleCodegen {
97101
}
98102
}
99103

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.
100109
pub(crate) fn run(cgcx: &CodegenContext,
101110
modules: Vec<ModuleCodegen>,
111+
cached_modules: Vec<(SerializedModule, WorkProduct)>,
102112
timeline: &mut Timeline)
103-
-> Result<Vec<LtoModuleCodegen>, FatalError>
113+
-> Result<(Vec<LtoModuleCodegen>, Vec<WorkProduct>), FatalError>
104114
{
105115
let diag_handler = cgcx.create_diag_handler();
106116
let export_threshold = match cgcx.lto {
@@ -187,19 +197,34 @@ pub(crate) fn run(cgcx: &CodegenContext,
187197
}
188198
}
189199

190-
let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
200+
let symbol_white_list = symbol_white_list.iter()
201+
.map(|c| c.as_ptr())
202+
.collect::<Vec<_>>();
191203
match cgcx.lto {
192204
Lto::Yes | // `-C lto` == fat LTO by default
193205
Lto::Fat => {
194-
fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline)
206+
assert!(cached_modules.is_empty());
207+
let opt_jobs = fat_lto(cgcx,
208+
&diag_handler,
209+
modules,
210+
upstream_modules,
211+
&symbol_white_list,
212+
timeline);
213+
opt_jobs.map(|opt_jobs| (opt_jobs, vec![]))
195214
}
196215
Lto::Thin |
197216
Lto::ThinLocal => {
198217
if cgcx.opts.debugging_opts.cross_lang_lto.enabled() {
199218
unreachable!("We should never reach this case if the LTO step \
200219
is deferred to the linker");
201220
}
202-
thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline)
221+
thin_lto(cgcx,
222+
&diag_handler,
223+
modules,
224+
upstream_modules,
225+
cached_modules,
226+
&symbol_white_list,
227+
timeline)
203228
}
204229
Lto::No => unreachable!(),
205230
}
@@ -229,7 +254,7 @@ fn fat_lto(cgcx: &CodegenContext,
229254
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
230255
.map(|(i, module)| {
231256
let cost = unsafe {
232-
llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod())
257+
llvm::LLVMRustModuleCost(module.module_llvm.llmod())
233258
};
234259
(cost, i)
235260
})
@@ -239,7 +264,7 @@ fn fat_lto(cgcx: &CodegenContext,
239264
let mut serialized_bitcode = Vec::new();
240265
{
241266
let (llcx, llmod) = {
242-
let llvm = module.llvm().expect("can't lto pre-codegened modules");
267+
let llvm = &module.module_llvm;
243268
(&llvm.llcx, llvm.llmod())
244269
};
245270
info!("using {:?} as a base module", module.name);
@@ -255,8 +280,7 @@ fn fat_lto(cgcx: &CodegenContext,
255280
// way we know of to do that is to serialize them to a string and them parse
256281
// them later. Not great but hey, that's why it's "fat" LTO, right?
257282
for module in modules {
258-
let llvm = module.llvm().expect("can't lto pre-codegened modules");
259-
let buffer = ModuleBuffer::new(llvm.llmod());
283+
let buffer = ModuleBuffer::new(module.module_llvm.llmod());
260284
let llmod_id = CString::new(&module.name[..]).unwrap();
261285
serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
262286
}
@@ -362,16 +386,23 @@ impl Drop for Linker<'a> {
362386
/// calculating the *index* for ThinLTO. This index will then be shared amongst
363387
/// all of the `LtoModuleCodegen` units returned below and destroyed once
364388
/// they all go out of scope.
365-
fn thin_lto(diag_handler: &Handler,
389+
fn thin_lto(cgcx: &CodegenContext,
390+
diag_handler: &Handler,
366391
modules: Vec<ModuleCodegen>,
367392
serialized_modules: Vec<(SerializedModule, CString)>,
393+
cached_modules: Vec<(SerializedModule, WorkProduct)>,
368394
symbol_white_list: &[*const libc::c_char],
369395
timeline: &mut Timeline)
370-
-> Result<Vec<LtoModuleCodegen>, FatalError>
396+
-> Result<(Vec<LtoModuleCodegen>, Vec<WorkProduct>), FatalError>
371397
{
372398
unsafe {
373399
info!("going for that thin, thin LTO");
374400

401+
let green_modules: FxHashMap<_, _> = cached_modules
402+
.iter()
403+
.map(|&(_, ref wp)| (wp.cgu_name.clone(), wp.clone()))
404+
.collect();
405+
375406
let mut thin_buffers = Vec::new();
376407
let mut module_names = Vec::new();
377408
let mut thin_modules = Vec::new();
@@ -385,9 +416,24 @@ fn thin_lto(diag_handler: &Handler,
385416
// analysis!
386417
for (i, module) in modules.iter().enumerate() {
387418
info!("local module: {} - {}", i, module.name);
388-
let llvm = module.llvm().expect("can't lto precodegened module");
389419
let name = CString::new(module.name.clone()).unwrap();
390-
let buffer = ThinBuffer::new(llvm.llmod());
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+
let path = incr_comp_session_dir
427+
.join(pre_lto_bitcode_filename(&module.name));
428+
429+
fs::write(&path, buffer.data()).unwrap_or_else(|e| {
430+
panic!("Error writing pre-lto-bitcode file `{}`: {}",
431+
path.display(),
432+
e);
433+
});
434+
}
435+
}
436+
391437
thin_modules.push(llvm::ThinLTOModule {
392438
identifier: name.as_ptr(),
393439
data: buffer.data().as_ptr(),
@@ -415,8 +461,13 @@ fn thin_lto(diag_handler: &Handler,
415461
// looking at upstream modules entirely sometimes (the contents,
416462
// we must always unconditionally look at the index).
417463
let mut serialized = Vec::new();
418-
for (module, name) in serialized_modules {
419-
info!("foreign module {:?}", name);
464+
465+
let cached_modules = cached_modules.into_iter().map(|(sm, wp)| {
466+
(sm, CString::new(wp.cgu_name).unwrap())
467+
});
468+
469+
for (module, name) in serialized_modules.into_iter().chain(cached_modules) {
470+
info!("upstream or cached module {:?}", name);
420471
thin_modules.push(llvm::ThinLTOModule {
421472
identifier: name.as_ptr(),
422473
data: module.data().as_ptr(),
@@ -426,6 +477,9 @@ fn thin_lto(diag_handler: &Handler,
426477
module_names.push(name);
427478
}
428479

480+
// Sanity check
481+
assert_eq!(thin_modules.len(), module_names.len());
482+
429483
// Delegate to the C++ bindings to create some data here. Once this is a
430484
// tried-and-true interface we may wish to try to upstream some of this
431485
// to LLVM itself, right now we reimplement a lot of what they do
@@ -439,10 +493,22 @@ fn thin_lto(diag_handler: &Handler,
439493
write::llvm_err(&diag_handler, "failed to prepare thin LTO context".to_string())
440494
})?;
441495

442-
let data = ThinData(data);
443496
info!("thin LTO data created");
444497
timeline.record("data");
445498

499+
let import_map = if cgcx.incr_comp_session_dir.is_some() {
500+
ThinLTOImports::from_thin_lto_data(data)
501+
} else {
502+
// If we don't compile incrementally, we don't need to load the
503+
// import data from LLVM.
504+
assert!(green_modules.is_empty());
505+
ThinLTOImports::new()
506+
};
507+
info!("thin LTO import map loaded");
508+
timeline.record("import-map-loaded");
509+
510+
let data = ThinData(data);
511+
446512
// Throw our data in an `Arc` as we'll be sharing it across threads. We
447513
// also put all memory referenced by the C++ data (buffers, ids, etc)
448514
// into the arc as well. After this we'll create a thin module
@@ -453,12 +519,38 @@ fn thin_lto(diag_handler: &Handler,
453519
serialized_modules: serialized,
454520
module_names,
455521
});
456-
Ok((0..shared.module_names.len()).map(|i| {
457-
LtoModuleCodegen::Thin(ThinModule {
522+
523+
let mut copy_jobs = vec![];
524+
let mut opt_jobs = vec![];
525+
526+
info!("checking which modules can be-reused and which have to be re-optimized.");
527+
for (module_index, module_name) in shared.module_names.iter().enumerate() {
528+
let module_name = module_name_to_str(module_name);
529+
530+
// If the module hasn't changed and none of the modules it imports
531+
// from has changed, we can re-use the post-ThinLTO version of the
532+
// module.
533+
if green_modules.contains_key(module_name) {
534+
let imports_all_green = import_map.modules_imported_by(module_name)
535+
.iter()
536+
.all(|imported_module| green_modules.contains_key(imported_module));
537+
538+
if imports_all_green {
539+
let work_product = green_modules[module_name].clone();
540+
copy_jobs.push(work_product);
541+
info!(" - {}: re-used", module_name);
542+
continue
543+
}
544+
}
545+
546+
info!(" - {}: re-compiled", module_name);
547+
opt_jobs.push(LtoModuleCodegen::Thin(ThinModule {
458548
shared: shared.clone(),
459-
idx: i,
460-
})
461-
}).collect())
549+
idx: module_index,
550+
}));
551+
}
552+
553+
Ok((opt_jobs, copy_jobs))
462554
}
463555
}
464556

@@ -527,13 +619,15 @@ fn run_pass_manager(cgcx: &CodegenContext,
527619
pub enum SerializedModule {
528620
Local(ModuleBuffer),
529621
FromRlib(Vec<u8>),
622+
FromUncompressedFile(memmap::Mmap, File),
530623
}
531624

532625
impl SerializedModule {
533626
fn data(&self) -> &[u8] {
534627
match *self {
535628
SerializedModule::Local(ref m) => m.data(),
536629
SerializedModule::FromRlib(ref m) => m,
630+
SerializedModule::FromUncompressedFile(ref m, _) => m,
537631
}
538632
}
539633
}
@@ -663,16 +757,16 @@ impl ThinModule {
663757
write::llvm_err(&diag_handler, msg)
664758
})? as *const _;
665759
let module = ModuleCodegen {
666-
source: ModuleSource::Codegened(ModuleLlvm {
760+
module_llvm: ModuleLlvm {
667761
llmod_raw,
668762
llcx,
669763
tm,
670-
}),
764+
},
671765
name: self.name().to_string(),
672766
kind: ModuleKind::Regular,
673767
};
674768
{
675-
let llmod = module.llvm().unwrap().llmod();
769+
let llmod = module.module_llvm.llmod();
676770
cgcx.save_temp_bitcode(&module, "thin-lto-input");
677771

678772
// Before we do much else find the "main" `DICompileUnit` that we'll be
@@ -768,11 +862,69 @@ impl ThinModule {
768862
// little differently.
769863
info!("running thin lto passes over {}", module.name);
770864
let config = cgcx.config(module.kind);
771-
run_pass_manager(cgcx, module.llvm().unwrap().tm, llmod, config, true);
865+
run_pass_manager(cgcx, module.module_llvm.tm, llmod, config, true);
772866
cgcx.save_temp_bitcode(&module, "thin-lto-after-pm");
773867
timeline.record("thin-done");
774868
}
775869

776870
Ok(module)
777871
}
778872
}
873+
874+
#[derive(Debug)]
875+
pub struct ThinLTOImports {
876+
// key = llvm name of importing module, value = list of modules it imports from
877+
imports: FxHashMap<String, Vec<String>>,
878+
}
879+
880+
impl ThinLTOImports {
881+
fn new() -> ThinLTOImports {
882+
ThinLTOImports {
883+
imports: FxHashMap(),
884+
}
885+
}
886+
887+
fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] {
888+
self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[])
889+
}
890+
891+
/// Load the ThinLTO import map from ThinLTOData.
892+
unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports {
893+
unsafe extern "C" fn imported_module_callback(payload: *mut libc::c_void,
894+
importing_module_name: *const libc::c_char,
895+
imported_module_name: *const libc::c_char) {
896+
let map = &mut* (payload as *mut ThinLTOImports);
897+
let importing_module_name = CStr::from_ptr(importing_module_name);
898+
let importing_module_name = module_name_to_str(&importing_module_name);
899+
let imported_module_name = CStr::from_ptr(imported_module_name);
900+
let imported_module_name = module_name_to_str(&imported_module_name);
901+
902+
if !map.imports.contains_key(importing_module_name) {
903+
map.imports.insert(importing_module_name.to_owned(), vec![]);
904+
}
905+
906+
map.imports
907+
.get_mut(importing_module_name)
908+
.unwrap()
909+
.push(imported_module_name.to_owned());
910+
}
911+
let mut map = ThinLTOImports {
912+
imports: FxHashMap(),
913+
};
914+
llvm::LLVMRustGetThinLTOModuleImports(data,
915+
imported_module_callback,
916+
&mut map as *mut _ as *mut libc::c_void);
917+
map
918+
}
919+
}
920+
921+
fn module_name_to_str(c_str: &CStr) -> &str {
922+
match c_str.to_str() {
923+
Ok(s) => s,
924+
Err(e) => {
925+
bug!("Encountered non-utf8 LLVM module name `{}`: {}",
926+
c_str.to_string_lossy(),
927+
e)
928+
}
929+
}
930+
}

‎src/librustc_codegen_llvm/back/write.rs

Lines changed: 277 additions & 163 deletions
Large diffs are not rendered by default.

‎src/librustc_codegen_llvm/base.rs

Lines changed: 96 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
//! int) and rec(x=int, y=int, z=int) will have the same llvm::Type.
2525
2626
use super::ModuleLlvm;
27-
use super::ModuleSource;
2827
use super::ModuleCodegen;
2928
use super::ModuleKind;
29+
use super::CachedModuleCodegen;
3030

3131
use abi;
3232
use back::write::{self, OngoingCodegen};
@@ -40,12 +40,11 @@ use rustc::middle::cstore::{EncodedMetadata};
4040
use rustc::ty::{self, Ty, TyCtxt};
4141
use rustc::ty::layout::{self, Align, TyLayout, LayoutOf};
4242
use rustc::ty::query::Providers;
43-
use rustc::dep_graph::{DepNode, DepConstructor};
4443
use rustc::middle::cstore::{self, LinkagePreference};
4544
use rustc::middle::exported_symbols;
4645
use rustc::util::common::{time, print_time_passes_entry};
4746
use rustc::util::profiling::ProfileCategory;
48-
use rustc::session::config::{self, DebugInfo, EntryFnType};
47+
use rustc::session::config::{self, DebugInfo, EntryFnType, Lto};
4948
use rustc::session::Session;
5049
use rustc_incremental;
5150
use allocator;
@@ -698,6 +697,50 @@ pub fn iter_globals(llmod: &'ll llvm::Module) -> ValueIter<'ll> {
698697
}
699698
}
700699

700+
#[derive(Debug)]
701+
enum CguReUsable {
702+
PreLto,
703+
PostLto,
704+
No
705+
}
706+
707+
fn determine_cgu_reuse<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
708+
cgu: &CodegenUnit<'tcx>)
709+
-> CguReUsable {
710+
if !tcx.dep_graph.is_fully_enabled() {
711+
return CguReUsable::No
712+
}
713+
714+
let work_product_id = &cgu.work_product_id();
715+
if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
716+
// We don't have anything cached for this CGU. This can happen
717+
// if the CGU did not exist in the previous session.
718+
return CguReUsable::No
719+
}
720+
721+
// Try to mark the CGU as green. If it we can do so, it means that nothing
722+
// affecting the LLVM module has changed and we can re-use a cached version.
723+
// If we compile with any kind of LTO, this means we can re-use the bitcode
724+
// of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
725+
// know that later). If we are not doing LTO, there is only one optimized
726+
// version of each module, so we re-use that.
727+
let dep_node = cgu.codegen_dep_node(tcx);
728+
assert!(!tcx.dep_graph.dep_node_exists(&dep_node),
729+
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
730+
cgu.name());
731+
732+
if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() {
733+
// We can re-use either the pre- or the post-thinlto state
734+
if tcx.sess.lto() != Lto::No {
735+
CguReUsable::PreLto
736+
} else {
737+
CguReUsable::PostLto
738+
}
739+
} else {
740+
CguReUsable::No
741+
}
742+
}
743+
701744
pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
702745
rx: mpsc::Receiver<Box<dyn Any + Send>>)
703746
-> OngoingCodegen {
@@ -734,7 +777,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
734777

735778
let metadata_module = ModuleCodegen {
736779
name: metadata_cgu_name,
737-
source: ModuleSource::Codegened(metadata_llvm_module),
780+
module_llvm: metadata_llvm_module,
738781
kind: ModuleKind::Metadata,
739782
};
740783

@@ -823,7 +866,7 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
823866

824867
Some(ModuleCodegen {
825868
name: llmod_id,
826-
source: ModuleSource::Codegened(modules),
869+
module_llvm: modules,
827870
kind: ModuleKind::Allocator,
828871
})
829872
} else {
@@ -851,48 +894,40 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
851894
ongoing_codegen.wait_for_signal_to_codegen_item();
852895
ongoing_codegen.check_for_errors(tcx.sess);
853896

854-
// First, if incremental compilation is enabled, we try to re-use the
855-
// codegen unit from the cache.
856-
if tcx.dep_graph.is_fully_enabled() {
857-
let cgu_id = cgu.work_product_id();
858-
859-
// Check whether there is a previous work-product we can
860-
// re-use. Not only must the file exist, and the inputs not
861-
// be dirty, but the hash of the symbols we will generate must
862-
// be the same.
863-
if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) {
864-
let dep_node = &DepNode::new(tcx,
865-
DepConstructor::CompileCodegenUnit(cgu.name().clone()));
866-
867-
// We try to mark the DepNode::CompileCodegenUnit green. If we
868-
// succeed it means that none of the dependencies has changed
869-
// and we can safely re-use.
870-
if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) {
871-
let module = ModuleCodegen {
872-
name: cgu.name().to_string(),
873-
source: ModuleSource::Preexisting(buf),
874-
kind: ModuleKind::Regular,
875-
};
876-
tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true);
877-
write::submit_codegened_module_to_llvm(tcx, module, 0);
878-
// Continue to next cgu, this one is done.
879-
continue
880-
}
881-
} else {
882-
// This can happen if files were deleted from the cache
883-
// directory for some reason. We just re-compile then.
897+
let loaded_from_cache = match determine_cgu_reuse(tcx, &cgu) {
898+
CguReUsable::No => {
899+
let _timing_guard = time_graph.as_ref().map(|time_graph| {
900+
time_graph.start(write::CODEGEN_WORKER_TIMELINE,
901+
write::CODEGEN_WORK_PACKAGE_KIND,
902+
&format!("codegen {}", cgu.name()))
903+
});
904+
let start_time = Instant::now();
905+
let stats = compile_codegen_unit(tcx, *cgu.name());
906+
all_stats.extend(stats);
907+
total_codegen_time += start_time.elapsed();
908+
false
884909
}
885-
}
910+
CguReUsable::PreLto => {
911+
write::submit_pre_lto_module_to_llvm(tcx, CachedModuleCodegen {
912+
name: cgu.name().to_string(),
913+
source: cgu.work_product(tcx),
914+
});
915+
true
916+
}
917+
CguReUsable::PostLto => {
918+
write::submit_post_lto_module_to_llvm(tcx, CachedModuleCodegen {
919+
name: cgu.name().to_string(),
920+
source: cgu.work_product(tcx),
921+
});
922+
true
923+
}
924+
};
886925

887-
let _timing_guard = time_graph.as_ref().map(|time_graph| {
888-
time_graph.start(write::CODEGEN_WORKER_TIMELINE,
889-
write::CODEGEN_WORK_PACKAGE_KIND,
890-
&format!("codegen {}", cgu.name()))
891-
});
892-
let start_time = Instant::now();
893-
all_stats.extend(tcx.compile_codegen_unit(*cgu.name()));
894-
total_codegen_time += start_time.elapsed();
895-
ongoing_codegen.check_for_errors(tcx.sess);
926+
if tcx.dep_graph.is_fully_enabled() {
927+
let dep_node = cgu.codegen_dep_node(tcx);
928+
let dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node);
929+
tcx.dep_graph.mark_loaded_from_cache(dep_node_index, loaded_from_cache);
930+
}
896931
}
897932

898933
ongoing_codegen.codegen_finished(tcx);
@@ -1156,11 +1191,15 @@ fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool {
11561191
}
11571192

11581193
fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1159-
cgu: InternedString) -> Stats {
1160-
let cgu = tcx.codegen_unit(cgu);
1161-
1194+
cgu_name: InternedString)
1195+
-> Stats {
11621196
let start_time = Instant::now();
1163-
let (stats, module) = module_codegen(tcx, cgu);
1197+
1198+
let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx);
1199+
let ((stats, module), _) = tcx.dep_graph.with_task(dep_node,
1200+
tcx,
1201+
cgu_name,
1202+
module_codegen);
11641203
let time_to_codegen = start_time.elapsed();
11651204

11661205
// We assume that the cost to run LLVM on a CGU is proportional to
@@ -1169,23 +1208,23 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
11691208
time_to_codegen.subsec_nanos() as u64;
11701209

11711210
write::submit_codegened_module_to_llvm(tcx,
1172-
module,
1173-
cost);
1211+
module,
1212+
cost);
11741213
return stats;
11751214

11761215
fn module_codegen<'a, 'tcx>(
11771216
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1178-
cgu: Arc<CodegenUnit<'tcx>>)
1217+
cgu_name: InternedString)
11791218
-> (Stats, ModuleCodegen)
11801219
{
1181-
let cgu_name = cgu.name().to_string();
1220+
let cgu = tcx.codegen_unit(cgu_name);
11821221

11831222
// Instantiate monomorphizations without filling out definitions yet...
1184-
let llvm_module = ModuleLlvm::new(tcx.sess, &cgu_name);
1223+
let llvm_module = ModuleLlvm::new(tcx.sess, &cgu_name.as_str());
11851224
let stats = {
11861225
let cx = CodegenCx::new(tcx, cgu, &llvm_module);
11871226
let mono_items = cx.codegen_unit
1188-
.items_in_deterministic_order(cx.tcx);
1227+
.items_in_deterministic_order(cx.tcx);
11891228
for &(mono_item, (linkage, visibility)) in &mono_items {
11901229
mono_item.predefine(&cx, linkage, visibility);
11911230
}
@@ -1234,8 +1273,8 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12341273
};
12351274

12361275
(stats, ModuleCodegen {
1237-
name: cgu_name,
1238-
source: ModuleSource::Codegened(llvm_module),
1276+
name: cgu_name.to_string(),
1277+
module_llvm: llvm_module,
12391278
kind: ModuleKind::Regular,
12401279
})
12411280
}
@@ -1254,7 +1293,6 @@ pub fn provide(providers: &mut Providers) {
12541293
.cloned()
12551294
.unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
12561295
};
1257-
providers.compile_codegen_unit = compile_codegen_unit;
12581296

12591297
provide_extern(providers);
12601298
}

‎src/librustc_codegen_llvm/lib.rs

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ extern crate rustc_errors as errors;
6666
extern crate serialize;
6767
extern crate cc; // Used to locate MSVC
6868
extern crate tempfile;
69+
extern crate memmap;
6970

7071
use back::bytecode::RLIB_BYTECODE_EXTENSION;
7172

7273
pub use llvm_util::target_features;
73-
7474
use std::any::Any;
75-
use std::path::PathBuf;
75+
use std::path::{PathBuf};
7676
use std::sync::mpsc;
7777
use rustc_data_structures::sync::Lrc;
7878

@@ -100,7 +100,7 @@ mod back {
100100
mod command;
101101
pub mod linker;
102102
pub mod link;
103-
mod lto;
103+
pub mod lto;
104104
pub mod symbol_export;
105105
pub mod write;
106106
mod rpath;
@@ -273,10 +273,15 @@ struct ModuleCodegen {
273273
/// as the crate name and disambiguator.
274274
/// We currently generate these names via CodegenUnit::build_cgu_name().
275275
name: String,
276-
source: ModuleSource,
276+
module_llvm: ModuleLlvm,
277277
kind: ModuleKind,
278278
}
279279

280+
struct CachedModuleCodegen {
281+
name: String,
282+
source: WorkProduct,
283+
}
284+
280285
#[derive(Copy, Clone, Debug, PartialEq)]
281286
enum ModuleKind {
282287
Regular,
@@ -285,22 +290,11 @@ enum ModuleKind {
285290
}
286291

287292
impl ModuleCodegen {
288-
fn llvm(&self) -> Option<&ModuleLlvm> {
289-
match self.source {
290-
ModuleSource::Codegened(ref llvm) => Some(llvm),
291-
ModuleSource::Preexisting(_) => None,
292-
}
293-
}
294-
295293
fn into_compiled_module(self,
296-
emit_obj: bool,
297-
emit_bc: bool,
298-
emit_bc_compressed: bool,
299-
outputs: &OutputFilenames) -> CompiledModule {
300-
let pre_existing = match self.source {
301-
ModuleSource::Preexisting(_) => true,
302-
ModuleSource::Codegened(_) => false,
303-
};
294+
emit_obj: bool,
295+
emit_bc: bool,
296+
emit_bc_compressed: bool,
297+
outputs: &OutputFilenames) -> CompiledModule {
304298
let object = if emit_obj {
305299
Some(outputs.temp_path(OutputType::Object, Some(&self.name)))
306300
} else {
@@ -321,7 +315,6 @@ impl ModuleCodegen {
321315
CompiledModule {
322316
name: self.name.clone(),
323317
kind: self.kind,
324-
pre_existing,
325318
object,
326319
bytecode,
327320
bytecode_compressed,
@@ -333,20 +326,11 @@ impl ModuleCodegen {
333326
struct CompiledModule {
334327
name: String,
335328
kind: ModuleKind,
336-
pre_existing: bool,
337329
object: Option<PathBuf>,
338330
bytecode: Option<PathBuf>,
339331
bytecode_compressed: Option<PathBuf>,
340332
}
341333

342-
enum ModuleSource {
343-
/// Copy the `.o` files or whatever from the incr. comp. directory.
344-
Preexisting(WorkProduct),
345-
346-
/// Rebuild from this LLVM module.
347-
Codegened(ModuleLlvm),
348-
}
349-
350334
struct ModuleLlvm {
351335
llcx: &'static mut llvm::Context,
352336
llmod_raw: *const llvm::Module,

‎src/librustc_codegen_llvm/llvm/ffi.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ extern { pub type ThinLTOData; }
363363
/// LLVMRustThinLTOBuffer
364364
extern { pub type ThinLTOBuffer; }
365365

366+
// LLVMRustModuleNameCallback
367+
pub type ThinLTOModuleNameCallback =
368+
unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char);
369+
366370
/// LLVMRustThinLTOModule
367371
#[repr(C)]
368372
pub struct ThinLTOModule {
@@ -1622,6 +1626,11 @@ extern "C" {
16221626
Data: &ThinLTOData,
16231627
Module: &Module,
16241628
) -> bool;
1629+
pub fn LLVMRustGetThinLTOModuleImports(
1630+
Data: *const ThinLTOData,
1631+
ModuleNameCallback: ThinLTOModuleNameCallback,
1632+
CallbackPayload: *mut c_void,
1633+
);
16251634
pub fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData);
16261635
pub fn LLVMRustParseBitcodeForThinLTO(
16271636
Context: &Context,

‎src/librustc_incremental/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub use persist::copy_cgu_workproducts_to_incr_comp_cache_dir;
4444
pub use persist::save_dep_graph;
4545
pub use persist::save_work_product_index;
4646
pub use persist::in_incr_comp_dir;
47+
pub use persist::in_incr_comp_dir_sess;
4748
pub use persist::prepare_session_directory;
4849
pub use persist::finalize_session_directory;
4950
pub use persist::delete_workproduct_files;

‎src/librustc_incremental/persist/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod file_format;
2323
pub use self::fs::finalize_session_directory;
2424
pub use self::fs::garbage_collect_session_directories;
2525
pub use self::fs::in_incr_comp_dir;
26+
pub use self::fs::in_incr_comp_dir_sess;
2627
pub use self::fs::prepare_session_directory;
2728
pub use self::load::dep_graph_tcx_init;
2829
pub use self::load::load_dep_graph;

‎src/librustc_mir/monomorphize/partitioning.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
//! inlining, even when they are not marked #[inline].
104104
105105
use monomorphize::collector::InliningMap;
106-
use rustc::dep_graph::WorkProductId;
106+
use rustc::dep_graph::{WorkProductId, WorkProduct, DepNode, DepConstructor};
107107
use rustc::hir::CodegenFnAttrFlags;
108108
use rustc::hir::def_id::{DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
109109
use rustc::hir::map::DefPathData;
@@ -150,6 +150,15 @@ pub trait CodegenUnitExt<'tcx> {
150150
WorkProductId::from_cgu_name(&self.name().as_str())
151151
}
152152

153+
fn work_product(&self, tcx: TyCtxt) -> WorkProduct {
154+
let work_product_id = self.work_product_id();
155+
tcx.dep_graph
156+
.previous_work_product(&work_product_id)
157+
.unwrap_or_else(|| {
158+
panic!("Could not find work-product for CGU `{}`", self.name())
159+
})
160+
}
161+
153162
fn items_in_deterministic_order<'a>(&self,
154163
tcx: TyCtxt<'a, 'tcx, 'tcx>)
155164
-> Vec<(MonoItem<'tcx>,
@@ -194,6 +203,10 @@ pub trait CodegenUnitExt<'tcx> {
194203
items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i));
195204
items
196205
}
206+
207+
fn codegen_dep_node(&self, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> DepNode {
208+
DepNode::new(tcx, DepConstructor::CompileCodegenUnit(self.name().clone()))
209+
}
197210
}
198211

199212
impl<'tcx> CodegenUnitExt<'tcx> for CodegenUnit<'tcx> {

‎src/rustllvm/PassWrapper.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,28 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
11231123
return true;
11241124
}
11251125

1126+
extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload
1127+
const char*, // importing module name
1128+
const char*); // imported module name
1129+
1130+
// Calls `module_name_callback` for each module import done by ThinLTO.
1131+
// The callback is provided with regular null-terminated C strings.
1132+
extern "C" void
1133+
LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data,
1134+
LLVMRustModuleNameCallback module_name_callback,
1135+
void* callback_payload) {
1136+
for (const auto& importing_module : data->ImportLists) {
1137+
const std::string importing_module_id = importing_module.getKey().str();
1138+
const auto& imports = importing_module.getValue();
1139+
for (const auto& imported_module : imports) {
1140+
const std::string imported_module_id = imported_module.getKey().str();
1141+
module_name_callback(callback_payload,
1142+
importing_module_id.c_str(),
1143+
imported_module_id.c_str());
1144+
}
1145+
}
1146+
}
1147+
11261148
// This struct and various functions are sort of a hack right now, but the
11271149
// problem is that we've got in-memory LLVM modules after we generate and
11281150
// optimize all codegen-units for one compilation in rustc. To be compatible
@@ -1288,6 +1310,11 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
12881310
report_fatal_error("ThinLTO not available");
12891311
}
12901312

1313+
extern "C" LLVMRustThinLTOModuleImports
1314+
LLVMRustGetLLVMRustThinLTOModuleImports(const LLVMRustThinLTOData *Data) {
1315+
report_fatal_error("ThinLTO not available");
1316+
}
1317+
12911318
extern "C" void
12921319
LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
12931320
report_fatal_error("ThinLTO not available");

‎src/tools/tidy/src/deps.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ static WHITELIST: &'static [Crate] = &[
9494
Crate("log"),
9595
Crate("log_settings"),
9696
Crate("memchr"),
97+
Crate("memmap"),
9798
Crate("memoffset"),
9899
Crate("miniz-sys"),
99100
Crate("nodrop"),

0 commit comments

Comments
 (0)
Please sign in to comment.