Skip to content

Commit e772c28

Browse files
committed
Auto merge of #43506 - michaelwoerister:async-llvm, r=alexcrichton
Run translation and LLVM in parallel when compiling with multiple CGUs This is still a work in progress but the bulk of the implementation is done, so I thought it would be good to get it in front of more eyes. This PR makes the compiler start running LLVM while translation is still in progress, effectively allowing for more parallelism towards the end of the compilation pipeline. It also allows the main thread to switch between either translation or running LLVM, which allows to reduce peak memory usage since not all LLVM module have to be kept in memory until linking. This is especially good for incr. comp. but it works just as well when running with `-Ccodegen-units=N`. In order to help tuning and debugging the work scheduler, the PR adds the `-Ztrans-time-graph` flag which spits out html files that show how work packages where scheduled: ![Building regex](https://user-images.githubusercontent.com/1825894/28679272-f6752bd8-72f2-11e7-8a6c-56207855ce95.png) (red is translation, green is llvm) One side effect here is that `-Ztime-passes` might show something not quite correct because trans and LLVM are not strictly separated anymore. I plan to have some special handling there that will try to produce useful output. One open question is how to determine whether the trans-thread should switch to intermediate LLVM processing. TODO: - [x] Restore `-Z time-passes` output for LLVM. - [x] Update documentation, esp. for work package scheduling. - [x] Tune the scheduling algorithm. cc @alexcrichton @rust-lang/compiler
2 parents c240751 + 6468cad commit e772c28

File tree

16 files changed

+1558
-653
lines changed

16 files changed

+1558
-653
lines changed

src/Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc/middle/cstore.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub use self::NativeLibraryKind::*;
5050

5151
// lonely orphan structs and enums looking for a better home
5252

53-
#[derive(Clone, Debug)]
53+
#[derive(Clone, Debug, Copy)]
5454
pub struct LinkMeta {
5555
pub crate_hash: Svh,
5656
}
@@ -161,15 +161,13 @@ pub struct ExternCrate {
161161
}
162162

163163
pub struct EncodedMetadata {
164-
pub raw_data: Vec<u8>,
165-
pub hashes: EncodedMetadataHashes,
164+
pub raw_data: Vec<u8>
166165
}
167166

168167
impl EncodedMetadata {
169168
pub fn new() -> EncodedMetadata {
170169
EncodedMetadata {
171170
raw_data: Vec::new(),
172-
hashes: EncodedMetadataHashes::new(),
173171
}
174172
}
175173
}
@@ -294,7 +292,7 @@ pub trait CrateStore {
294292
tcx: TyCtxt<'a, 'tcx, 'tcx>,
295293
link_meta: &LinkMeta,
296294
reachable: &NodeSet)
297-
-> EncodedMetadata;
295+
-> (EncodedMetadata, EncodedMetadataHashes);
298296
fn metadata_encoding_version(&self) -> &[u8];
299297
}
300298

@@ -424,7 +422,7 @@ impl CrateStore for DummyCrateStore {
424422
tcx: TyCtxt<'a, 'tcx, 'tcx>,
425423
link_meta: &LinkMeta,
426424
reachable: &NodeSet)
427-
-> EncodedMetadata {
425+
-> (EncodedMetadata, EncodedMetadataHashes) {
428426
bug!("encode_metadata")
429427
}
430428
fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") }

src/librustc/session/config.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10591059
"choose which RELRO level to use"),
10601060
nll: bool = (false, parse_bool, [UNTRACKED],
10611061
"run the non-lexical lifetimes MIR pass"),
1062+
trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
1063+
"generate a graphical HTML report of time spent in trans and LLVM"),
10621064
}
10631065

10641066
pub fn default_lib_output() -> CrateType {
@@ -1498,6 +1500,23 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
14981500
early_error(error_format, "Value for codegen units must be a positive nonzero integer");
14991501
}
15001502

