Skip to content

Commit 64a738d

Browse files
Support local ThinLTO with incremental compilation.
1 parent 72c1993 commit 64a738d

File tree

10 files changed

+634
-266
lines changed

10 files changed

+634
-266
lines changed

src/librustc/dep_graph/graph.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -878,11 +878,12 @@ 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,
885885
BytecodeCompressed,
886+
PreThinLtoBytecode,
886887
}
887888

888889
pub(super) struct CurrentDepGraph {

src/librustc/session/config.rs

+1-1
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

+12-7
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_codegen_llvm/Cargo.toml

+1
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

+88-20
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ use errors::{FatalError, Handler};
1616
use llvm::archive_ro::ArchiveRO;
1717
use llvm::{True, False};
1818
use llvm;
19+
use memmap;
1920
use rustc::hir::def_id::LOCAL_CRATE;
2021
use rustc::middle::exported_symbols::SymbolExportLevel;
2122
use rustc::session::config::{self, Lto};
2223
use rustc::util::common::time_ext;
23-
use rustc_data_structures::fx::FxHashMap;
24+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2425
use time_graph::Timeline;
25-
use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource};
26+
use {ModuleCodegen, ModuleLlvm, ModuleKind};
2627

2728
use libc;
2829

@@ -82,8 +83,8 @@ impl LtoModuleCodegen {
8283
let module = module.take().unwrap();
8384
{
8485
let config = cgcx.config(module.kind);
85-
let llmod = module.llvm().unwrap().llmod();
86-
let tm = &*module.llvm().unwrap().tm;
86+
let llmod = module.module_llvm.llmod();
87+
let tm = &*module.module_llvm.tm;
8788
run_pass_manager(cgcx, tm, llmod, config, false);
8889
timeline.record("fat-done");
8990
}
@@ -106,6 +107,7 @@ impl LtoModuleCodegen {
106107

107108
pub(crate) fn run(cgcx: &CodegenContext,
108109
modules: Vec<ModuleCodegen>,
110+
import_only_modules: Vec<(SerializedModule, CString)>,
109111
timeline: &mut Timeline)
110112
-> Result<Vec<LtoModuleCodegen>, FatalError>
111113
{
@@ -194,19 +196,33 @@ pub(crate) fn run(cgcx: &CodegenContext,
194196
}
195197
}
196198

197-
let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
199+
let symbol_white_list = symbol_white_list.iter()
200+
.map(|c| c.as_ptr())
201+
.collect::<Vec<_>>();
198202
match cgcx.lto {
199203
Lto::Yes | // `-C lto` == fat LTO by default
200204
Lto::Fat => {
201-
fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline)
205+
assert!(import_only_modules.is_empty());
206+
fat_lto(cgcx,
207+
&diag_handler,
208+
modules,
209+
upstream_modules,
210+
&symbol_white_list,
211+
timeline)
202212
}
203213
Lto::Thin |
204214
Lto::ThinLocal => {
205215
if cgcx.opts.debugging_opts.cross_lang_lto.enabled() {
206216
unreachable!("We should never reach this case if the LTO step \
207217
is deferred to the linker");
208218
}
209-
thin_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline)
219+
thin_lto(cgcx,
220+
&diag_handler,
221+
modules,
222+
upstream_modules,
223+
import_only_modules,
224+
&symbol_white_list,
225+
timeline)
210226
}
211227
Lto::No => unreachable!(),
212228
}
@@ -236,7 +252,7 @@ fn fat_lto(cgcx: &CodegenContext,
236252
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
237253
.map(|(i, module)| {
238254
let cost = unsafe {
239-
llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod())
255+
llvm::LLVMRustModuleCost(module.module_llvm.llmod())
240256
};
241257
(cost, i)
242258
})
@@ -246,7 +262,7 @@ fn fat_lto(cgcx: &CodegenContext,
246262
let mut serialized_bitcode = Vec::new();
247263
{
248264
let (llcx, llmod) = {
249-
let llvm = module.llvm().expect("can't lto pre-codegened modules");
265+
let llvm = &module.module_llvm;
250266
(&llvm.llcx, llvm.llmod())
251267
};
252268
info!("using {:?} as a base module", module.name);
@@ -262,8 +278,7 @@ fn fat_lto(cgcx: &CodegenContext,
262278
// way we know of to do that is to serialize them to a string and them parse
263279
// them later. Not great but hey, that's why it's "fat" LTO, right?
264280
for module in modules {
265-
let llvm = module.llvm().expect("can't lto pre-codegened modules");
266-
let buffer = ModuleBuffer::new(llvm.llmod());
281+
let buffer = ModuleBuffer::new(module.module_llvm.llmod());
267282
let llmod_id = CString::new(&module.name[..]).unwrap();
268283
serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
269284
}
@@ -373,6 +388,7 @@ fn thin_lto(cgcx: &CodegenContext,
373388
diag_handler: &Handler,
374389
modules: Vec<ModuleCodegen>,
375390
serialized_modules: Vec<(SerializedModule, CString)>,
391+
import_only_modules: Vec<(SerializedModule, CString)>,
376392
symbol_white_list: &[*const libc::c_char],
377393
timeline: &mut Timeline)
378394
-> Result<Vec<LtoModuleCodegen>, FatalError>
@@ -393,9 +409,8 @@ fn thin_lto(cgcx: &CodegenContext,
393409
// analysis!
394410
for (i, module) in modules.iter().enumerate() {
395411
info!("local module: {} - {}", i, module.name);
396-
let llvm = module.llvm().expect("can't lto precodegened module");
397412
let name = CString::new(module.name.clone()).unwrap();
398-
let buffer = ThinBuffer::new(llvm.llmod());
413+
let buffer = ThinBuffer::new(module.module_llvm.llmod());
399414
thin_modules.push(llvm::ThinLTOModule {
400415
identifier: name.as_ptr(),
401416
data: buffer.data().as_ptr(),
@@ -434,6 +449,22 @@ fn thin_lto(cgcx: &CodegenContext,
434449
module_names.push(name);
435450
}
436451

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);
459+
thin_modules.push(llvm::ThinLTOModule {
460+
identifier: name.as_ptr(),
461+
data: module.data().as_ptr(),
462+
len: module.data().len(),
463+
});
464+
serialized.push(module);
465+
module_names.push(name);
466+
}
467+
437468
// Delegate to the C++ bindings to create some data here. Once this is a
438469
// tried-and-true interface we may wish to try to upstream some of this
439470
// to LLVM itself, right now we reimplement a lot of what they do
@@ -450,7 +481,21 @@ fn thin_lto(cgcx: &CodegenContext,
450481
// Save the ThinLTO import information for incremental compilation.
451482
if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir {
452483
let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME);
453-
let imports = ThinLTOImports::from_thin_lto_data(data);
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+
454499
if let Err(err) = imports.save_to_file(&path) {
455500
let msg = format!("Error while writing ThinLTO import data: {}",
456501
err);
@@ -472,7 +517,7 @@ fn thin_lto(cgcx: &CodegenContext,
472517
serialized_modules: serialized,
473518
module_names,
474519
});
475-
Ok((0..shared.module_names.len()).map(|i| {
520+
Ok((0..modules_to_optimize).map(|i| {
476521
LtoModuleCodegen::Thin(ThinModule {
477522
shared: shared.clone(),
478523
idx: i,
@@ -546,13 +591,15 @@ fn run_pass_manager(cgcx: &CodegenContext,
546591
pub enum SerializedModule {
547592
Local(ModuleBuffer),
548593
FromRlib(Vec<u8>),
594+
FromUncompressedFile(memmap::Mmap, File),
549595
}
550596

551597
impl SerializedModule {
552598
fn data(&self) -> &[u8] {
553599
match *self {
554600
SerializedModule::Local(ref m) => m.data(),
555601
SerializedModule::FromRlib(ref m) => m,
602+
SerializedModule::FromUncompressedFile(ref m, _) => m,
556603
}
557604
}
558605
}
@@ -682,16 +729,16 @@ impl ThinModule {
682729
write::llvm_err(&diag_handler, msg)
683730
})? as *const _;
684731
let module = ModuleCodegen {
685-
source: ModuleSource::Codegened(ModuleLlvm {
732+
module_llvm: ModuleLlvm {
686733
llmod_raw,
687734
llcx,
688735
tm,
689-
}),
736+
},
690737
name: self.name().to_string(),
691738
kind: ModuleKind::Regular,
692739
};
693740
{
694-
let llmod = module.llvm().unwrap().llmod();
741+
let llmod = module.module_llvm.llmod();
695742
cgcx.save_temp_bitcode(&module, "thin-lto-input");
696743

697744
// Before we do much else find the "main" `DICompileUnit` that we'll be
@@ -787,7 +834,7 @@ impl ThinModule {
787834
// little differently.
788835
info!("running thin lto passes over {}", module.name);
789836
let config = cgcx.config(module.kind);
790-
run_pass_manager(cgcx, module.llvm().unwrap().tm, llmod, config, true);
837+
run_pass_manager(cgcx, module.module_llvm.tm, llmod, config, true);
791838
cgcx.save_temp_bitcode(&module, "thin-lto-after-pm");
792839
timeline.record("thin-done");
793840
}
@@ -809,6 +856,26 @@ impl ThinLTOImports {
809856
}
810857
}
811858

859+
pub fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] {
860+
self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[])
861+
}
862+
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+
812879
/// Load the ThinLTO import map from ThinLTOData.
813880
unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports {
814881
fn module_name_to_str(c_str: &CStr) -> &str {
@@ -832,6 +899,7 @@ impl ThinLTOImports {
832899
if !map.imports.contains_key(importing_module_name) {
833900
map.imports.insert(importing_module_name.to_owned(), vec![]);
834901
}
902+
835903
map.imports
836904
.get_mut(importing_module_name)
837905
.unwrap()
@@ -888,4 +956,4 @@ impl ThinLTOImports {
888956
imports
889957
})
890958
}
891-
}
959+
}

0 commit comments

Comments
 (0)