1503+
// It's possible that we have `codegen_units > 1` but only one item in
1504+
// `trans.modules`. We could theoretically proceed and do LTO in that
1505+
// case, but it would be confusing to have the validity of
1506+
// `-Z lto -C codegen-units=2` depend on details of the crate being
1507+
// compiled, so we complain regardless.
1508+
if cg.lto && cg.codegen_units > 1 {
1509+
// This case is impossible to handle because LTO expects to be able
1510+
// to combine the entire crate and all its dependencies into a
1511+
// single compilation unit, but each codegen unit is in a separate
1512+
// LLVM context, so they can't easily be combined.
1513+
early_error(error_format, "can't perform LTO when using multiple codegen units");
1514+
}
1515+
1516+
if cg.lto && debugging_opts.incremental.is_some() {
1517+
early_error(error_format, "can't perform LTO when compiling incrementally");
1518+
}
1519+
15011520
let mut prints = Vec::<PrintRequest>::new();
15021521
if cg.target_cpu.as_ref().map_or(false, |s| s == "help") {
15031522
prints.push(PrintRequest::TargetCPUs);

src/librustc/util/common.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ pub fn time<T, F>(do_it: bool, what: &str, f: F) -> T where
5757
let rv = f();
5858
let dur = start.elapsed();
5959

60+
print_time_passes_entry_internal(what, dur);
61+
62+
TIME_DEPTH.with(|slot| slot.set(old));
63+
64+
rv
65+
}
66+
67+
pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) {
68+
if !do_it {
69+
return
70+
}
71+
72+
let old = TIME_DEPTH.with(|slot| {
73+
let r = slot.get();
74+
slot.set(r + 1);
75+
r
76+
});
77+
78+
print_time_passes_entry_internal(what, dur);
79+
80+
TIME_DEPTH.with(|slot| slot.set(old));
81+
}
82+
83+
fn print_time_passes_entry_internal(what: &str, dur: Duration) {
84+
let indentation = TIME_DEPTH.with(|slot| slot.get());
85+
6086
let mem_string = match get_resident() {
6187
Some(n) => {
6288
let mb = n as f64 / 1_000_000.0;
@@ -65,14 +91,10 @@ pub fn time<T, F>(do_it: bool, what: &str, f: F) -> T where
6591
None => "".to_owned(),
6692
};
6793
println!("{}time: {}{}\t{}",
68-
repeat(" ").take(old).collect::<String>(),
94+
repeat(" ").take(indentation).collect::<String>(),
6995
duration_to_secs_str(dur),
7096
mem_string,
7197
what);
72-
73-
TIME_DEPTH.with(|slot| slot.set(old));
74-
75-
rv
7698
}
7799

78100
// Hack up our own formatting for the duration to make it easier for scripts

src/librustc_driver/driver.rs

+12-52
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use rustc_data_structures::stable_hasher::StableHasher;
1515
use rustc_mir as mir;
1616
use rustc::session::{Session, CompileResult};
1717
use rustc::session::CompileIncomplete;
18-
use rustc::session::config::{self, Input, OutputFilenames, OutputType,
19-
OutputTypes};
18+
use rustc::session::config::{self, Input, OutputFilenames, OutputType};
2019
use rustc::session::search_paths::PathKind;
2120
use rustc::lint;
2221
use rustc::middle::{self, dependency_format, stability, reachable};
@@ -26,7 +25,6 @@ use rustc::ty::{self, TyCtxt, Resolutions, GlobalArenas};
2625
use rustc::traits;
2726
use rustc::util::common::{ErrorReported, time};
2827
use rustc::util::nodemap::NodeSet;
29-
use rustc::util::fs::rename_or_copy_remove;
3028
use rustc_allocator as allocator;
3129
use rustc_borrowck as borrowck;
3230
use rustc_incremental::{self, IncrementalHashesMap};
@@ -208,7 +206,7 @@ pub fn compile_input(sess: &Session,
208206
println!("Pre-trans");
209207
tcx.print_debug_stats();
210208
}
211-
let trans = phase_4_translate_to_llvm(tcx, analysis, &incremental_hashes_map,
209+
let trans = phase_4_translate_to_llvm(tcx, analysis, incremental_hashes_map,
212210
&outputs);
213211

214212
if log_enabled!(::log::LogLevel::Info) {
@@ -231,16 +229,14 @@ pub fn compile_input(sess: &Session,
231229
sess.code_stats.borrow().print_type_sizes();
232230
}
233231

234-
let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);
232+
let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans);
235233

236234
controller_entry_point!(after_llvm,
237235
sess,
238236
CompileState::state_after_llvm(input, sess, outdir, output, &trans),
239237
phase5_result);
240238
phase5_result?;
241239

242-
write::cleanup_llvm(&trans);
243-
244240
phase_6_link_output(sess, &trans, &outputs);
245241

246242
// Now that we won't touch anything in the incremental compilation directory
@@ -1055,9 +1051,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
10551051
/// be discarded.
10561052
pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10571053
analysis: ty::CrateAnalysis,
1058-
incremental_hashes_map: &IncrementalHashesMap,
1054+
incremental_hashes_map: IncrementalHashesMap,
10591055
output_filenames: &OutputFilenames)
1060-
-> trans::CrateTranslation {
1056+
-> write::OngoingCrateTranslation {
10611057
let time_passes = tcx.sess.time_passes();
10621058

10631059
time(time_passes,
@@ -1067,63 +1063,27 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10671063
let translation =
10681064
time(time_passes,
10691065
"translation",
1070-
move || trans::trans_crate(tcx, analysis, &incremental_hashes_map, output_filenames));
1071-
1072-
time(time_passes,
1073-
"assert dep graph",
1074-
|| rustc_incremental::assert_dep_graph(tcx));
1066+
move || trans::trans_crate(tcx, analysis, incremental_hashes_map, output_filenames));
10751067

1076-
time(time_passes,
1077-
"serialize dep graph",
1078-
|| rustc_incremental::save_dep_graph(tcx,
1079-
&incremental_hashes_map,
1080-
&translation.metadata.hashes,
1081-
translation.link.crate_hash));
10821068
translation
10831069
}
10841070

10851071
/// Run LLVM itself, producing a bitcode file, assembly file or object file
10861072
/// as a side effect.
10871073
pub fn phase_5_run_llvm_passes(sess: &Session,
1088-
trans: &trans::CrateTranslation,
1089-
outputs: &OutputFilenames) -> CompileResult {
1090-
if sess.opts.cg.no_integrated_as ||
1091-
(sess.target.target.options.no_integrated_as &&
1092-
(outputs.outputs.contains_key(&OutputType::Object) ||
1093-
outputs.outputs.contains_key(&OutputType::Exe)))
1094-
{
1095-
let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]);
1096-
time(sess.time_passes(),
1097-
"LLVM passes",
1098-
|| write::run_passes(sess, trans, &output_types, outputs));
1099-
1100-
write::run_assembler(sess, outputs);
1101-
1102-
// HACK the linker expects the object file to be named foo.0.o but
1103-
// `run_assembler` produces an object named just foo.o. Rename it if we
1104-
// are going to build an executable
1105-
if sess.opts.output_types.contains_key(&OutputType::Exe) {
1106-
let f = outputs.path(OutputType::Object);
1107-
rename_or_copy_remove(&f,
1108-
f.with_file_name(format!("{}.0.o",
1109-
f.file_stem().unwrap().to_string_lossy()))).unwrap();
1110-
}
1074+
trans: write::OngoingCrateTranslation)
1075+
-> (CompileResult, trans::CrateTranslation) {
1076+
let trans = trans.join(sess);
11111077

1112-
// Remove assembly source, unless --save-temps was specified
1113-
if !sess.opts.cg.save_temps {
1114-
fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap();
1115-
}
1116-
} else {
1117-
time(sess.time_passes(),
1118-
"LLVM passes",
1119-
|| write::run_passes(sess, trans, &sess.opts.output_types, outputs));
1078+
if sess.opts.debugging_opts.incremental_info {
1079+
write::dump_incremental_data(&trans);
11201080
}
11211081

11221082
time(sess.time_passes(),
11231083
"serialize work products",
11241084
move || rustc_incremental::save_work_products(sess));
11251085

1126-
sess.compile_status()
1086+
(sess.compile_status(), trans)
11271087
}
11281088

11291089
/// Run the linker on any artifacts that resulted from the LLVM run.

src/librustc_incremental/persist/save.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use super::file_format;
3434
use super::work_product;
3535

3636
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
37-
incremental_hashes_map: &IncrementalHashesMap,
37+
incremental_hashes_map: IncrementalHashesMap,
3838
metadata_hashes: &EncodedMetadataHashes,
3939
svh: Svh) {
4040
debug!("save_dep_graph()");
@@ -51,7 +51,7 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
5151
eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges());
5252
}
5353

54-
let mut hcx = HashContext::new(tcx, incremental_hashes_map);
54+
let mut hcx = HashContext::new(tcx, &incremental_hashes_map);
5555
let preds = Predecessors::new(&query, &mut hcx);
5656
let mut current_metadata_hashes = FxHashMap();
5757

src/librustc_metadata/cstore_impl.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use schema;
1515
use rustc::ty::maps::QueryConfig;
1616
use rustc::middle::cstore::{CrateStore, CrateSource, LibSource, DepKind,
1717
NativeLibrary, MetadataLoader, LinkMeta,
18-
LinkagePreference, LoadedMacro, EncodedMetadata};
18+
LinkagePreference, LoadedMacro, EncodedMetadata,
19+
EncodedMetadataHashes};
1920
use rustc::hir::def;
2021
use rustc::middle::lang_items;
2122
use rustc::session::Session;
@@ -443,7 +444,7 @@ impl CrateStore for cstore::CStore {
443444
tcx: TyCtxt<'a, 'tcx, 'tcx>,
444445
link_meta: &LinkMeta,
445446
reachable: &NodeSet)
446-
-> EncodedMetadata
447+
-> (EncodedMetadata, EncodedMetadataHashes)
447448
{
448449
encoder::encode_metadata(tcx, link_meta, reachable)
449450
}

src/librustc_metadata/encoder.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -1638,7 +1638,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'a, 'tcx> {
16381638
pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
16391639
link_meta: &LinkMeta,
16401640
exported_symbols: &NodeSet)
1641-
-> EncodedMetadata
1641+
-> (EncodedMetadata, EncodedMetadataHashes)
16421642
{
16431643
let mut cursor = Cursor::new(vec![]);
16441644
cursor.write_all(METADATA_HEADER).unwrap();
@@ -1681,10 +1681,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
16811681
result[header + 2] = (pos >> 8) as u8;
16821682
result[header + 3] = (pos >> 0) as u8;
16831683

1684-
EncodedMetadata {
1685-
raw_data: result,
1686-
hashes: metadata_hashes,
1687-
}
1684+
(EncodedMetadata { raw_data: result }, metadata_hashes)
16881685
}
16891686

16901687
pub fn get_repr_options<'a, 'tcx, 'gcx>(tcx: &TyCtxt<'a, 'tcx, 'gcx>, did: DefId) -> ReprOptions {

src/librustc_trans/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ crate-type = ["dylib"]
1010
test = false
1111

1212
[dependencies]
13-
crossbeam = "0.2"
13+
num_cpus = "1.0"
1414
flate2 = "0.2"
1515
jobserver = "0.1.5"
1616
log = "0.3"

0 commit comments

Comments
 (0)