From c4adeceb37e4bdf9be3b70ff2454b121531466ce Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 21 Jul 2017 15:14:21 +0200 Subject: [PATCH 01/29] async-llvm(1): Run LLVM already in trans_crate(). --- src/librustc_driver/driver.rs | 57 +++++---------------------- src/librustc_trans/back/write.rs | 12 ++---- src/librustc_trans/base.rs | 66 ++++++++++++++++++++++++-------- src/librustc_trans/lib.rs | 66 +++++++++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 72 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index c592882a1e43b..1bc3f59ed0476 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -15,8 +15,7 @@ use rustc_data_structures::stable_hasher::StableHasher; use rustc_mir as mir; use rustc::session::{Session, CompileResult}; use rustc::session::CompileIncomplete; -use rustc::session::config::{self, Input, OutputFilenames, OutputType, - OutputTypes}; +use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc::lint; use rustc::middle::{self, dependency_format, stability, reachable}; @@ -26,7 +25,6 @@ use rustc::ty::{self, TyCtxt, Resolutions, GlobalArenas}; use rustc::traits; use rustc::util::common::{ErrorReported, time}; use rustc::util::nodemap::NodeSet; -use rustc::util::fs::rename_or_copy_remove; use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_incremental::{self, IncrementalHashesMap}; @@ -231,7 +229,7 @@ pub fn compile_input(sess: &Session, sess.code_stats.borrow().print_type_sizes(); } - let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs); + let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans, &outputs); controller_entry_point!(after_llvm, sess, @@ -1057,7 +1055,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, incremental_hashes_map: &IncrementalHashesMap, output_filenames: &OutputFilenames) - -> trans::CrateTranslation { + -> trans::OngoingCrateTranslation { let time_passes = tcx.sess.time_passes(); time(time_passes, @@ -1069,61 +1067,26 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "translation", move || trans::trans_crate(tcx, analysis, &incremental_hashes_map, output_filenames)); - time(time_passes, - "assert dep graph", - || rustc_incremental::assert_dep_graph(tcx)); - - time(time_passes, - "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx, - &incremental_hashes_map, - &translation.metadata.hashes, - translation.link.crate_hash)); translation } /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: &trans::CrateTranslation, - outputs: &OutputFilenames) -> CompileResult { - if sess.opts.cg.no_integrated_as || - (sess.target.target.options.no_integrated_as && - (outputs.outputs.contains_key(&OutputType::Object) || - outputs.outputs.contains_key(&OutputType::Exe))) - { - let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); - time(sess.time_passes(), - "LLVM passes", - || write::run_passes(sess, trans, &output_types, outputs)); - - write::run_assembler(sess, outputs); - - // HACK the linker expects the object file to be named foo.0.o but - // `run_assembler` produces an object named just foo.o. Rename it if we - // are going to build an executable - if sess.opts.output_types.contains_key(&OutputType::Exe) { - let f = outputs.path(OutputType::Object); - rename_or_copy_remove(&f, - f.with_file_name(format!("{}.0.o", - f.file_stem().unwrap().to_string_lossy()))).unwrap(); - } + trans: trans::OngoingCrateTranslation, + outputs: &OutputFilenames) + -> (CompileResult, trans::CrateTranslation) { + let trans = trans.join(sess, outputs); - // Remove assembly source, unless --save-temps was specified - if !sess.opts.cg.save_temps { - fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap(); - } - } else { - time(sess.time_passes(), - "LLVM passes", - || write::run_passes(sess, trans, &sess.opts.output_types, outputs)); + if sess.opts.debugging_opts.incremental_info { + write::dump_incremental_data(&trans); } time(sess.time_passes(), "serialize work products", move || rustc_incremental::save_work_products(sess)); - sess.compile_status() + (sess.compile_status(), trans) } /// Run the linker on any artifacts that resulted from the LLVM run. diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 26553c85023b7..4af4ee664a257 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -18,7 +18,7 @@ use rustc::session::Session; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; -use {CrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation}; +use {CrateTranslation, OngoingCrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation}; use rustc::hir::def_id::CrateNum; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; use rustc::util::fs::link_or_copy; @@ -255,7 +255,7 @@ impl ModuleConfig { } } - fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { + fn set_flags(&mut self, sess: &Session, trans: &OngoingCrateTranslation) { self.no_verify = sess.no_verify(); self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; self.no_builtins = trans.no_builtins; @@ -614,7 +614,7 @@ pub fn cleanup_llvm(trans: &CrateTranslation) { } pub fn run_passes(sess: &Session, - trans: &CrateTranslation, + trans: &OngoingCrateTranslation, output_types: &OutputTypes, crate_output: &OutputFilenames) { // It's possible that we have `codegen_units > 1` but only one item in @@ -748,10 +748,6 @@ pub fn run_passes(sess: &Session, work_items.push(work); } - if sess.opts.debugging_opts.incremental_info { - dump_incremental_data(&trans); - } - let client = sess.jobserver_from_env.clone().unwrap_or_else(|| { // Pick a "reasonable maximum" if we don't otherwise have a jobserver in // our environment, capping out at 32 so we don't take everything down @@ -938,7 +934,7 @@ pub fn run_passes(sess: &Session, } } -fn dump_incremental_data(trans: &CrateTranslation) { +pub fn dump_incremental_data(trans: &CrateTranslation) { let mut reuse = 0; for mtrans in trans.modules.iter() { match mtrans.source { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 7b836399f9cb5..1fd871d31b59b 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -23,7 +23,7 @@ //! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, //! int) and rec(x=int, y=int, z=int) will have the same TypeRef. -use super::CrateTranslation; +use super::OngoingCrateTranslation; use super::ModuleLlvm; use super::ModuleSource; use super::ModuleTranslation; @@ -43,9 +43,9 @@ use rustc::dep_graph::AssertDepGraphSafe; use rustc::middle::cstore::LinkMeta; use rustc::hir::map as hir_map; use rustc::util::common::time; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames}; +use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, OutputTypes}; use rustc::session::Session; -use rustc_incremental::IncrementalHashesMap; +use rustc_incremental::{self, IncrementalHashesMap}; use abi; use allocator; use mir::lvalue::LvalueRef; @@ -922,7 +922,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, incremental_hashes_map: &IncrementalHashesMap, output_filenames: &OutputFilenames) - -> CrateTranslation { + -> OngoingCrateTranslation { // Be careful with this krate: obviously it gives access to the // entire contents of the krate. So if you push any subtasks of // `TransCrate`, you need to be careful to register "reads" of the @@ -961,17 +961,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - return CrateTranslation { + return OngoingCrateTranslation { crate_name: tcx.crate_name(LOCAL_CRATE), modules: vec![], metadata_module: metadata_module, allocator_module: None, link: link_meta, metadata: metadata, - exported_symbols: empty_exported_symbols, + exported_symbols: Arc::new(empty_exported_symbols), no_builtins: no_builtins, linker_info: linker_info, windows_subsystem: None, + no_integrated_as: false, }; } @@ -1210,19 +1211,52 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, subsystem.to_string() }); - CrateTranslation { + let outputs = output_filenames; + + let no_integrated_as = sess.opts.cg.no_integrated_as || + (sess.target.target.options.no_integrated_as && + (outputs.outputs.contains_key(&OutputType::Object) || + outputs.outputs.contains_key(&OutputType::Exe))); + + let crate_translation = OngoingCrateTranslation { crate_name: tcx.crate_name(LOCAL_CRATE), - modules: modules, - metadata_module: metadata_module, - allocator_module: allocator_module, link: link_meta, metadata: metadata, - exported_symbols: Arc::try_unwrap(exported_symbols) - .expect("There's still a reference to exported_symbols?"), - no_builtins: no_builtins, - linker_info: linker_info, - windows_subsystem: windows_subsystem, - } + exported_symbols, + no_builtins, + linker_info, + windows_subsystem, + no_integrated_as, + + modules, + metadata_module, + allocator_module, + }; + + time(sess.time_passes(), + "assert dep graph", + || rustc_incremental::assert_dep_graph(tcx)); + + time(sess.time_passes(), + "serialize dep graph", + || rustc_incremental::save_dep_graph(tcx, + incremental_hashes_map, + &crate_translation.metadata.hashes, + crate_translation.link.crate_hash)); + // --- + + if no_integrated_as { + let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); + time(sess.time_passes(), + "LLVM passes", + || ::back::write::run_passes(sess, &crate_translation, &output_types, outputs)) + } else { + time(sess.time_passes(), + "LLVM passes", + || ::back::write::run_passes(sess, &crate_translation, &sess.opts.output_types, outputs)) + }; + + crate_translation } #[inline(never)] // give this a place in the profiler diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 70337a91731d7..c386d11fa84c4 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -35,7 +35,12 @@ #![feature(conservative_impl_trait)] use rustc::dep_graph::WorkProduct; +use rustc::session::Session; +use rustc::session::config::{OutputType, OutputFilenames}; +use rustc::util::fs::rename_or_copy_remove; use syntax_pos::symbol::Symbol; +use std::fs; +use std::sync::Arc; extern crate flate2; extern crate crossbeam; @@ -167,10 +172,69 @@ pub struct CrateTranslation { pub allocator_module: Option, pub link: rustc::middle::cstore::LinkMeta, pub metadata: rustc::middle::cstore::EncodedMetadata, - pub exported_symbols: back::symbol_export::ExportedSymbols, + pub exported_symbols: Arc, pub no_builtins: bool, pub windows_subsystem: Option, pub linker_info: back::linker::LinkerInfo } +pub struct OngoingCrateTranslation { + pub crate_name: Symbol, + pub link: rustc::middle::cstore::LinkMeta, + pub metadata: rustc::middle::cstore::EncodedMetadata, + pub exported_symbols: Arc, + pub no_builtins: bool, + pub windows_subsystem: Option, + pub linker_info: back::linker::LinkerInfo, + pub no_integrated_as: bool, + + // These will be replaced by a Future. + pub modules: Vec, + pub metadata_module: ModuleTranslation, + pub allocator_module: Option, +} + +impl OngoingCrateTranslation { + pub fn join(self, + sess: &Session, + outputs: &OutputFilenames) + -> CrateTranslation { + + let trans = CrateTranslation { + crate_name: self.crate_name, + link: self.link, + metadata: self.metadata, + exported_symbols: self.exported_symbols, + no_builtins: self.no_builtins, + windows_subsystem: self.windows_subsystem, + linker_info: self.linker_info, + + modules: self.modules, + metadata_module: self.metadata_module, + allocator_module: self.allocator_module, + }; + + if self.no_integrated_as { + back::write::run_assembler(sess, outputs); + + // HACK the linker expects the object file to be named foo.0.o but + // `run_assembler` produces an object named just foo.o. Rename it if we + // are going to build an executable + if sess.opts.output_types.contains_key(&OutputType::Exe) { + let f = outputs.path(OutputType::Object); + rename_or_copy_remove(&f, + f.with_file_name(format!("{}.0.o", + f.file_stem().unwrap().to_string_lossy()))).unwrap(); + } + + // Remove assembly source, unless --save-temps was specified + if !sess.opts.cg.save_temps { + fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap(); + } + } + + trans + } +} + __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } From 29d4725b31bee27f025c17320f0eb59c5fc7af3b Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 21 Jul 2017 17:15:18 +0200 Subject: [PATCH 02/29] async-llvm(2): Decouple diagnostics emission from LLVM worker coordination. --- src/librustc_trans/back/write.rs | 350 +++++++++++++++++++------------ src/librustc_trans/base.rs | 28 ++- src/librustc_trans/lib.rs | 14 +- 3 files changed, 238 insertions(+), 154 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 4af4ee664a257..28d255d420455 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -23,7 +23,7 @@ use rustc::hir::def_id::CrateNum; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; use rustc::util::fs::link_or_copy; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError}; -use errors::emitter::Emitter; +use errors::emitter::{Emitter}; use syntax::ext::hygiene::Mark; use syntax_pos::MultiSpan; use context::{is_pie_binary, get_reloc_model}; @@ -38,7 +38,7 @@ use std::io; use std::io::Write; use std::path::{Path, PathBuf}; use std::str; -use std::sync::mpsc::{channel, Sender}; +use std::sync::mpsc::{channel, Sender, Receiver}; use std::slice; use libc::{c_uint, c_void, c_char, size_t}; @@ -304,6 +304,9 @@ pub struct CodegenContext<'a> { pub incr_comp_session_dir: Option, // Channel back to the main control thread to send messages to pub tx: Sender, + + // Error messages... + pub shared_emitter: SharedEmitter, } struct HandlerFreeVars<'a> { @@ -313,7 +316,7 @@ struct HandlerFreeVars<'a> { unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, msg: &'b str, cookie: c_uint) { - drop(cgcx.tx.send(Message::InlineAsmError(cookie as u32, msg.to_string()))); + cgcx.shared_emitter.inline_asm_error(cookie as u32, msg.to_string()); } unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, @@ -613,8 +616,17 @@ pub fn cleanup_llvm(trans: &CrateTranslation) { } } +pub struct RunLLVMPassesResult { + pub modules: Vec, + pub metadata_module: ModuleTranslation, + pub allocator_module: Option, +} + pub fn run_passes(sess: &Session, trans: &OngoingCrateTranslation, + modules: Vec, + metadata_module: ModuleTranslation, + allocator_module: Option, output_types: &OutputTypes, crate_output: &OutputFilenames) { // It's possible that we have `codegen_units > 1` but only one item in @@ -631,7 +643,7 @@ pub fn run_passes(sess: &Session, } // Sanity check - assert!(trans.modules.len() == sess.opts.cg.codegen_units || + assert!(modules.len() == sess.opts.cg.codegen_units || sess.opts.debugging_opts.incremental.is_some() || !sess.opts.output_types.should_trans() || sess.opts.debugging_opts.no_trans); @@ -722,17 +734,17 @@ pub fn run_passes(sess: &Session, // Populate a buffer with a list of codegen threads. Items are processed in // LIFO order, just because it's a tiny bit simpler that way. (The order // doesn't actually matter.) - let mut work_items = Vec::with_capacity(1 + trans.modules.len()); + let mut work_items = Vec::with_capacity(1 + modules.len()); { let work = build_work_item(sess, - trans.metadata_module.clone(), + metadata_module.clone(), metadata_config.clone(), crate_output.clone()); work_items.push(work); } - if let Some(allocator) = trans.allocator_module.clone() { + if let Some(allocator) = allocator_module.clone() { let work = build_work_item(sess, allocator, allocator_config.clone(), @@ -740,7 +752,7 @@ pub fn run_passes(sess: &Session, work_items.push(work); } - for mtrans in trans.modules.iter() { + for mtrans in modules.iter() { let work = build_work_item(sess, mtrans.clone(), modules_config.clone(), @@ -760,7 +772,7 @@ pub fn run_passes(sess: &Session, }); // If in incr. comp. mode, preserve the `.o` files for potential re-use - for mtrans in trans.modules.iter() { + for mtrans in modules.iter() { let mut files = vec![]; if modules_config.emit_obj { @@ -781,80 +793,82 @@ pub fn run_passes(sess: &Session, llvm::LLVMRustDisposeTargetMachine(tm); } - // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); - } - }; - - let copy_if_one_unit = |output_type: OutputType, - keep_numbered: bool| { - if trans.modules.len() == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&trans.modules[0].name[..]); - let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, - &crate_output.path(output_type)); - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.#module-name#.x`. - remove(sess, &path); - } - } else { - let ext = crate_output.temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - - if crate_output.outputs.contains_key(&output_type) { - // 2) Multiple codegen units, with `--emit foo=some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring emit path because multiple .{} files \ - were produced", ext)); - } else if crate_output.single_output_file.is_some() { - // 3) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring -o because multiple .{} files \ - were produced", ext)); - } else { - // 4) Multiple codegen units, but no explicit name. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } - } - }; - - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. let mut user_wants_bitcode = false; let mut user_wants_objects = false; - for output_type in output_types.keys() { - match *output_type { - OutputType::Bitcode => { - user_wants_bitcode = true; - // Copy to .bc, but always keep the .0.bc. There is a later - // check to figure out if we should delete .0.bc files, or keep - // them for making an rlib. - copy_if_one_unit(OutputType::Bitcode, true); - } - OutputType::LlvmAssembly => { - copy_if_one_unit(OutputType::LlvmAssembly, false); + { + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &Path| { + if let Err(e) = fs::copy(from, to) { + sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); } - OutputType::Assembly => { - copy_if_one_unit(OutputType::Assembly, false); + }; + + let copy_if_one_unit = |output_type: OutputType, + keep_numbered: bool| { + if modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + copy_gracefully(&path, + &crate_output.path(output_type)); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + remove(sess, &path); + } + } else { + let ext = crate_output.temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_key(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring emit path because multiple .{} files \ + were produced", ext)); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring -o because multiple .{} files \ + were produced", ext)); + } else { + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } } - OutputType::Object => { - user_wants_objects = true; - copy_if_one_unit(OutputType::Object, true); + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in output_types.keys() { + match *output_type { + OutputType::Bitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + copy_if_one_unit(OutputType::LlvmAssembly, false); + } + OutputType::Assembly => { + copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | + OutputType::Metadata | + OutputType::Exe | + OutputType::DepInfo => {} } - OutputType::Mir | - OutputType::Metadata | - OutputType::Exe | - OutputType::DepInfo => {} } } let user_wants_bitcode = user_wants_bitcode; @@ -895,7 +909,7 @@ pub fn run_passes(sess: &Session, let keep_numbered_objects = needs_crate_object || (user_wants_objects && sess.opts.cg.codegen_units > 1); - for module_name in trans.modules.iter().map(|m| Some(&m.name[..])) { + for module_name in modules.iter().map(|m| Some(&m.name[..])) { if modules_config.emit_obj && !keep_numbered_objects { let path = crate_output.temp_path(OutputType::Object, module_name); remove(sess, &path); @@ -909,11 +923,11 @@ pub fn run_passes(sess: &Session, if metadata_config.emit_bc && !user_wants_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, - Some(&trans.metadata_module.name)); + Some(&metadata_module.name)); remove(sess, &path); } if allocator_config.emit_bc && !user_wants_bitcode { - if let Some(ref module) = trans.allocator_module { + if let Some(ref module) = allocator_module { let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name)); remove(sess, &path); @@ -932,6 +946,14 @@ pub fn run_passes(sess: &Session, if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { unsafe { llvm::LLVMRustPrintPassTimings(); } } + + *trans.result.borrow_mut() = Some( + RunLLVMPassesResult { + modules, + metadata_module, + allocator_module, + } + ); } pub fn dump_incremental_data(trans: &CrateTranslation) { @@ -1011,10 +1033,7 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) pub enum Message { Token(io::Result), - Diagnostic(Diagnostic), Done { success: bool }, - InlineAsmError(u32, String), - AbortIfErrors, } pub struct Diagnostic { @@ -1048,6 +1067,8 @@ fn execute_work<'a>(sess: &'a Session, helper.request_token(); } + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + // This is the "main loop" of parallel work happening for parallel codegen. // It's here that we manage parallelism, schedule work, and work with // messages coming from clients. @@ -1112,7 +1133,13 @@ fn execute_work<'a>(sess: &'a Session, while work_items.len() > 0 && running < tokens.len() + 1 { let item = work_items.pop().unwrap(); let index = work_items.len(); - spawn_work(sess, exported_symbols, scope, tx.clone(), item, index); + spawn_work(sess, + exported_symbols, + scope, + tx.clone(), + shared_emitter.clone(), + item, + index); running += 1; } @@ -1140,70 +1167,22 @@ fn execute_work<'a>(sess: &'a Session, running -= 1; } Message::Done { success: false } => { - sess.fatal("aborting due to worker thread panic"); - } - - // Our worker wants us to emit an error message, so get ahold of our - // `sess` and print it out - Message::Diagnostic(diag) => { - let handler = sess.diagnostic(); - match diag.code { - Some(ref code) => { - handler.emit_with_code(&MultiSpan::new(), - &diag.msg, - &code, - diag.lvl); - } - None => { - handler.emit(&MultiSpan::new(), - &diag.msg, - diag.lvl); - } - } - } - Message::InlineAsmError(cookie, msg) => { - match Mark::from_u32(cookie).expn_info() { - Some(ei) => sess.span_err(ei.call_site, &msg), - None => sess.err(&msg), - } + shared_emitter.fatal("aborting due to worker thread panic".to_string()); } - - // Sent to us after a worker sends us a batch of error messages, and - // it's the point at which we check for errors. - Message::AbortIfErrors => sess.diagnostic().abort_if_errors(), } + + shared_emitter_main.check(sess); } // Just in case, check this on the way out. sess.diagnostic().abort_if_errors(); } -struct SharedEmitter { - tx: Sender, -} - -impl Emitter for SharedEmitter { - fn emit(&mut self, db: &DiagnosticBuilder) { - drop(self.tx.send(Message::Diagnostic(Diagnostic { - msg: db.message(), - code: db.code.clone(), - lvl: db.level, - }))); - for child in &db.children { - drop(self.tx.send(Message::Diagnostic(Diagnostic { - msg: child.message(), - code: None, - lvl: child.level, - }))); - } - drop(self.tx.send(Message::AbortIfErrors)); - } -} - fn spawn_work<'a>(sess: &'a Session, exported_symbols: &'a ExportedSymbols, scope: &Scope<'a>, tx: Sender, + emitter: SharedEmitter, work: WorkItem, idx: usize) { let plugin_passes = sess.plugin_llvm_passes.borrow().clone(); @@ -1244,8 +1223,8 @@ fn spawn_work<'a>(sess: &'a Session, // Set up our non-`Send` `CodegenContext` now that we're in a helper // thread and have all our info available to us. - let emitter = SharedEmitter { tx: tx.clone() }; - let diag_handler = Handler::with_emitter(true, false, Box::new(emitter)); + // let emitter = SharedEmitter { tx: tx.clone() }; + let diag_handler = Handler::with_emitter(true, false, Box::new(emitter.clone())); let cgcx = CodegenContext { crate_types: crate_types, @@ -1261,6 +1240,7 @@ fn spawn_work<'a>(sess: &'a Session, worker: idx, incr_comp_session_dir: incr_comp_session_dir, tx: tx.clone(), + shared_emitter: emitter, }; // Execute the work itself, and if it finishes successfully then flag @@ -1371,3 +1351,95 @@ pub unsafe fn with_llvm_pmb(llmod: ModuleRef, f(builder); llvm::LLVMPassManagerBuilderDispose(builder); } + + +enum SharedEmitterMessage { + Diagnostic(Diagnostic), + InlineAsmError(u32, String), + AbortIfErrors, + Fatal(String), +} + +#[derive(Clone)] +pub struct SharedEmitter { + sender: Sender, +} + +pub struct SharedEmitterMain { + receiver: Receiver, +} + +impl SharedEmitter { + pub fn new() -> (SharedEmitter, SharedEmitterMain) { + let (sender, receiver) = channel(); + + (SharedEmitter { sender }, SharedEmitterMain { receiver }) + } + + fn inline_asm_error(&self, cookie: u32, msg: String) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + } + + fn fatal(&self, msg: String) { + drop(self.sender.send(SharedEmitterMessage::Fatal(msg))); + } +} + +impl Emitter for SharedEmitter { + fn emit(&mut self, db: &DiagnosticBuilder) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: db.message(), + code: db.code.clone(), + lvl: db.level, + }))); + for child in &db.children { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: child.message(), + code: None, + lvl: child.level, + }))); + } + drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); + } +} + +impl SharedEmitterMain { + pub fn check(&self, sess: &Session) { + loop { + match self.receiver.try_recv() { + Ok(SharedEmitterMessage::Diagnostic(diag)) => { + let handler = sess.diagnostic(); + match diag.code { + Some(ref code) => { + handler.emit_with_code(&MultiSpan::new(), + &diag.msg, + &code, + diag.lvl); + } + None => { + handler.emit(&MultiSpan::new(), + &diag.msg, + diag.lvl); + } + } + } + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { + match Mark::from_u32(cookie).expn_info() { + Some(ei) => sess.span_err(ei.call_site, &msg), + None => sess.err(&msg), + } + } + Ok(SharedEmitterMessage::AbortIfErrors) => { + sess.abort_if_errors(); + } + Ok(SharedEmitterMessage::Fatal(msg)) => { + sess.fatal(&msg); + } + Err(_) => { + break; + } + } + + } + } +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1fd871d31b59b..5582079c78f13 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -963,9 +963,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); return OngoingCrateTranslation { crate_name: tcx.crate_name(LOCAL_CRATE), - modules: vec![], - metadata_module: metadata_module, - allocator_module: None, link: link_meta, metadata: metadata, exported_symbols: Arc::new(empty_exported_symbols), @@ -973,6 +970,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info: linker_info, windows_subsystem: None, no_integrated_as: false, + result: ::std::cell::RefCell::new(Some(::back::write::RunLLVMPassesResult { + modules: vec![], + metadata_module: metadata_module, + allocator_module: None, + })), }; } @@ -1228,9 +1230,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, windows_subsystem, no_integrated_as, - modules, - metadata_module, - allocator_module, + result: ::std::cell::RefCell::new(None), }; time(sess.time_passes(), @@ -1249,11 +1249,23 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); time(sess.time_passes(), "LLVM passes", - || ::back::write::run_passes(sess, &crate_translation, &output_types, outputs)) + || ::back::write::run_passes(sess, + &crate_translation, + modules, + metadata_module, + allocator_module, + &output_types, + outputs)) } else { time(sess.time_passes(), "LLVM passes", - || ::back::write::run_passes(sess, &crate_translation, &sess.opts.output_types, outputs)) + || ::back::write::run_passes(sess, + &crate_translation, + modules, + metadata_module, + allocator_module, + &sess.opts.output_types, + outputs)) }; crate_translation diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index c386d11fa84c4..e7debe3919b4f 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -188,10 +188,8 @@ pub struct OngoingCrateTranslation { pub linker_info: back::linker::LinkerInfo, pub no_integrated_as: bool, - // These will be replaced by a Future. - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub allocator_module: Option, + // This will be replaced by a Future. + pub result: ::std::cell::RefCell>, } impl OngoingCrateTranslation { @@ -200,6 +198,8 @@ impl OngoingCrateTranslation { outputs: &OutputFilenames) -> CrateTranslation { + let result = self.result.borrow_mut().take().unwrap(); + let trans = CrateTranslation { crate_name: self.crate_name, link: self.link, @@ -209,9 +209,9 @@ impl OngoingCrateTranslation { windows_subsystem: self.windows_subsystem, linker_info: self.linker_info, - modules: self.modules, - metadata_module: self.metadata_module, - allocator_module: self.allocator_module, + modules: result.modules, + metadata_module: result.metadata_module, + allocator_module: result.allocator_module, }; if self.no_integrated_as { From bac57cf65430ce52bfa6a50e7f4db1b99c02d1cb Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 24 Jul 2017 13:54:18 +0200 Subject: [PATCH 03/29] async-llvm(3): Make write::CodegenContext Clone and Send. --- src/librustc_trans/back/lto.rs | 26 +++--- src/librustc_trans/back/write.rs | 134 +++++++++++++++---------------- 2 files changed, 78 insertions(+), 82 deletions(-) diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index feed127b0b60b..e160d6b6c6ab3 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -12,7 +12,7 @@ use back::link; use back::write; use back::symbol_export; use rustc::session::config; -use errors::FatalError; +use errors::{FatalError, Handler}; use llvm; use llvm::archive_ro::ArchiveRO; use llvm::{ModuleRef, TargetMachineRef, True, False}; @@ -41,24 +41,24 @@ pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { } pub fn run(cgcx: &CodegenContext, + diag_handler: &Handler, llmod: ModuleRef, tm: TargetMachineRef, config: &ModuleConfig, temp_no_opt_bc_filename: &Path) -> Result<(), FatalError> { - let handler = cgcx.handler; if cgcx.opts.cg.prefer_dynamic { - handler.struct_err("cannot prefer dynamic linking when performing LTO") - .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ - supported with LTO") - .emit(); + diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ + supported with LTO") + .emit(); return Err(FatalError) } // Make sure we actually can run LTO for crate_type in cgcx.crate_types.iter() { if !crate_type_allows_lto(*crate_type) { - let e = handler.fatal("lto can only be run for executables, cdylibs and \ - static library outputs"); + let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ + static library outputs"); return Err(e) } } @@ -116,13 +116,13 @@ pub fn run(cgcx: &CodegenContext, if res.is_err() { let msg = format!("failed to decompress bc of `{}`", name); - Err(handler.fatal(&msg)) + Err(diag_handler.fatal(&msg)) } else { Ok(inflated) } } else { - Err(handler.fatal(&format!("Unsupported bytecode format version {}", - version))) + Err(diag_handler.fatal(&format!("Unsupported bytecode format version {}", + version))) } })? } else { @@ -136,7 +136,7 @@ pub fn run(cgcx: &CodegenContext, if res.is_err() { let msg = format!("failed to decompress bc of `{}`", name); - Err(handler.fatal(&msg)) + Err(diag_handler.fatal(&msg)) } else { Ok(inflated) } @@ -152,7 +152,7 @@ pub fn run(cgcx: &CodegenContext, Ok(()) } else { let msg = format!("failed to load bc of `{}`", name); - Err(write::llvm_err(handler, msg)) + Err(write::llvm_err(&diag_handler, msg)) } })?; } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 28d255d420455..d2fc968285270 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -282,6 +282,7 @@ impl ModuleConfig { } /// Additional resources used by optimize_and_codegen (not module specific) +#[derive(Clone)] pub struct CodegenContext<'a> { // Resouces needed when running LTO pub time_passes: bool, @@ -292,7 +293,7 @@ pub struct CodegenContext<'a> { pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, // Handler to use for diagnostics produced during codegen. - pub handler: &'a Handler, + pub diag_emitter: SharedEmitter, // LLVM passes added by plugins. pub plugin_passes: Vec, // LLVM optimizations for which we want to print remarks. @@ -303,20 +304,24 @@ pub struct CodegenContext<'a> { // compiling incrementally pub incr_comp_session_dir: Option, // Channel back to the main control thread to send messages to - pub tx: Sender, + pub coordinator_send: Sender, +} - // Error messages... - pub shared_emitter: SharedEmitter, +impl<'a> CodegenContext<'a> { + fn create_diag_handler(&self) -> Handler { + Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) + } } struct HandlerFreeVars<'a> { cgcx: &'a CodegenContext<'a>, + diag_handler: &'a Handler, } unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, msg: &'b str, cookie: c_uint) { - cgcx.shared_emitter.inline_asm_error(cookie as u32, msg.to_string()); + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); } unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, @@ -331,7 +336,7 @@ unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, } unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - let HandlerFreeVars { cgcx, .. } = *(user as *const HandlerFreeVars); + let HandlerFreeVars { cgcx, diag_handler, .. } = *(user as *const HandlerFreeVars); match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { @@ -347,7 +352,7 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo }; if enabled { - cgcx.handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", + diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", opt.kind.describe(), opt.pass_name, opt.filename, @@ -363,6 +368,7 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo // Unsafe due to LLVM calls. unsafe fn optimize_and_codegen(cgcx: &CodegenContext, + diag_handler: &Handler, mtrans: ModuleTranslation, mllvm: ModuleLlvm, config: ModuleConfig, @@ -375,6 +381,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, let fv = HandlerFreeVars { cgcx: cgcx, + diag_handler: diag_handler, }; let fv = &fv as *const HandlerFreeVars as *mut c_void; @@ -409,7 +416,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llvm::PassKind::Function => fpm, llvm::PassKind::Module => mpm, llvm::PassKind::Other => { - cgcx.handler.err("Encountered LLVM pass kind we can't handle"); + diag_handler.err("Encountered LLVM pass kind we can't handle"); return true }, }; @@ -429,20 +436,20 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, for pass in &config.passes { if !addpass(pass) { - cgcx.handler.warn(&format!("unknown pass `{}`, ignoring", + diag_handler.warn(&format!("unknown pass `{}`, ignoring", pass)); } } for pass in &cgcx.plugin_passes { if !addpass(pass) { - cgcx.handler.err(&format!("a plugin asked for LLVM pass \ + diag_handler.err(&format!("a plugin asked for LLVM pass \ `{}` but LLVM does not \ recognize it", pass)); } } - cgcx.handler.abort_if_errors(); + diag_handler.abort_if_errors(); // Finally, run the actual optimization passes time(config.time_passes, &format!("llvm function passes [{}]", cgcx.worker), || @@ -459,6 +466,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, let temp_no_opt_bc_filename = output_names.temp_path_ext("no-opt.lto.bc", module_name); lto::run(cgcx, + diag_handler, llmod, tm, &config, @@ -564,7 +572,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llmod }; with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &path, + write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) })?; if config.emit_obj { @@ -574,7 +582,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if write_obj { with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(cgcx.handler, tm, cpm, llmod, &obj_out, + write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) })?; } @@ -585,14 +593,14 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if copy_bc_to_obj { debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); if let Err(e) = link_or_copy(&bc_out, &obj_out) { - cgcx.handler.err(&format!("failed to copy bitcode to object file: {}", e)); + diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); } } if rm_bc { debug!("removing_bitcode {:?}", bc_out); if let Err(e) = fs::remove_file(&bc_out) { - cgcx.handler.err(&format!("failed to remove bitcode: {}", e)); + diag_handler.err(&format!("failed to remove bitcode: {}", e)); } } @@ -991,11 +999,13 @@ fn build_work_item(sess: &Session, fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) -> Result<(), FatalError> { + let diag_handler = cgcx.create_diag_handler(); unsafe { match work_item.mtrans.source { ModuleSource::Translated(mllvm) => { debug!("llvm-optimizing {:?}", work_item.mtrans.name); optimize_and_codegen(cgcx, + &diag_handler, work_item.mtrans, mllvm, work_item.config, @@ -1017,7 +1027,7 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) match link_or_copy(&source_file, &obj_out) { Ok(_) => { } Err(err) => { - cgcx.handler.err(&format!("unable to copy {} to {}: {}", + diag_handler.err(&format!("unable to copy {} to {}: {}", source_file.display(), obj_out.display(), err)); @@ -1069,6 +1079,30 @@ fn execute_work<'a>(sess: &'a Session, let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + let mut each_linked_rlib_for_lto = Vec::new(); + drop(link::each_linked_rlib(sess, &mut |cnum, path| { + if link::ignored_for_lto(sess, cnum) { + return + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + + let cgcx = CodegenContext { + crate_types: sess.crate_types.borrow().clone(), + each_linked_rlib_for_lto: each_linked_rlib_for_lto, + lto: sess.lto(), + no_landing_pads: sess.no_landing_pads(), + opts: &sess.opts, + time_passes: sess.time_passes(), + exported_symbols: exported_symbols, + plugin_passes: sess.plugin_llvm_passes.borrow().clone(), + remark: sess.opts.cg.remark.clone(), + worker: 0, + incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + coordinator_send: tx.clone(), + diag_emitter: shared_emitter.clone(), + }; + // This is the "main loop" of parallel work happening for parallel codegen. // It's here that we manage parallelism, schedule work, and work with // messages coming from clients. @@ -1132,14 +1166,16 @@ fn execute_work<'a>(sess: &'a Session, // parallelism slots and work left to spawn. while work_items.len() > 0 && running < tokens.len() + 1 { let item = work_items.pop().unwrap(); - let index = work_items.len(); - spawn_work(sess, - exported_symbols, + let worker_index = work_items.len(); + + let cgcx = CodegenContext { + worker: worker_index, + .. cgcx.clone() + }; + + spawn_work(cgcx, scope, - tx.clone(), - shared_emitter.clone(), - item, - index); + item); running += 1; } @@ -1178,29 +1214,10 @@ fn execute_work<'a>(sess: &'a Session, sess.diagnostic().abort_if_errors(); } -fn spawn_work<'a>(sess: &'a Session, - exported_symbols: &'a ExportedSymbols, +fn spawn_work<'a>(cgcx: CodegenContext<'a>, scope: &Scope<'a>, - tx: Sender, - emitter: SharedEmitter, - work: WorkItem, - idx: usize) { - let plugin_passes = sess.plugin_llvm_passes.borrow().clone(); - let remark = sess.opts.cg.remark.clone(); - let incr_comp_session_dir = sess.incr_comp_session_dir_opt().map(|r| r.clone()); + work: WorkItem) { let depth = time_depth(); - let lto = sess.lto(); - let crate_types = sess.crate_types.borrow().clone(); - let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(sess, &mut |cnum, path| { - if link::ignored_for_lto(sess, cnum) { - return - } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); - })); - let time_passes = sess.time_passes(); - let no_landing_pads = sess.no_landing_pads(); - let opts = &sess.opts; scope.spawn(move || { set_time_depth(depth); @@ -1208,41 +1225,20 @@ fn spawn_work<'a>(sess: &'a Session, // Set up a destructor which will fire off a message that we're done as // we exit. struct Bomb { - tx: Sender, + coordinator_send: Sender, success: bool, } impl Drop for Bomb { fn drop(&mut self) { - drop(self.tx.send(Message::Done { success: self.success })); + drop(self.coordinator_send.send(Message::Done { success: self.success })); } } + let mut bomb = Bomb { - tx: tx.clone(), + coordinator_send: cgcx.coordinator_send.clone(), success: false, }; - // Set up our non-`Send` `CodegenContext` now that we're in a helper - // thread and have all our info available to us. - // let emitter = SharedEmitter { tx: tx.clone() }; - let diag_handler = Handler::with_emitter(true, false, Box::new(emitter.clone())); - - let cgcx = CodegenContext { - crate_types: crate_types, - each_linked_rlib_for_lto: each_linked_rlib_for_lto, - lto: lto, - no_landing_pads: no_landing_pads, - opts: opts, - time_passes: time_passes, - exported_symbols: exported_symbols, - handler: &diag_handler, - plugin_passes: plugin_passes, - remark: remark, - worker: idx, - incr_comp_session_dir: incr_comp_session_dir, - tx: tx.clone(), - shared_emitter: emitter, - }; - // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. // From df6be33d84f14c286689938eb2a2686315926e9f Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 24 Jul 2017 14:21:28 +0200 Subject: [PATCH 04/29] async-llvm(4): Move work coordination to separate thread in order to free up the main thread for translation. --- src/librustc_trans/back/write.rs | 124 +++++++++++++++---------------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index d2fc968285270..08eccd8fdf3a4 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -28,7 +28,6 @@ use syntax::ext::hygiene::Mark; use syntax_pos::MultiSpan; use context::{is_pie_binary, get_reloc_model}; use jobserver::{Client, Acquired}; -use crossbeam::{scope, Scope}; use rustc_demangle; use std::cmp; @@ -38,8 +37,10 @@ use std::io; use std::io::Write; use std::path::{Path, PathBuf}; use std::str; +use std::sync::Arc; use std::sync::mpsc::{channel, Sender, Receiver}; use std::slice; +use std::thread; use libc::{c_uint, c_void, c_char, size_t}; pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ @@ -283,13 +284,13 @@ impl ModuleConfig { /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] -pub struct CodegenContext<'a> { +pub struct CodegenContext { // Resouces needed when running LTO pub time_passes: bool, pub lto: bool, pub no_landing_pads: bool, - pub exported_symbols: &'a ExportedSymbols, - pub opts: &'a config::Options, + pub exported_symbols: Arc, + pub opts: Arc, pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, // Handler to use for diagnostics produced during codegen. @@ -307,18 +308,18 @@ pub struct CodegenContext<'a> { pub coordinator_send: Sender, } -impl<'a> CodegenContext<'a> { +impl CodegenContext { fn create_diag_handler(&self) -> Handler { Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) } } struct HandlerFreeVars<'a> { - cgcx: &'a CodegenContext<'a>, + cgcx: &'a CodegenContext, diag_handler: &'a Handler, } -unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, +unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, msg: &'b str, cookie: c_uint) { cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); @@ -775,9 +776,8 @@ pub fn run_passes(sess: &Session, let num_workers = cmp::min(work_items.len() - 1, 32); Client::new(num_workers).expect("failed to create jobserver") }); - scope(|scope| { - execute_work(sess, work_items, client, &trans.exported_symbols, scope); - }); + + execute_work(sess, work_items, client, trans.exported_symbols.clone()); // If in incr. comp. mode, preserve the `.o` files for potential re-use for mtrans in modules.iter() { @@ -1052,11 +1052,10 @@ pub struct Diagnostic { lvl: Level, } -fn execute_work<'a>(sess: &'a Session, - mut work_items: Vec, - jobserver: Client, - exported_symbols: &'a ExportedSymbols, - scope: &Scope<'a>) { +fn execute_work(sess: &Session, + mut work_items: Vec, + jobserver: Client, + exported_symbols: Arc) { let (tx, rx) = channel(); let tx2 = tx.clone(); @@ -1092,7 +1091,7 @@ fn execute_work<'a>(sess: &'a Session, each_linked_rlib_for_lto: each_linked_rlib_for_lto, lto: sess.lto(), no_landing_pads: sess.no_landing_pads(), - opts: &sess.opts, + opts: Arc::new(sess.opts.clone()), time_passes: sess.time_passes(), exported_symbols: exported_symbols, plugin_passes: sess.plugin_llvm_passes.borrow().clone(), @@ -1158,68 +1157,67 @@ fn execute_work<'a>(sess: &'a Session, // Before that work finishes, however, we may acquire a token. In that case // we actually wastefully acquired the token, so we relinquish it back to // the jobserver. - let mut tokens = Vec::new(); - let mut running = 0; - while work_items.len() > 0 || running > 0 { - - // Spin up what work we can, only doing this while we've got available - // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() + 1 { - let item = work_items.pop().unwrap(); - let worker_index = work_items.len(); - - let cgcx = CodegenContext { - worker: worker_index, - .. cgcx.clone() - }; - spawn_work(cgcx, - scope, - item); - running += 1; - } + thread::spawn(move || { + let mut tokens = Vec::new(); + let mut running = 0; + while work_items.len() > 0 || running > 0 { - // Relinquish accidentally acquired extra tokens - tokens.truncate(running.saturating_sub(1)); + // Spin up what work we can, only doing this while we've got available + // parallelism slots and work left to spawn. + while work_items.len() > 0 && running < tokens.len() + 1 { + let item = work_items.pop().unwrap(); + let worker_index = work_items.len(); - match rx.recv().unwrap() { - // Save the token locally and the next turn of the loop will use - // this to spawn a new unit of work, or it may get dropped - // immediately if we have no more work to spawn. - Message::Token(token) => { - tokens.push(token.expect("failed to acquire jobserver token")); - } + let cgcx = CodegenContext { + worker: worker_index, + .. cgcx.clone() + }; - // If a thread exits successfully then we drop a token associated - // with that worker and update our `running` count. We may later - // re-acquire a token to continue running more work. We may also not - // actually drop a token here if the worker was running with an - // "ephemeral token" - // - // Note that if the thread failed that means it panicked, so we - // abort immediately. - Message::Done { success: true } => { - drop(tokens.pop()); - running -= 1; + spawn_work(cgcx, item); + running += 1; } - Message::Done { success: false } => { - shared_emitter.fatal("aborting due to worker thread panic".to_string()); + + // Relinquish accidentally acquired extra tokens + tokens.truncate(running.saturating_sub(1)); + + match rx.recv().unwrap() { + // Save the token locally and the next turn of the loop will use + // this to spawn a new unit of work, or it may get dropped + // immediately if we have no more work to spawn. + Message::Token(token) => { + tokens.push(token.expect("failed to acquire jobserver token")); + } + + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running` count. We may later + // re-acquire a token to continue running more work. We may also not + // actually drop a token here if the worker was running with an + // "ephemeral token" + // + // Note that if the thread failed that means it panicked, so we + // abort immediately. + Message::Done { success: true } => { + drop(tokens.pop()); + running -= 1; + } + Message::Done { success: false } => { + shared_emitter.fatal("aborting due to worker thread panic".to_string()); + } } } + }).join().unwrap(); - shared_emitter_main.check(sess); - } + shared_emitter_main.check(sess); // Just in case, check this on the way out. sess.diagnostic().abort_if_errors(); } -fn spawn_work<'a>(cgcx: CodegenContext<'a>, - scope: &Scope<'a>, - work: WorkItem) { +fn spawn_work(cgcx: CodegenContext, work: WorkItem) { let depth = time_depth(); - scope.spawn(move || { + thread::spawn(move || { set_time_depth(depth); // Set up a destructor which will fire off a message that we're done as From b18a61a15ba5c267f7b25edebf3ff4aa7c3896f6 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 24 Jul 2017 14:51:00 +0200 Subject: [PATCH 05/29] async-llvm(5): Do continuous error handling on main thread. --- src/librustc_trans/back/write.rs | 69 +++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 08eccd8fdf3a4..9e4c1b87aacc5 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -32,6 +32,7 @@ use rustc_demangle; use std::cmp; use std::ffi::CString; +use std::fmt; use std::fs; use std::io; use std::io::Write; @@ -777,7 +778,33 @@ pub fn run_passes(sess: &Session, Client::new(num_workers).expect("failed to create jobserver") }); - execute_work(sess, work_items, client, trans.exported_symbols.clone()); + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + let (trans_worker_send, trans_worker_receive) = channel(); + + let coordinator_thread = start_executing_work(sess, + work_items, + shared_emitter, + trans_worker_send, + client, + trans.exported_symbols.clone()); + loop { + shared_emitter_main.check(sess); + + match trans_worker_receive.recv() { + Ok(Message::AllWorkDone) | + Err(_) => break, + + Ok(Message::CheckErrorMessages) => continue, + Ok(msg) => { + bug!("unexpected message {:?}", msg); + } + } + } + + coordinator_thread.join().unwrap(); + + // Just in case, check this on the way out. + sess.diagnostic().abort_if_errors(); // If in incr. comp. mode, preserve the `.o` files for potential re-use for mtrans in modules.iter() { @@ -975,12 +1002,18 @@ pub fn dump_incremental_data(trans: &CrateTranslation) { eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); } -struct WorkItem { +pub struct WorkItem { mtrans: ModuleTranslation, config: ModuleConfig, output_names: OutputFilenames } +impl fmt::Debug for WorkItem { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WorkItem({})", self.mtrans.name) + } +} + fn build_work_item(sess: &Session, mtrans: ModuleTranslation, config: ModuleConfig, @@ -1041,21 +1074,29 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) Ok(()) } +#[derive(Debug)] pub enum Message { Token(io::Result), Done { success: bool }, + WorkItem(WorkItem), + CheckErrorMessages, + AllWorkDone, } + pub struct Diagnostic { msg: String, code: Option, lvl: Level, } -fn execute_work(sess: &Session, - mut work_items: Vec, - jobserver: Client, - exported_symbols: Arc) { +fn start_executing_work(sess: &Session, + mut work_items: Vec, + shared_emitter: SharedEmitter, + trans_worker_send: Sender, + jobserver: Client, + exported_symbols: Arc) + -> thread::JoinHandle<()> { let (tx, rx) = channel(); let tx2 = tx.clone(); @@ -1076,8 +1117,6 @@ fn execute_work(sess: &Session, helper.request_token(); } - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); - let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(sess, &mut |cnum, path| { if link::ignored_for_lto(sess, cnum) { @@ -1200,18 +1239,20 @@ fn execute_work(sess: &Session, Message::Done { success: true } => { drop(tokens.pop()); running -= 1; + trans_worker_send.send(Message::CheckErrorMessages).unwrap(); } Message::Done { success: false } => { shared_emitter.fatal("aborting due to worker thread panic".to_string()); + trans_worker_send.send(Message::CheckErrorMessages).unwrap(); + } + msg @ Message::WorkItem(_) | + msg @ Message::AllWorkDone | + msg @ Message::CheckErrorMessages => { + bug!("unexpected message: {:?}", msg); } } } - }).join().unwrap(); - - shared_emitter_main.check(sess); - - // Just in case, check this on the way out. - sess.diagnostic().abort_if_errors(); + }) } fn spawn_work(cgcx: CodegenContext, work: WorkItem) { From 8f6894e177cecf3cd35833e2063256a69841415a Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 24 Jul 2017 15:50:42 +0200 Subject: [PATCH 06/29] async-llvm(6): Make the LLVM work coordinator get its work package through a channel instead of upfront. --- src/librustc_trans/back/write.rs | 81 +++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 9e4c1b87aacc5..ee3c9ace7dc14 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -780,19 +780,31 @@ pub fn run_passes(sess: &Session, let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (trans_worker_send, trans_worker_receive) = channel(); + let (coordinator_send, coordinator_receive) = channel(); let coordinator_thread = start_executing_work(sess, - work_items, + work_items.len(), shared_emitter, trans_worker_send, + coordinator_send.clone(), + coordinator_receive, client, trans.exported_symbols.clone()); + for work_item in work_items { + coordinator_send.send(Message::WorkItem(work_item)).unwrap(); + } + loop { shared_emitter_main.check(sess); match trans_worker_receive.recv() { - Ok(Message::AllWorkDone) | - Err(_) => break, + Err(_) => { + // An `Err` here means that all senders for this channel have + // been closed. This could happen because all work has + // completed successfully or there has been some error. + // At this point we don't care which it is. + break + } Ok(Message::CheckErrorMessages) => continue, Ok(msg) => { @@ -801,9 +813,15 @@ pub fn run_passes(sess: &Session, } } - coordinator_thread.join().unwrap(); + match coordinator_thread.join() { + Ok(()) => {}, + Err(err) => { + panic!("error: {:?}", err); + } + } // Just in case, check this on the way out. + shared_emitter_main.check(sess); sess.diagnostic().abort_if_errors(); // If in incr. comp. mode, preserve the `.o` files for potential re-use @@ -1080,7 +1098,6 @@ pub enum Message { Done { success: bool }, WorkItem(WorkItem), CheckErrorMessages, - AllWorkDone, } @@ -1091,15 +1108,14 @@ pub struct Diagnostic { } fn start_executing_work(sess: &Session, - mut work_items: Vec, + total_work_item_count: usize, shared_emitter: SharedEmitter, trans_worker_send: Sender, + coordinator_send: Sender, + coordinator_receive: Receiver, jobserver: Client, exported_symbols: Arc) - -> thread::JoinHandle<()> { - let (tx, rx) = channel(); - let tx2 = tx.clone(); - + -> thread::JoinHandle<()> { // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. Once we've got the helper // thread then request `n-1` tokens because all of our work items are ready @@ -1110,10 +1126,11 @@ fn start_executing_work(sess: &Session, // // After we've requested all these tokens then we'll, when we can, get // tokens on `rx` above which will get managed in the main loop below. + let coordinator_send2 = coordinator_send.clone(); let helper = jobserver.into_helper_thread(move |token| { - drop(tx2.send(Message::Token(token))); + drop(coordinator_send2.send(Message::Token(token))); }).expect("failed to spawn helper thread"); - for _ in 0..work_items.len() - 1 { + for _ in 0..total_work_item_count - 1 { helper.request_token(); } @@ -1137,7 +1154,7 @@ fn start_executing_work(sess: &Session, remark: sess.opts.cg.remark.clone(), worker: 0, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - coordinator_send: tx.clone(), + coordinator_send: coordinator_send, diag_emitter: shared_emitter.clone(), }; @@ -1198,29 +1215,35 @@ fn start_executing_work(sess: &Session, // the jobserver. thread::spawn(move || { + let mut work_items_left = total_work_item_count; + let mut work_items = Vec::with_capacity(total_work_item_count); let mut tokens = Vec::new(); let mut running = 0; - while work_items.len() > 0 || running > 0 { + while work_items_left > 0 || running > 0 { // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() + 1 { - let item = work_items.pop().unwrap(); - let worker_index = work_items.len(); - - let cgcx = CodegenContext { - worker: worker_index, - .. cgcx.clone() - }; - - spawn_work(cgcx, item); - running += 1; + while work_items_left > 0 && running < tokens.len() + 1 { + if let Some(item) = work_items.pop() { + work_items_left -= 1; + let worker_index = work_items_left; + + let cgcx = CodegenContext { + worker: worker_index, + .. cgcx.clone() + }; + + spawn_work(cgcx, item); + running += 1; + } else { + break + } } // Relinquish accidentally acquired extra tokens tokens.truncate(running.saturating_sub(1)); - match rx.recv().unwrap() { + match coordinator_receive.recv().unwrap() { // Save the token locally and the next turn of the loop will use // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. @@ -1228,6 +1251,10 @@ fn start_executing_work(sess: &Session, tokens.push(token.expect("failed to acquire jobserver token")); } + Message::WorkItem(work_item) => { + work_items.push(work_item); + } + // If a thread exits successfully then we drop a token associated // with that worker and update our `running` count. We may later // re-acquire a token to continue running more work. We may also not @@ -1245,8 +1272,6 @@ fn start_executing_work(sess: &Session, shared_emitter.fatal("aborting due to worker thread panic".to_string()); trans_worker_send.send(Message::CheckErrorMessages).unwrap(); } - msg @ Message::WorkItem(_) | - msg @ Message::AllWorkDone | msg @ Message::CheckErrorMessages => { bug!("unexpected message: {:?}", msg); } From 4282dd87ea79ea7836978e0b45514aea35dc1d0d Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 24 Jul 2017 16:18:11 +0200 Subject: [PATCH 07/29] async-llvm(7): Clean up error handling a bit. --- src/librustc_trans/back/write.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index ee3c9ace7dc14..5d9444218c4d3 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1248,7 +1248,13 @@ fn start_executing_work(sess: &Session, // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. Message::Token(token) => { - tokens.push(token.expect("failed to acquire jobserver token")); + if let Ok(token) = token { + tokens.push(token); + } else { + shared_emitter.fatal("failed to acquire jobserver token"); + drop(trans_worker_send.send(Message::CheckErrorMessages)); + return + } } Message::WorkItem(work_item) => { @@ -1266,11 +1272,12 @@ fn start_executing_work(sess: &Session, Message::Done { success: true } => { drop(tokens.pop()); running -= 1; - trans_worker_send.send(Message::CheckErrorMessages).unwrap(); + drop(trans_worker_send.send(Message::CheckErrorMessages)); } Message::Done { success: false } => { - shared_emitter.fatal("aborting due to worker thread panic".to_string()); - trans_worker_send.send(Message::CheckErrorMessages).unwrap(); + shared_emitter.fatal("aborting due to worker thread panic"); + drop(trans_worker_send.send(Message::CheckErrorMessages)); + return } msg @ Message::CheckErrorMessages => { bug!("unexpected message: {:?}", msg); @@ -1440,8 +1447,8 @@ impl SharedEmitter { drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); } - fn fatal(&self, msg: String) { - drop(self.sender.send(SharedEmitterMessage::Fatal(msg))); + fn fatal(&self, msg: &str) { + drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); } } From 645841ea446ed0772835f07fb62aaeecdb06b604 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 25 Jul 2017 17:26:24 +0200 Subject: [PATCH 08/29] async-llvm(8): Clean up resource management and drop LLVM modules ASAP. --- src/librustc_driver/driver.rs | 2 - src/librustc_trans/back/write.rs | 315 ++++++++++++++++++------------- src/librustc_trans/base.rs | 27 ++- src/librustc_trans/lib.rs | 63 ++++++- 4 files changed, 256 insertions(+), 151 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 1bc3f59ed0476..ff9f666af75d2 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -237,8 +237,6 @@ pub fn compile_input(sess: &Session, phase5_result); phase5_result?; - write::cleanup_llvm(&trans); - phase_6_link_output(sess, &trans, &outputs); // Now that we won't touch anything in the incremental compilation directory diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 5d9444218c4d3..83f7f574493eb 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -18,7 +18,8 @@ use rustc::session::Session; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; -use {CrateTranslation, OngoingCrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation}; +use {CrateTranslation, OngoingCrateTranslation, ModuleSource, ModuleTranslation, + CompiledModule, ModuleKind}; use rustc::hir::def_id::CrateNum; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; use rustc::util::fs::link_or_copy; @@ -192,7 +193,6 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef { /// Module-specific configuration for `optimize_and_codegen`. -#[derive(Clone)] pub struct ModuleConfig { /// LLVM TargetMachine to use for codegen. tm: TargetMachineRef, @@ -231,9 +231,9 @@ pub struct ModuleConfig { unsafe impl Send for ModuleConfig { } impl ModuleConfig { - fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { + fn new(sess: &Session, passes: Vec) -> ModuleConfig { ModuleConfig { - tm: tm, + tm: create_target_machine(sess), passes: passes, opt_level: None, opt_size: None, @@ -281,6 +281,40 @@ impl ModuleConfig { self.merge_functions = sess.opts.optimize == config::OptLevel::Default || sess.opts.optimize == config::OptLevel::Aggressive; } + + fn clone(&self, sess: &Session) -> ModuleConfig { + ModuleConfig { + tm: create_target_machine(sess), + passes: self.passes.clone(), + opt_level: self.opt_level, + opt_size: self.opt_size, + + emit_no_opt_bc: self.emit_no_opt_bc, + emit_bc: self.emit_bc, + emit_lto_bc: self.emit_lto_bc, + emit_ir: self.emit_ir, + emit_asm: self.emit_asm, + emit_obj: self.emit_obj, + obj_is_bitcode: self.obj_is_bitcode, + + no_verify: self.no_verify, + no_prepopulate_passes: self.no_prepopulate_passes, + no_builtins: self.no_builtins, + time_passes: self.time_passes, + vectorize_loop: self.vectorize_loop, + vectorize_slp: self.vectorize_slp, + merge_functions: self.merge_functions, + inline_threshold: self.inline_threshold, + } + } +} + +impl Drop for ModuleConfig { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustDisposeTargetMachine(self.tm); + } + } } /// Additional resources used by optimize_and_codegen (not module specific) @@ -372,13 +406,17 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo unsafe fn optimize_and_codegen(cgcx: &CodegenContext, diag_handler: &Handler, mtrans: ModuleTranslation, - mllvm: ModuleLlvm, config: ModuleConfig, output_names: OutputFilenames) - -> Result<(), FatalError> + -> Result { - let llmod = mllvm.llmod; - let llcx = mllvm.llcx; + let (llmod, llcx) = match mtrans.source { + ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx), + ModuleSource::Preexisting(_) => { + bug!("optimize_and_codegen: called with ModuleSource::Preexisting") + } + }; + let tm = config.tm; let fv = HandlerFreeVars { @@ -390,7 +428,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); - let module_name = Some(&mtrans.name[..]); + let module_name = mtrans.name.clone(); + let module_name = Some(&module_name[..]); if config.emit_no_opt_bc { let out = output_names.temp_path_ext("no-opt.bc", module_name); @@ -606,30 +645,13 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } } - llvm::LLVMRustDisposeTargetMachine(tm); - Ok(()) -} - - -pub fn cleanup_llvm(trans: &CrateTranslation) { - for module in trans.modules.iter() { - unsafe { - match module.source { - ModuleSource::Translated(llvm) => { - llvm::LLVMDisposeModule(llvm.llmod); - llvm::LLVMContextDispose(llvm.llcx); - } - ModuleSource::Preexisting(_) => { - } - } - } - } + Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc)) } -pub struct RunLLVMPassesResult { - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub allocator_module: Option, +pub struct CompiledModules { + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, } pub fn run_passes(sess: &Session, @@ -658,13 +680,11 @@ pub fn run_passes(sess: &Session, !sess.opts.output_types.should_trans() || sess.opts.debugging_opts.no_trans); - let tm = create_target_machine(sess); - // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(tm, vec![]); - let mut allocator_config = ModuleConfig::new(tm, vec![]); + let mut modules_config = ModuleConfig::new(sess, sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(sess, vec![]); + let mut allocator_config = ModuleConfig::new(sess, vec![]); if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { match *sanitizer { @@ -747,25 +767,22 @@ pub fn run_passes(sess: &Session, let mut work_items = Vec::with_capacity(1 + modules.len()); { - let work = build_work_item(sess, - metadata_module.clone(), - metadata_config.clone(), + let work = build_work_item(metadata_module, + metadata_config.clone(sess), crate_output.clone()); work_items.push(work); } - if let Some(allocator) = allocator_module.clone() { - let work = build_work_item(sess, - allocator, - allocator_config.clone(), + if let Some(allocator) = allocator_module { + let work = build_work_item(allocator, + allocator_config.clone(sess), crate_output.clone()); work_items.push(work); } - for mtrans in modules.iter() { - let work = build_work_item(sess, - mtrans.clone(), - modules_config.clone(), + for mtrans in modules { + let work = build_work_item(mtrans, + modules_config.clone(sess), crate_output.clone()); work_items.push(work); } @@ -778,6 +795,10 @@ pub fn run_passes(sess: &Session, Client::new(num_workers).expect("failed to create jobserver") }); + drop(modules_config); + drop(metadata_config); + drop(allocator_config); + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (trans_worker_send, trans_worker_receive) = channel(); let (coordinator_send, coordinator_receive) = channel(); @@ -813,37 +834,27 @@ pub fn run_passes(sess: &Session, } } - match coordinator_thread.join() { - Ok(()) => {}, - Err(err) => { - panic!("error: {:?}", err); - } - } + let compiled_modules = coordinator_thread.join().unwrap(); // Just in case, check this on the way out. shared_emitter_main.check(sess); sess.diagnostic().abort_if_errors(); // If in incr. comp. mode, preserve the `.o` files for potential re-use - for mtrans in modules.iter() { + for module in compiled_modules.modules.iter() { let mut files = vec![]; - if modules_config.emit_obj { - let path = crate_output.temp_path(OutputType::Object, Some(&mtrans.name)); + if module.emit_obj { + let path = crate_output.temp_path(OutputType::Object, Some(&module.name)); files.push((OutputType::Object, path)); } - if modules_config.emit_bc { - let path = crate_output.temp_path(OutputType::Bitcode, Some(&mtrans.name)); + if module.emit_bc { + let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name)); files.push((OutputType::Bitcode, path)); } - save_trans_partition(sess, &mtrans.name, mtrans.symbol_name_hash, &files); - } - - // All codegen is finished. - unsafe { - llvm::LLVMRustDisposeTargetMachine(tm); + save_trans_partition(sess, &module.name, module.symbol_name_hash, &files); } let mut user_wants_bitcode = false; @@ -858,10 +869,10 @@ pub fn run_passes(sess: &Session, let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { - if modules.len() == 1 { + if compiled_modules.modules.len() == 1 { // 1) Only one codegen unit. In this case it's no difficulty // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&modules[0].name[..]); + let module_name = Some(&compiled_modules.modules[0].name[..]); let path = crate_output.temp_path(output_type, module_name); copy_gracefully(&path, &crate_output.path(output_type)); @@ -962,27 +973,30 @@ pub fn run_passes(sess: &Session, let keep_numbered_objects = needs_crate_object || (user_wants_objects && sess.opts.cg.codegen_units > 1); - for module_name in modules.iter().map(|m| Some(&m.name[..])) { - if modules_config.emit_obj && !keep_numbered_objects { + for module in compiled_modules.modules.iter() { + let module_name = Some(&module.name[..]); + + if module.emit_obj && !keep_numbered_objects { let path = crate_output.temp_path(OutputType::Object, module_name); remove(sess, &path); } - if modules_config.emit_bc && !keep_numbered_bitcode { + if module.emit_bc && !keep_numbered_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, module_name); remove(sess, &path); } } - if metadata_config.emit_bc && !user_wants_bitcode { + if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, - Some(&metadata_module.name)); + Some(&compiled_modules.metadata_module.name)); remove(sess, &path); } - if allocator_config.emit_bc && !user_wants_bitcode { - if let Some(ref module) = allocator_module { + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if allocator_module.emit_bc && !user_wants_bitcode { let path = crate_output.temp_path(OutputType::Bitcode, - Some(&module.name)); + Some(&allocator_module.name)); remove(sess, &path); } } @@ -1000,21 +1014,14 @@ pub fn run_passes(sess: &Session, unsafe { llvm::LLVMRustPrintPassTimings(); } } - *trans.result.borrow_mut() = Some( - RunLLVMPassesResult { - modules, - metadata_module, - allocator_module, - } - ); + *trans.result.borrow_mut() = Some(compiled_modules); } pub fn dump_incremental_data(trans: &CrateTranslation) { let mut reuse = 0; for mtrans in trans.modules.iter() { - match mtrans.source { - ModuleSource::Preexisting(..) => reuse += 1, - ModuleSource::Translated(..) => (), + if mtrans.pre_existing { + reuse += 1; } } eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); @@ -1032,14 +1039,11 @@ impl fmt::Debug for WorkItem { } } -fn build_work_item(sess: &Session, - mtrans: ModuleTranslation, +fn build_work_item(mtrans: ModuleTranslation, config: ModuleConfig, output_names: OutputFilenames) -> WorkItem { - let mut config = config; - config.tm = create_target_machine(sess); WorkItem { mtrans: mtrans, config: config, @@ -1048,54 +1052,65 @@ fn build_work_item(sess: &Session, } fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) - -> Result<(), FatalError> + -> Result { let diag_handler = cgcx.create_diag_handler(); - unsafe { - match work_item.mtrans.source { - ModuleSource::Translated(mllvm) => { - debug!("llvm-optimizing {:?}", work_item.mtrans.name); - optimize_and_codegen(cgcx, - &diag_handler, - work_item.mtrans, - mllvm, - work_item.config, - work_item.output_names)?; - } - ModuleSource::Preexisting(wp) => { - let incr_comp_session_dir = cgcx.incr_comp_session_dir - .as_ref() - .unwrap(); - let name = &work_item.mtrans.name; - for (kind, saved_file) in wp.saved_files { - let obj_out = work_item.output_names.temp_path(kind, Some(name)); - let source_file = in_incr_comp_dir(&incr_comp_session_dir, - &saved_file); - debug!("copying pre-existing module `{}` from {:?} to {}", - work_item.mtrans.name, - source_file, - obj_out.display()); - match link_or_copy(&source_file, &obj_out) { - Ok(_) => { } - Err(err) => { - diag_handler.err(&format!("unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err)); - } - } + let module_name = work_item.mtrans.name.clone(); + + let pre_existing = match work_item.mtrans.source { + ModuleSource::Translated(_) => None, + ModuleSource::Preexisting(ref wp) => Some(wp.clone()), + }; + + if let Some(wp) = pre_existing { + let incr_comp_session_dir = cgcx.incr_comp_session_dir + .as_ref() + .unwrap(); + let name = &work_item.mtrans.name; + for (kind, saved_file) in wp.saved_files { + let obj_out = work_item.output_names.temp_path(kind, Some(name)); + let source_file = in_incr_comp_dir(&incr_comp_session_dir, + &saved_file); + debug!("copying pre-existing module `{}` from {:?} to {}", + work_item.mtrans.name, + source_file, + obj_out.display()); + match link_or_copy(&source_file, &obj_out) { + Ok(_) => { } + Err(err) => { + diag_handler.err(&format!("unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err)); } } } - } - Ok(()) + Ok(CompiledModule { + name: module_name, + kind: ModuleKind::Regular, + pre_existing: true, + symbol_name_hash: work_item.mtrans.symbol_name_hash, + emit_bc: work_item.config.emit_bc, + emit_obj: work_item.config.emit_obj, + }) + } else { + debug!("llvm-optimizing {:?}", module_name); + + unsafe { + optimize_and_codegen(cgcx, + &diag_handler, + work_item.mtrans, + work_item.config, + work_item.output_names) + } + } } #[derive(Debug)] pub enum Message { Token(io::Result), - Done { success: bool }, + Done { result: Result }, WorkItem(WorkItem), CheckErrorMessages, } @@ -1115,7 +1130,7 @@ fn start_executing_work(sess: &Session, coordinator_receive: Receiver, jobserver: Client, exported_symbols: Arc) - -> thread::JoinHandle<()> { + -> thread::JoinHandle { // First up, convert our jobserver into a helper thread so we can use normal // mpsc channels to manage our messages and such. Once we've got the helper // thread then request `n-1` tokens because all of our work items are ready @@ -1215,6 +1230,10 @@ fn start_executing_work(sess: &Session, // the jobserver. thread::spawn(move || { + let mut compiled_modules = vec![]; + let mut compiled_metadata_module = None; + let mut compiled_allocator_module = None; + let mut work_items_left = total_work_item_count; let mut work_items = Vec::with_capacity(total_work_item_count); let mut tokens = Vec::new(); @@ -1253,7 +1272,8 @@ fn start_executing_work(sess: &Session, } else { shared_emitter.fatal("failed to acquire jobserver token"); drop(trans_worker_send.send(Message::CheckErrorMessages)); - return + // Exit the coordinator thread + panic!() } } @@ -1269,21 +1289,42 @@ fn start_executing_work(sess: &Session, // // Note that if the thread failed that means it panicked, so we // abort immediately. - Message::Done { success: true } => { + Message::Done { result: Ok(compiled_module) } => { drop(tokens.pop()); running -= 1; drop(trans_worker_send.send(Message::CheckErrorMessages)); + + match compiled_module.kind { + ModuleKind::Regular => { + compiled_modules.push(compiled_module); + } + ModuleKind::Metadata => { + assert!(compiled_metadata_module.is_none()); + compiled_metadata_module = Some(compiled_module); + } + ModuleKind::Allocator => { + assert!(compiled_allocator_module.is_none()); + compiled_allocator_module = Some(compiled_module); + } + } } - Message::Done { success: false } => { + Message::Done { result: Err(()) } => { shared_emitter.fatal("aborting due to worker thread panic"); drop(trans_worker_send.send(Message::CheckErrorMessages)); - return + // Exit the coordinator thread + panic!() } msg @ Message::CheckErrorMessages => { bug!("unexpected message: {:?}", msg); } } } + + CompiledModules { + modules: compiled_modules, + metadata_module: compiled_metadata_module.unwrap(), + allocator_module: compiled_allocator_module, + } }) } @@ -1297,17 +1338,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we exit. struct Bomb { coordinator_send: Sender, - success: bool, + result: Option, } impl Drop for Bomb { fn drop(&mut self) { - drop(self.coordinator_send.send(Message::Done { success: self.success })); + let result = match self.result.take() { + Some(compiled_module) => Ok(compiled_module), + None => Err(()) + }; + + drop(self.coordinator_send.send(Message::Done { result })); } } let mut bomb = Bomb { coordinator_send: cgcx.coordinator_send.clone(), - success: false, + result: None, }; // Execute the work itself, and if it finishes successfully then flag @@ -1323,8 +1369,7 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we just ignore the result and then send off our message saying that // we're done, which if `execute_work_item` failed is unlikely to be // seen by the main thread, but hey we might as well try anyway. - drop(execute_work_item(&cgcx, work).is_err()); - bomb.success = true; + bomb.result = Some(execute_work_item(&cgcx, work).unwrap()); }); } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 5582079c78f13..7ece92ef9dd66 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -27,6 +27,7 @@ use super::OngoingCrateTranslation; use super::ModuleLlvm; use super::ModuleSource; use super::ModuleTranslation; +use super::ModuleKind; use assert_module_sources; use back::link; @@ -952,6 +953,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, llcx: metadata_llcx, llmod: metadata_llmod, }), + kind: ModuleKind::Metadata, }; let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); @@ -961,7 +963,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - return OngoingCrateTranslation { + let crate_translation = OngoingCrateTranslation { crate_name: tcx.crate_name(LOCAL_CRATE), link: link_meta, metadata: metadata, @@ -970,12 +972,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info: linker_info, windows_subsystem: None, no_integrated_as: false, - result: ::std::cell::RefCell::new(Some(::back::write::RunLLVMPassesResult { - modules: vec![], - metadata_module: metadata_module, - allocator_module: None, - })), + result: ::std::cell::RefCell::new(None), }; + + ::back::write::run_passes(tcx.sess, + &crate_translation, + vec![], + metadata_module, + None, + &output_filenames.outputs, + output_filenames); + + return crate_translation; } let exported_symbols = Arc::new(ExportedSymbols::compute(tcx, @@ -1047,7 +1055,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let module = ModuleTranslation { name: cgu_name, symbol_name_hash, - source: ModuleSource::Preexisting(buf.clone()) + source: ModuleSource::Preexisting(buf.clone()), + kind: ModuleKind::Regular, }; return (Stats::default(), module); } @@ -1108,7 +1117,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, source: ModuleSource::Translated(ModuleLlvm { llcx: ccx.llcx(), llmod: ccx.llmod(), - }) + }), + kind: ModuleKind::Regular, } }; @@ -1196,6 +1206,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, name: link::ALLOCATOR_MODULE_NAME.to_string(), symbol_name_hash: 0, // we always rebuild allocator shims source: ModuleSource::Translated(modules), + kind: ModuleKind::Allocator, }) } }); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e7debe3919b4f..1df1bd272fd83 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -135,7 +135,6 @@ mod type_; mod type_of; mod value; -#[derive(Clone)] pub struct ModuleTranslation { /// The name of the module. When the crate may be saved between /// compilations, incremental compilation requires that name be @@ -145,6 +144,58 @@ pub struct ModuleTranslation { pub name: String, pub symbol_name_hash: u64, pub source: ModuleSource, + pub kind: ModuleKind, +} + +#[derive(Copy, Clone, Debug)] +pub enum ModuleKind { + Regular, + Metadata, + Allocator, +} + +impl ModuleTranslation { + pub fn into_compiled_module(self, emit_obj: bool, emit_bc: bool) -> CompiledModule { + let pre_existing = match self.source { + ModuleSource::Preexisting(_) => true, + ModuleSource::Translated(_) => false, + }; + + CompiledModule { + name: self.name.clone(), + kind: self.kind, + symbol_name_hash: self.symbol_name_hash, + pre_existing, + emit_obj, + emit_bc, + } + } +} + +impl Drop for ModuleTranslation { + fn drop(&mut self) { + match self.source { + ModuleSource::Preexisting(_) => { + // Nothing to dispose. + }, + ModuleSource::Translated(llvm) => { + unsafe { + llvm::LLVMDisposeModule(llvm.llmod); + llvm::LLVMContextDispose(llvm.llcx); + } + }, + } + } +} + +#[derive(Debug)] +pub struct CompiledModule { + pub name: String, + pub kind: ModuleKind, + pub symbol_name_hash: u64, + pub pre_existing: bool, + pub emit_obj: bool, + pub emit_bc: bool, } #[derive(Clone)] @@ -156,7 +207,7 @@ pub enum ModuleSource { Translated(ModuleLlvm), } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct ModuleLlvm { pub llcx: llvm::ContextRef, pub llmod: llvm::ModuleRef, @@ -167,9 +218,9 @@ unsafe impl Sync for ModuleTranslation { } pub struct CrateTranslation { pub crate_name: Symbol, - pub modules: Vec, - pub metadata_module: ModuleTranslation, - pub allocator_module: Option, + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, pub link: rustc::middle::cstore::LinkMeta, pub metadata: rustc::middle::cstore::EncodedMetadata, pub exported_symbols: Arc, @@ -189,7 +240,7 @@ pub struct OngoingCrateTranslation { pub no_integrated_as: bool, // This will be replaced by a Future. - pub result: ::std::cell::RefCell>, + pub result: ::std::cell::RefCell>, } impl OngoingCrateTranslation { From ccb970be4c28490a02ce8564e7d0bd00601ad322 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 10:27:24 +0200 Subject: [PATCH 09/29] async-llvm(9): Move OngoingCrateTranslation into back::write. --- src/librustc_driver/driver.rs | 4 +- src/librustc_trans/back/write.rs | 87 +++++++++++++++++++++++++++++--- src/librustc_trans/base.rs | 2 +- src/librustc_trans/lib.rs | 63 ----------------------- 4 files changed, 83 insertions(+), 73 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ff9f666af75d2..44c046131f1bc 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1053,7 +1053,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, incremental_hashes_map: &IncrementalHashesMap, output_filenames: &OutputFilenames) - -> trans::OngoingCrateTranslation { + -> write::OngoingCrateTranslation { let time_passes = tcx.sess.time_passes(); time(time_passes, @@ -1071,7 +1071,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: trans::OngoingCrateTranslation, + trans: write::OngoingCrateTranslation, outputs: &OutputFilenames) -> (CompileResult, trans::CrateTranslation) { let trans = trans.join(sess, outputs); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 83f7f574493eb..987d88c7c6136 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -10,23 +10,25 @@ use back::lto; use back::link::{self, get_linker, remove}; +use back::linker::LinkerInfo; use back::symbol_export::ExportedSymbols; use rustc_incremental::{save_trans_partition, in_incr_comp_dir}; +use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses, AllPasses, Sanitizer}; use rustc::session::Session; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; -use {CrateTranslation, OngoingCrateTranslation, ModuleSource, ModuleTranslation, - CompiledModule, ModuleKind}; +use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; use rustc::hir::def_id::CrateNum; use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; -use rustc::util::fs::link_or_copy; +use rustc::util::fs::{link_or_copy, rename_or_copy_remove}; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError}; use errors::emitter::{Emitter}; use syntax::ext::hygiene::Mark; use syntax_pos::MultiSpan; +use syntax_pos::symbol::Symbol; use context::{is_pie_binary, get_reloc_model}; use jobserver::{Client, Acquired}; use rustc_demangle; @@ -816,7 +818,7 @@ pub fn run_passes(sess: &Session, } loop { - shared_emitter_main.check(sess); + shared_emitter_main.check(sess, false); match trans_worker_receive.recv() { Err(_) => { @@ -837,7 +839,7 @@ pub fn run_passes(sess: &Session, let compiled_modules = coordinator_thread.join().unwrap(); // Just in case, check this on the way out. - shared_emitter_main.check(sess); + shared_emitter_main.check(sess, false); sess.diagnostic().abort_if_errors(); // If in incr. comp. mode, preserve the `.o` files for potential re-use @@ -1516,9 +1518,21 @@ impl Emitter for SharedEmitter { } impl SharedEmitterMain { - pub fn check(&self, sess: &Session) { + pub fn check(&self, sess: &Session, blocking: bool) { loop { - match self.receiver.try_recv() { + let message = if blocking { + match self.receiver.recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + } else { + match self.receiver.try_recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + }; + + match message { Ok(SharedEmitterMessage::Diagnostic(diag)) => { let handler = sess.diagnostic(); match diag.code { @@ -1555,3 +1569,62 @@ impl SharedEmitterMain { } } } + +pub struct OngoingCrateTranslation { + pub crate_name: Symbol, + pub link: LinkMeta, + pub metadata: EncodedMetadata, + pub exported_symbols: Arc, + pub no_builtins: bool, + pub windows_subsystem: Option, + pub linker_info: LinkerInfo, + pub no_integrated_as: bool, + + // This will be replaced by a Future. + pub result: ::std::cell::RefCell>, +} + +impl OngoingCrateTranslation { + pub fn join(self, + sess: &Session, + outputs: &OutputFilenames) + -> CrateTranslation { + + let result = self.result.borrow_mut().take().unwrap(); + + let trans = CrateTranslation { + crate_name: self.crate_name, + link: self.link, + metadata: self.metadata, + exported_symbols: self.exported_symbols, + no_builtins: self.no_builtins, + windows_subsystem: self.windows_subsystem, + linker_info: self.linker_info, + + modules: result.modules, + metadata_module: result.metadata_module, + allocator_module: result.allocator_module, + }; + + if self.no_integrated_as { + run_assembler(sess, outputs); + + // HACK the linker expects the object file to be named foo.0.o but + // `run_assembler` produces an object named just foo.o. Rename it if we + // are going to build an executable + if sess.opts.output_types.contains_key(&OutputType::Exe) { + let f = outputs.path(OutputType::Object); + rename_or_copy_remove(&f, + f.with_file_name(format!("{}.0.o", + f.file_stem().unwrap().to_string_lossy()))).unwrap(); + } + + // Remove assembly source, unless --save-temps was specified + if !sess.opts.cg.save_temps { + fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap(); + } + } + + trans + } +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 7ece92ef9dd66..6eb38dc52eec1 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -23,7 +23,6 @@ //! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, //! int) and rec(x=int, y=int, z=int) will have the same TypeRef. -use super::OngoingCrateTranslation; use super::ModuleLlvm; use super::ModuleSource; use super::ModuleTranslation; @@ -33,6 +32,7 @@ use assert_module_sources; use back::link; use back::linker::LinkerInfo; use back::symbol_export::{self, ExportedSymbols}; +use back::write::OngoingCrateTranslation; use llvm::{ContextRef, Linkage, ModuleRef, ValueRef, Vector, get_param}; use llvm; use metadata; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 1df1bd272fd83..62ff1535be956 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -35,11 +35,7 @@ #![feature(conservative_impl_trait)] use rustc::dep_graph::WorkProduct; -use rustc::session::Session; -use rustc::session::config::{OutputType, OutputFilenames}; -use rustc::util::fs::rename_or_copy_remove; use syntax_pos::symbol::Symbol; -use std::fs; use std::sync::Arc; extern crate flate2; @@ -229,63 +225,4 @@ pub struct CrateTranslation { pub linker_info: back::linker::LinkerInfo } -pub struct OngoingCrateTranslation { - pub crate_name: Symbol, - pub link: rustc::middle::cstore::LinkMeta, - pub metadata: rustc::middle::cstore::EncodedMetadata, - pub exported_symbols: Arc, - pub no_builtins: bool, - pub windows_subsystem: Option, - pub linker_info: back::linker::LinkerInfo, - pub no_integrated_as: bool, - - // This will be replaced by a Future. - pub result: ::std::cell::RefCell>, -} - -impl OngoingCrateTranslation { - pub fn join(self, - sess: &Session, - outputs: &OutputFilenames) - -> CrateTranslation { - - let result = self.result.borrow_mut().take().unwrap(); - - let trans = CrateTranslation { - crate_name: self.crate_name, - link: self.link, - metadata: self.metadata, - exported_symbols: self.exported_symbols, - no_builtins: self.no_builtins, - windows_subsystem: self.windows_subsystem, - linker_info: self.linker_info, - - modules: result.modules, - metadata_module: result.metadata_module, - allocator_module: result.allocator_module, - }; - - if self.no_integrated_as { - back::write::run_assembler(sess, outputs); - - // HACK the linker expects the object file to be named foo.0.o but - // `run_assembler` produces an object named just foo.o. Rename it if we - // are going to build an executable - if sess.opts.output_types.contains_key(&OutputType::Exe) { - let f = outputs.path(OutputType::Object); - rename_or_copy_remove(&f, - f.with_file_name(format!("{}.0.o", - f.file_stem().unwrap().to_string_lossy()))).unwrap(); - } - - // Remove assembly source, unless --save-temps was specified - if !sess.opts.cg.save_temps { - fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap(); - } - } - - trans - } -} - __build_diagnostic_array! { librustc_trans, DIAGNOSTICS } From 28589ec3e474a7cce15f761d6bcd24f80aebdee1 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 10:48:54 +0200 Subject: [PATCH 10/29] async-llvm(10): Factor compile output files cleanup into separate functions. --- src/librustc_trans/back/write.rs | 186 +++++++++++++++++-------------- 1 file changed, 101 insertions(+), 85 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 987d88c7c6136..bae50da3209d0 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -656,6 +656,11 @@ pub struct CompiledModules { pub allocator_module: Option, } +fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { + sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains_key(&OutputType::Exe) +} + pub fn run_passes(sess: &Session, trans: &OngoingCrateTranslation, modules: Vec, @@ -723,12 +728,7 @@ pub fn run_passes(sess: &Session, // Emit bitcode files for the crate if we're emitting an rlib. // Whenever an rlib is created, the bitcode is inserted into the // archive in order to allow LTO against it. - let needs_crate_bitcode = - sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains_key(&OutputType::Exe); - let needs_crate_object = - sess.opts.output_types.contains_key(&OutputType::Exe); - if needs_crate_bitcode { + if need_crate_bitcode_for_rlib(sess) { modules_config.emit_bc = true; } @@ -842,7 +842,26 @@ pub fn run_passes(sess: &Session, shared_emitter_main.check(sess, false); sess.diagnostic().abort_if_errors(); - // If in incr. comp. mode, preserve the `.o` files for potential re-use + copy_module_artifacts_into_incr_comp_cache(sess, &compiled_modules, crate_output); + + produce_final_output_artifacts(sess, &compiled_modules, crate_output); + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { + unsafe { llvm::LLVMRustPrintPassTimings(); } + } + + *trans.result.borrow_mut() = Some(compiled_modules); +} + +fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, + compiled_modules: &CompiledModules, + crate_output: &OutputFilenames) { + if sess.opts.incremental.is_none() { + return; + } + for module in compiled_modules.modules.iter() { let mut files = vec![]; @@ -858,86 +877,88 @@ pub fn run_passes(sess: &Session, save_trans_partition(sess, &module.name, module.symbol_name_hash, &files); } +} +fn produce_final_output_artifacts(sess: &Session, + compiled_modules: &CompiledModules, + crate_output: &OutputFilenames) { let mut user_wants_bitcode = false; let mut user_wants_objects = false; - { - // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); - } - }; - let copy_if_one_unit = |output_type: OutputType, - keep_numbered: bool| { - if compiled_modules.modules.len() == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&compiled_modules.modules[0].name[..]); - let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, - &crate_output.path(output_type)); - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.#module-name#.x`. - remove(sess, &path); - } + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &Path| { + if let Err(e) = fs::copy(from, to) { + sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); + } + }; + + let copy_if_one_unit = |output_type: OutputType, + keep_numbered: bool| { + if compiled_modules.modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&compiled_modules.modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + copy_gracefully(&path, + &crate_output.path(output_type)); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + remove(sess, &path); + } + } else { + let ext = crate_output.temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_key(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring emit path because multiple .{} files \ + were produced", ext)); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring -o because multiple .{} files \ + were produced", ext)); } else { - let ext = crate_output.temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - - if crate_output.outputs.contains_key(&output_type) { - // 2) Multiple codegen units, with `--emit foo=some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring emit path because multiple .{} files \ - were produced", ext)); - } else if crate_output.single_output_file.is_some() { - // 3) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring -o because multiple .{} files \ - were produced", ext)); - } else { - // 4) Multiple codegen units, but no explicit name. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) } - }; + } + }; - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. - for output_type in output_types.keys() { - match *output_type { - OutputType::Bitcode => { - user_wants_bitcode = true; - // Copy to .bc, but always keep the .0.bc. There is a later - // check to figure out if we should delete .0.bc files, or keep - // them for making an rlib. - copy_if_one_unit(OutputType::Bitcode, true); - } - OutputType::LlvmAssembly => { - copy_if_one_unit(OutputType::LlvmAssembly, false); - } - OutputType::Assembly => { - copy_if_one_unit(OutputType::Assembly, false); - } - OutputType::Object => { - user_wants_objects = true; - copy_if_one_unit(OutputType::Object, true); - } - OutputType::Mir | - OutputType::Metadata | - OutputType::Exe | - OutputType::DepInfo => {} + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in crate_output.outputs.keys() { + match *output_type { + OutputType::Bitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + copy_if_one_unit(OutputType::LlvmAssembly, false); } + OutputType::Assembly => { + copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | + OutputType::Metadata | + OutputType::Exe | + OutputType::DepInfo => {} } } - let user_wants_bitcode = user_wants_bitcode; // Clean up unwanted temporary files. @@ -969,6 +990,9 @@ pub fn run_passes(sess: &Session, // If you change how this works, also update back::link::link_rlib, // where .#module-name#.bc files are (maybe) deleted after making an // rlib. + let needs_crate_bitcode = need_crate_bitcode_for_rlib(sess); + let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); + let keep_numbered_bitcode = needs_crate_bitcode || (user_wants_bitcode && sess.opts.cg.codegen_units > 1); @@ -1009,14 +1033,6 @@ pub fn run_passes(sess: &Session, // - #crate#.crate.metadata.o // - #crate#.bc // These are used in linking steps and will be cleaned up afterward. - - // FIXME: time_llvm_passes support - does this use a global context or - // something? - if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { - unsafe { llvm::LLVMRustPrintPassTimings(); } - } - - *trans.result.borrow_mut() = Some(compiled_modules); } pub fn dump_incremental_data(trans: &CrateTranslation) { From f3ce50558f6ba3f42011833a36a43f8026bf4863 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 11:41:34 +0200 Subject: [PATCH 11/29] async-llvm(11): Delay joining ongoing translation until right before linking. --- src/librustc_trans/back/write.rs | 105 +++++++++++++++---------------- src/librustc_trans/base.rs | 103 +++++++++++++++--------------- 2 files changed, 98 insertions(+), 110 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index bae50da3209d0..c33d65e3e5369 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -259,10 +259,10 @@ impl ModuleConfig { } } - fn set_flags(&mut self, sess: &Session, trans: &OngoingCrateTranslation) { + fn set_flags(&mut self, sess: &Session, no_builtins: bool) { self.no_verify = sess.no_verify(); self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = trans.no_builtins; + self.no_builtins = no_builtins; self.time_passes = sess.time_passes(); self.inline_threshold = sess.opts.cg.inline_threshold; self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; @@ -662,12 +662,21 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { } pub fn run_passes(sess: &Session, - trans: &OngoingCrateTranslation, modules: Vec, metadata_module: ModuleTranslation, allocator_module: Option, - output_types: &OutputTypes, - crate_output: &OutputFilenames) { + output_types_override: &OutputTypes, + crate_output: &OutputFilenames, + + crate_name: Symbol, + link: LinkMeta, + metadata: EncodedMetadata, + exported_symbols: Arc, + no_builtins: bool, + windows_subsystem: Option, + linker_info: LinkerInfo, + no_integrated_as: bool) + -> OngoingCrateTranslation { // It's possible that we have `codegen_units > 1` but only one item in // `trans.modules`. We could theoretically proceed and do LTO in that // case, but it would be confusing to have the validity of @@ -732,7 +741,7 @@ pub fn run_passes(sess: &Session, modules_config.emit_bc = true; } - for output_type in output_types.keys() { + for output_type in output_types_override.keys() { match *output_type { OutputType::Bitcode => { modules_config.emit_bc = true; } OutputType::LlvmAssembly => { modules_config.emit_ir = true; } @@ -758,9 +767,9 @@ pub fn run_passes(sess: &Session, } } - modules_config.set_flags(sess, trans); - metadata_config.set_flags(sess, trans); - allocator_config.set_flags(sess, trans); + modules_config.set_flags(sess, no_builtins); + metadata_config.set_flags(sess, no_builtins); + allocator_config.set_flags(sess, no_builtins); // Populate a buffer with a list of codegen threads. Items are processed in @@ -797,12 +806,8 @@ pub fn run_passes(sess: &Session, Client::new(num_workers).expect("failed to create jobserver") }); - drop(modules_config); - drop(metadata_config); - drop(allocator_config); - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); - let (trans_worker_send, trans_worker_receive) = channel(); + let (trans_worker_send, _trans_worker_receive) = channel(); let (coordinator_send, coordinator_receive) = channel(); let coordinator_thread = start_executing_work(sess, @@ -812,47 +817,24 @@ pub fn run_passes(sess: &Session, coordinator_send.clone(), coordinator_receive, client, - trans.exported_symbols.clone()); + exported_symbols.clone()); for work_item in work_items { coordinator_send.send(Message::WorkItem(work_item)).unwrap(); } - loop { - shared_emitter_main.check(sess, false); - - match trans_worker_receive.recv() { - Err(_) => { - // An `Err` here means that all senders for this channel have - // been closed. This could happen because all work has - // completed successfully or there has been some error. - // At this point we don't care which it is. - break - } - - Ok(Message::CheckErrorMessages) => continue, - Ok(msg) => { - bug!("unexpected message {:?}", msg); - } - } - } - - let compiled_modules = coordinator_thread.join().unwrap(); - - // Just in case, check this on the way out. - shared_emitter_main.check(sess, false); - sess.diagnostic().abort_if_errors(); - - copy_module_artifacts_into_incr_comp_cache(sess, &compiled_modules, crate_output); - - produce_final_output_artifacts(sess, &compiled_modules, crate_output); - - // FIXME: time_llvm_passes support - does this use a global context or - // something? - if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { - unsafe { llvm::LLVMRustPrintPassTimings(); } + OngoingCrateTranslation { + crate_name, + link, + metadata, + exported_symbols, + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as, + + shared_emitter_main, + future: coordinator_thread } - - *trans.result.borrow_mut() = Some(compiled_modules); } fn copy_module_artifacts_into_incr_comp_cache(sess: &Session, @@ -1596,8 +1578,8 @@ pub struct OngoingCrateTranslation { pub linker_info: LinkerInfo, pub no_integrated_as: bool, - // This will be replaced by a Future. - pub result: ::std::cell::RefCell>, + shared_emitter_main: SharedEmitterMain, + future: thread::JoinHandle, } impl OngoingCrateTranslation { @@ -1605,8 +1587,19 @@ impl OngoingCrateTranslation { sess: &Session, outputs: &OutputFilenames) -> CrateTranslation { + self.shared_emitter_main.check(sess, true); + let compiled_modules = self.future.join().unwrap(); + + sess.abort_if_errors(); - let result = self.result.borrow_mut().take().unwrap(); + copy_module_artifacts_into_incr_comp_cache(sess, &compiled_modules, outputs); + produce_final_output_artifacts(sess, &compiled_modules, outputs); + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { + unsafe { llvm::LLVMRustPrintPassTimings(); } + } let trans = CrateTranslation { crate_name: self.crate_name, @@ -1617,9 +1610,9 @@ impl OngoingCrateTranslation { windows_subsystem: self.windows_subsystem, linker_info: self.linker_info, - modules: result.modules, - metadata_module: result.metadata_module, - allocator_module: result.allocator_module, + modules: compiled_modules.modules, + metadata_module: compiled_modules.metadata_module, + allocator_module: compiled_modules.allocator_module, }; if self.no_integrated_as { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 6eb38dc52eec1..65041e60fe39a 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -32,7 +32,7 @@ use assert_module_sources; use back::link; use back::linker::LinkerInfo; use back::symbol_export::{self, ExportedSymbols}; -use back::write::OngoingCrateTranslation; +use back::write::{self, OngoingCrateTranslation}; use llvm::{ContextRef, Linkage, ModuleRef, ValueRef, Vector, get_param}; use llvm; use metadata; @@ -963,27 +963,21 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - let crate_translation = OngoingCrateTranslation { - crate_name: tcx.crate_name(LOCAL_CRATE), - link: link_meta, - metadata: metadata, - exported_symbols: Arc::new(empty_exported_symbols), - no_builtins: no_builtins, - linker_info: linker_info, - windows_subsystem: None, - no_integrated_as: false, - result: ::std::cell::RefCell::new(None), - }; - - ::back::write::run_passes(tcx.sess, - &crate_translation, - vec![], - metadata_module, - None, - &output_filenames.outputs, - output_filenames); - - return crate_translation; + return write::run_passes(tcx.sess, + vec![], + metadata_module, + None, + &output_filenames.outputs, + output_filenames, + + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + Arc::new(empty_exported_symbols), + no_builtins, + None, + linker_info, + false); } let exported_symbols = Arc::new(ExportedSymbols::compute(tcx, @@ -1231,19 +1225,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (outputs.outputs.contains_key(&OutputType::Object) || outputs.outputs.contains_key(&OutputType::Exe))); - let crate_translation = OngoingCrateTranslation { - crate_name: tcx.crate_name(LOCAL_CRATE), - link: link_meta, - metadata: metadata, - exported_symbols, - no_builtins, - linker_info, - windows_subsystem, - no_integrated_as, - - result: ::std::cell::RefCell::new(None), - }; - time(sess.time_passes(), "assert dep graph", || rustc_incremental::assert_dep_graph(tcx)); @@ -1252,34 +1233,48 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "serialize dep graph", || rustc_incremental::save_dep_graph(tcx, incremental_hashes_map, - &crate_translation.metadata.hashes, - crate_translation.link.crate_hash)); + &metadata.hashes, + link_meta.crate_hash)); // --- if no_integrated_as { let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); time(sess.time_passes(), "LLVM passes", - || ::back::write::run_passes(sess, - &crate_translation, - modules, - metadata_module, - allocator_module, - &output_types, - outputs)) + || write::run_passes(sess, + modules, + metadata_module, + allocator_module, + &output_types, + outputs, + + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + exported_symbols, + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as)) } else { time(sess.time_passes(), "LLVM passes", - || ::back::write::run_passes(sess, - &crate_translation, - modules, - metadata_module, - allocator_module, - &sess.opts.output_types, - outputs)) - }; - - crate_translation + || write::run_passes(sess, + modules, + metadata_module, + allocator_module, + &sess.opts.output_types, + outputs, + + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + exported_symbols, + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as)) + } } #[inline(never)] // give this a place in the profiler From 397b2a800f7a25e81c2aaab2ac0291adbfdce3ce Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 11:50:45 +0200 Subject: [PATCH 12/29] async-llvm(12): Hide no_integrated_as logic in write::run_passes. --- src/librustc_trans/back/write.rs | 7 +++- src/librustc_trans/base.rs | 57 ++++++++++---------------------- 2 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index c33d65e3e5369..280951e0dc87c 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -665,7 +665,6 @@ pub fn run_passes(sess: &Session, modules: Vec, metadata_module: ModuleTranslation, allocator_module: Option, - output_types_override: &OutputTypes, crate_output: &OutputFilenames, crate_name: Symbol, @@ -690,6 +689,12 @@ pub fn run_passes(sess: &Session, sess.fatal("can't perform LTO when using multiple codegen units"); } + let output_types_override = if no_integrated_as { + OutputTypes::new(&[(OutputType::Assembly, None)]) + } else { + sess.opts.output_types.clone() + }; + // Sanity check assert!(modules.len() == sess.opts.cg.codegen_units || sess.opts.debugging_opts.incremental.is_some() || diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 65041e60fe39a..53fb330e364d4 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -44,7 +44,7 @@ use rustc::dep_graph::AssertDepGraphSafe; use rustc::middle::cstore::LinkMeta; use rustc::hir::map as hir_map; use rustc::util::common::time; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, OutputTypes}; +use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType}; use rustc::session::Session; use rustc_incremental::{self, IncrementalHashesMap}; use abi; @@ -967,7 +967,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, vec![], metadata_module, None, - &output_filenames.outputs, output_filenames, tcx.crate_name(LOCAL_CRATE), @@ -1237,44 +1236,22 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta.crate_hash)); // --- - if no_integrated_as { - let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); - time(sess.time_passes(), - "LLVM passes", - || write::run_passes(sess, - modules, - metadata_module, - allocator_module, - &output_types, - outputs, - - tcx.crate_name(LOCAL_CRATE), - link_meta, - metadata, - exported_symbols, - no_builtins, - windows_subsystem, - linker_info, - no_integrated_as)) - } else { - time(sess.time_passes(), - "LLVM passes", - || write::run_passes(sess, - modules, - metadata_module, - allocator_module, - &sess.opts.output_types, - outputs, - - tcx.crate_name(LOCAL_CRATE), - link_meta, - metadata, - exported_symbols, - no_builtins, - windows_subsystem, - linker_info, - no_integrated_as)) - } + time(sess.time_passes(), + "LLVM passes", + || write::run_passes(sess, + modules, + metadata_module, + allocator_module, + outputs, + + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + exported_symbols, + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as)) } #[inline(never)] // give this a place in the profiler From b924ec1484bfca00c42a8aff68d77c41d4cd1ea6 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 12:35:23 +0200 Subject: [PATCH 13/29] async-llvm(13): Submit LLVM work packages from base::trans_crate(). --- src/librustc_driver/driver.rs | 7 +-- src/librustc_trans/back/write.rs | 97 +++++++++++++++----------------- src/librustc_trans/base.rs | 73 ++++++++++++++---------- 3 files changed, 90 insertions(+), 87 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 44c046131f1bc..ba4a6c0d67dd8 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -229,7 +229,7 @@ pub fn compile_input(sess: &Session, sess.code_stats.borrow().print_type_sizes(); } - let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans, &outputs); + let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans); controller_entry_point!(after_llvm, sess, @@ -1071,10 +1071,9 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: write::OngoingCrateTranslation, - outputs: &OutputFilenames) + trans: write::OngoingCrateTranslation) -> (CompileResult, trans::CrateTranslation) { - let trans = trans.join(sess, outputs); + let trans = trans.join(sess); if sess.opts.debugging_opts.incremental_info { write::dump_incremental_data(&trans); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 280951e0dc87c..c1c85394698c9 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -662,11 +662,8 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { } pub fn run_passes(sess: &Session, - modules: Vec, - metadata_module: ModuleTranslation, - allocator_module: Option, crate_output: &OutputFilenames, - + total_work_item_count: usize, crate_name: Symbol, link: LinkMeta, metadata: EncodedMetadata, @@ -695,12 +692,6 @@ pub fn run_passes(sess: &Session, sess.opts.output_types.clone() }; - // Sanity check - assert!(modules.len() == sess.opts.cg.codegen_units || - sess.opts.debugging_opts.incremental.is_some() || - !sess.opts.output_types.should_trans() || - sess.opts.debugging_opts.no_trans); - // Figure out what we actually need to build. let mut modules_config = ModuleConfig::new(sess, sess.opts.cg.passes.clone()); @@ -776,38 +767,11 @@ pub fn run_passes(sess: &Session, metadata_config.set_flags(sess, no_builtins); allocator_config.set_flags(sess, no_builtins); - - // Populate a buffer with a list of codegen threads. Items are processed in - // LIFO order, just because it's a tiny bit simpler that way. (The order - // doesn't actually matter.) - let mut work_items = Vec::with_capacity(1 + modules.len()); - - { - let work = build_work_item(metadata_module, - metadata_config.clone(sess), - crate_output.clone()); - work_items.push(work); - } - - if let Some(allocator) = allocator_module { - let work = build_work_item(allocator, - allocator_config.clone(sess), - crate_output.clone()); - work_items.push(work); - } - - for mtrans in modules { - let work = build_work_item(mtrans, - modules_config.clone(sess), - crate_output.clone()); - work_items.push(work); - } - let client = sess.jobserver_from_env.clone().unwrap_or_else(|| { // Pick a "reasonable maximum" if we don't otherwise have a jobserver in // our environment, capping out at 32 so we don't take everything down // by hogging the process run queue. - let num_workers = cmp::min(work_items.len() - 1, 32); + let num_workers = cmp::min(total_work_item_count - 1, 32); Client::new(num_workers).expect("failed to create jobserver") }); @@ -816,16 +780,13 @@ pub fn run_passes(sess: &Session, let (coordinator_send, coordinator_receive) = channel(); let coordinator_thread = start_executing_work(sess, - work_items.len(), + total_work_item_count, shared_emitter, trans_worker_send, coordinator_send.clone(), coordinator_receive, client, exported_symbols.clone()); - for work_item in work_items { - coordinator_send.send(Message::WorkItem(work_item)).unwrap(); - } OngoingCrateTranslation { crate_name, @@ -837,6 +798,12 @@ pub fn run_passes(sess: &Session, linker_info, no_integrated_as, + regular_module_config: modules_config, + metadata_module_config: metadata_config, + allocator_module_config: allocator_config, + + output_filenames: crate_output.clone(), + coordinator_send, shared_emitter_main, future: coordinator_thread } @@ -1583,22 +1550,29 @@ pub struct OngoingCrateTranslation { pub linker_info: LinkerInfo, pub no_integrated_as: bool, + output_filenames: OutputFilenames, + regular_module_config: ModuleConfig, + metadata_module_config: ModuleConfig, + allocator_module_config: ModuleConfig, + + coordinator_send: Sender, shared_emitter_main: SharedEmitterMain, future: thread::JoinHandle, } impl OngoingCrateTranslation { - pub fn join(self, - sess: &Session, - outputs: &OutputFilenames) - -> CrateTranslation { + pub fn join(self, sess: &Session) -> CrateTranslation { self.shared_emitter_main.check(sess, true); let compiled_modules = self.future.join().unwrap(); sess.abort_if_errors(); - copy_module_artifacts_into_incr_comp_cache(sess, &compiled_modules, outputs); - produce_final_output_artifacts(sess, &compiled_modules, outputs); + copy_module_artifacts_into_incr_comp_cache(sess, + &compiled_modules, + &self.output_filenames); + produce_final_output_artifacts(sess, + &compiled_modules, + &self.output_filenames); // FIXME: time_llvm_passes support - does this use a global context or // something? @@ -1621,24 +1595,41 @@ impl OngoingCrateTranslation { }; if self.no_integrated_as { - run_assembler(sess, outputs); + run_assembler(sess, &self.output_filenames); // HACK the linker expects the object file to be named foo.0.o but // `run_assembler` produces an object named just foo.o. Rename it if we // are going to build an executable if sess.opts.output_types.contains_key(&OutputType::Exe) { - let f = outputs.path(OutputType::Object); + let f = self.output_filenames.path(OutputType::Object); rename_or_copy_remove(&f, - f.with_file_name(format!("{}.0.o", - f.file_stem().unwrap().to_string_lossy()))).unwrap(); + f.with_file_name(format!("{}.0.o", + f.file_stem().unwrap().to_string_lossy()))).unwrap(); } // Remove assembly source, unless --save-temps was specified if !sess.opts.cg.save_temps { - fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap(); + fs::remove_file(&self.output_filenames + .temp_path(OutputType::Assembly, None)).unwrap(); } } trans } + + pub fn submit_translated_module_to_llvm(&self, + sess: &Session, + mtrans: ModuleTranslation) { + let module_config = match mtrans.kind { + ModuleKind::Regular => self.regular_module_config.clone(sess), + ModuleKind::Metadata => self.metadata_module_config.clone(sess), + ModuleKind::Allocator => self.allocator_module_config.clone(sess), + }; + + let work_item = build_work_item(mtrans, + module_config, + self.output_filenames.clone()); + + drop(self.coordinator_send.send(Message::WorkItem(work_item))); + } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 53fb330e364d4..144c1efd23b7d 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -963,20 +963,22 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - return write::run_passes(tcx.sess, - vec![], - metadata_module, - None, - output_filenames, - - tcx.crate_name(LOCAL_CRATE), - link_meta, - metadata, - Arc::new(empty_exported_symbols), - no_builtins, - None, - linker_info, - false); + let ongoing_translation = write::run_passes( + tcx.sess, + output_filenames, + 1, + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + Arc::new(empty_exported_symbols), + no_builtins, + None, + linker_info, + false); + + ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); + + return ongoing_translation; } let exported_symbols = Arc::new(ExportedSymbols::compute(tcx, @@ -1236,22 +1238,33 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta.crate_hash)); // --- - time(sess.time_passes(), - "LLVM passes", - || write::run_passes(sess, - modules, - metadata_module, - allocator_module, - outputs, - - tcx.crate_name(LOCAL_CRATE), - link_meta, - metadata, - exported_symbols, - no_builtins, - windows_subsystem, - linker_info, - no_integrated_as)) + let total_module_count = modules.len() + 1 + + if allocator_module.is_some() { 1 } else { 0 }; + + let ongoing_translation = write::run_passes( + sess, + outputs, + total_module_count, + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + exported_symbols, + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as); + + ongoing_translation.submit_translated_module_to_llvm(sess, metadata_module); + + for mtrans in modules { + ongoing_translation.submit_translated_module_to_llvm(sess, mtrans); + } + + if let Some(allocator_module) = allocator_module { + ongoing_translation.submit_translated_module_to_llvm(sess, allocator_module); + } + + ongoing_translation } #[inline(never)] // give this a place in the profiler From a1be65845c1a48c724961b136cf98b4d4b5e972d Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 14:14:40 +0200 Subject: [PATCH 14/29] async-llvm(14): Move LTO/codegen-unit conflict check to beginning of compilation process. --- src/librustc/session/config.rs | 17 +++++++++++++++++ src/librustc_trans/back/write.rs | 13 ------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 8b55eb4c099ae..8c4cc20deb7b9 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1498,6 +1498,23 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) early_error(error_format, "Value for codegen units must be a positive nonzero integer"); } + // It's possible that we have `codegen_units > 1` but only one item in + // `trans.modules`. We could theoretically proceed and do LTO in that + // case, but it would be confusing to have the validity of + // `-Z lto -C codegen-units=2` depend on details of the crate being + // compiled, so we complain regardless. + if cg.lto && cg.codegen_units > 1 { + // This case is impossible to handle because LTO expects to be able + // to combine the entire crate and all its dependencies into a + // single compilation unit, but each codegen unit is in a separate + // LLVM context, so they can't easily be combined. + early_error(error_format, "can't perform LTO when using multiple codegen units"); + } + + if cg.lto && debugging_opts.incremental.is_some() { + early_error(error_format, "can't perform LTO when compiling incrementally"); + } + let mut prints = Vec::::new(); if cg.target_cpu.as_ref().map_or(false, |s| s == "help") { prints.push(PrintRequest::TargetCPUs); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index c1c85394698c9..967b7d0eb62c1 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -673,19 +673,6 @@ pub fn run_passes(sess: &Session, linker_info: LinkerInfo, no_integrated_as: bool) -> OngoingCrateTranslation { - // It's possible that we have `codegen_units > 1` but only one item in - // `trans.modules`. We could theoretically proceed and do LTO in that - // case, but it would be confusing to have the validity of - // `-Z lto -C codegen-units=2` depend on details of the crate being - // compiled, so we complain regardless. - if sess.lto() && sess.opts.cg.codegen_units > 1 { - // This case is impossible to handle because LTO expects to be able - // to combine the entire crate and all its dependencies into a - // single compilation unit, but each codegen unit is in a separate - // LLVM context, so they can't easily be combined. - sess.fatal("can't perform LTO when using multiple codegen units"); - } - let output_types_override = if no_integrated_as { OutputTypes::new(&[(OutputType::Assembly, None)]) } else { From 943a5bdf35a27239013b0aced68588034d366d0f Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 14:18:11 +0200 Subject: [PATCH 15/29] async-llvm(15): Don't require number of codegen units upfront. --- src/librustc_trans/back/write.rs | 52 +++++++++++++++----------------- src/librustc_trans/base.rs | 9 ++---- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 967b7d0eb62c1..118853b871230 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -33,7 +33,6 @@ use context::{is_pie_binary, get_reloc_model}; use jobserver::{Client, Acquired}; use rustc_demangle; -use std::cmp; use std::ffi::CString; use std::fmt; use std::fs; @@ -663,7 +662,6 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { pub fn run_passes(sess: &Session, crate_output: &OutputFilenames, - total_work_item_count: usize, crate_name: Symbol, link: LinkMeta, metadata: EncodedMetadata, @@ -758,8 +756,7 @@ pub fn run_passes(sess: &Session, // Pick a "reasonable maximum" if we don't otherwise have a jobserver in // our environment, capping out at 32 so we don't take everything down // by hogging the process run queue. - let num_workers = cmp::min(total_work_item_count - 1, 32); - Client::new(num_workers).expect("failed to create jobserver") + Client::new(32).expect("failed to create jobserver") }); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); @@ -767,14 +764,12 @@ pub fn run_passes(sess: &Session, let (coordinator_send, coordinator_receive) = channel(); let coordinator_thread = start_executing_work(sess, - total_work_item_count, shared_emitter, trans_worker_send, coordinator_send.clone(), coordinator_receive, client, exported_symbols.clone()); - OngoingCrateTranslation { crate_name, link, @@ -1072,6 +1067,7 @@ pub enum Message { Done { result: Result }, WorkItem(WorkItem), CheckErrorMessages, + TranslationDone, } @@ -1082,7 +1078,6 @@ pub struct Diagnostic { } fn start_executing_work(sess: &Session, - total_work_item_count: usize, shared_emitter: SharedEmitter, trans_worker_send: Sender, coordinator_send: Sender, @@ -1104,9 +1099,6 @@ fn start_executing_work(sess: &Session, let helper = jobserver.into_helper_thread(move |token| { drop(coordinator_send2.send(Message::Token(token))); }).expect("failed to spawn helper thread"); - for _ in 0..total_work_item_count - 1 { - helper.request_token(); - } let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(sess, &mut |cnum, path| { @@ -1193,29 +1185,25 @@ fn start_executing_work(sess: &Session, let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; - let mut work_items_left = total_work_item_count; - let mut work_items = Vec::with_capacity(total_work_item_count); + let mut translation_done = false; + let mut work_items = Vec::new(); let mut tokens = Vec::new(); let mut running = 0; - while work_items_left > 0 || running > 0 { + while !translation_done || work_items.len() > 0 || running > 0 { // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. - while work_items_left > 0 && running < tokens.len() + 1 { - if let Some(item) = work_items.pop() { - work_items_left -= 1; - let worker_index = work_items_left; - - let cgcx = CodegenContext { - worker: worker_index, - .. cgcx.clone() - }; - - spawn_work(cgcx, item); - running += 1; - } else { - break - } + while work_items.len() > 0 && running < tokens.len() + 1 { + let item = work_items.pop().unwrap(); + let worker_index = work_items.len(); + + let cgcx = CodegenContext { + worker: worker_index, + .. cgcx.clone() + }; + + spawn_work(cgcx, item); + running += 1; } // Relinquish accidentally acquired extra tokens @@ -1238,6 +1226,7 @@ fn start_executing_work(sess: &Session, Message::WorkItem(work_item) => { work_items.push(work_item); + helper.request_token(); } // If a thread exits successfully then we drop a token associated @@ -1273,6 +1262,9 @@ fn start_executing_work(sess: &Session, // Exit the coordinator thread panic!() } + Message::TranslationDone => { + translation_done = true; + } msg @ Message::CheckErrorMessages => { bug!("unexpected message: {:?}", msg); } @@ -1619,4 +1611,8 @@ impl OngoingCrateTranslation { drop(self.coordinator_send.send(Message::WorkItem(work_item))); } + + pub fn signal_translation_done(&self) { + drop(self.coordinator_send.send(Message::TranslationDone)); + } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 144c1efd23b7d..c22bc617baaa2 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -966,7 +966,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ongoing_translation = write::run_passes( tcx.sess, output_filenames, - 1, tcx.crate_name(LOCAL_CRATE), link_meta, metadata, @@ -977,6 +976,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, false); ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); + ongoing_translation.signal_translation_done(); return ongoing_translation; } @@ -1237,14 +1237,9 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &metadata.hashes, link_meta.crate_hash)); // --- - - let total_module_count = modules.len() + 1 + - if allocator_module.is_some() { 1 } else { 0 }; - let ongoing_translation = write::run_passes( sess, outputs, - total_module_count, tcx.crate_name(LOCAL_CRATE), link_meta, metadata, @@ -1264,6 +1259,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.submit_translated_module_to_llvm(sess, allocator_module); } + ongoing_translation.signal_translation_done(); + ongoing_translation } From 0ad9eaa998d597bfa9597c4d6c751cfb66ed2e7e Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 14:21:01 +0200 Subject: [PATCH 16/29] async-llvm(16): Inject allocator shim into LLVM module immediately if necessary. --- src/librustc_trans/base.rs | 39 ++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index c22bc617baaa2..6088b8e479b6c 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -989,6 +989,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let (translation_items, codegen_units) = collect_and_partition_translation_items(&shared_ccx, &exported_symbols); + assert!(codegen_units.len() <= 1 || !tcx.sess.lto()); + let translation_items = Arc::new(translation_items); let mut all_stats = Stats::default(); @@ -1106,13 +1108,27 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debuginfo::finalize(&ccx); } + let llvm_module = ModuleLlvm { + llcx: ccx.llcx(), + llmod: ccx.llmod(), + }; + + // In LTO mode we inject the allocator shim into the existing + // module. + if ccx.sess().lto() { + if let Some(kind) = ccx.sess().allocator_kind.get() { + time(ccx.sess().time_passes(), "write allocator module", || { + unsafe { + allocator::trans(ccx.tcx(), &llvm_module, kind); + } + }); + } + } + ModuleTranslation { name: cgu_name, symbol_name_hash, - source: ModuleSource::Translated(ModuleLlvm { - llcx: ccx.llcx(), - llmod: ccx.llmod(), - }), + source: ModuleSource::Translated(llvm_module), kind: ModuleKind::Regular, } }; @@ -1180,13 +1196,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // links in an object file that has allocator functions. When we're // compiling a final LTO artifact, though, there's no need to worry about // this as we're not working with this dual "rlib/dylib" functionality. - let allocator_module = tcx.sess.allocator_kind.get().and_then(|kind| unsafe { - if sess.lto() && llvm_modules.len() > 0 { - time(tcx.sess.time_passes(), "write allocator module", || { - allocator::trans(tcx, &llvm_modules[0], kind) - }); - None - } else { + let allocator_module = if tcx.sess.lto() { + None + } else if let Some(kind) = tcx.sess.allocator_kind.get() { + unsafe { let (llcx, llmod) = context::create_context_and_module(tcx.sess, "allocator"); let modules = ModuleLlvm { @@ -1204,7 +1217,9 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, kind: ModuleKind::Allocator, }) } - }); + } else { + None + }; let linker_info = LinkerInfo::new(&shared_ccx, &exported_symbols); From e7d0fa340f904829abf28907c7f1add11a65389e Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 14:29:13 +0200 Subject: [PATCH 17/29] async-llvm(17): Create MSVC __imp_ symbols immediately for each module. --- src/librustc_trans/base.rs | 66 ++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 6088b8e479b6c..9e6fe5ab4349f 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -804,7 +804,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, // code references on its own. // See #26591, #27438 fn create_imps(sess: &Session, - llvm_modules: &[ModuleLlvm]) { + llvm_module: &ModuleLlvm) { // The x86 ABI seems to require that leading underscores are added to symbol // names, so we need an extra underscore on 32-bit. There's also a leading // '\x01' here which disables LLVM's symbol mangling (e.g. no extra @@ -815,28 +815,26 @@ fn create_imps(sess: &Session, "\x01__imp_" }; unsafe { - for ll in llvm_modules { - let exported: Vec<_> = iter_globals(ll.llmod) - .filter(|&val| { - llvm::LLVMRustGetLinkage(val) == - llvm::Linkage::ExternalLinkage && - llvm::LLVMIsDeclaration(val) == 0 - }) - .collect(); - - let i8p_ty = Type::i8p_llcx(ll.llcx); - for val in exported { - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name.to_bytes()); - let imp_name = CString::new(imp_name).unwrap(); - let imp = llvm::LLVMAddGlobal(ll.llmod, - i8p_ty.to_ref(), - imp_name.as_ptr() as *const _); - let init = llvm::LLVMConstBitCast(val, i8p_ty.to_ref()); - llvm::LLVMSetInitializer(imp, init); - llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); - } + let exported: Vec<_> = iter_globals(llvm_module.llmod) + .filter(|&val| { + llvm::LLVMRustGetLinkage(val) == + llvm::Linkage::ExternalLinkage && + llvm::LLVMIsDeclaration(val) == 0 + }) + .collect(); + + let i8p_ty = Type::i8p_llcx(llvm_module.llcx); + for val in exported { + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name.to_bytes()); + let imp_name = CString::new(imp_name).unwrap(); + let imp = llvm::LLVMAddGlobal(llvm_module.llmod, + i8p_ty.to_ref(), + imp_name.as_ptr() as *const _); + let init = llvm::LLVMConstBitCast(val, i8p_ty.to_ref()); + llvm::LLVMSetInitializer(imp, init); + llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); } } } @@ -1125,6 +1123,12 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } + // Adjust exported symbols for MSVC dllimport + if ccx.sess().target.target.options.is_like_msvc && + ccx.sess().crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) { + create_imps(ccx.sess(), &llvm_module); + } + ModuleTranslation { name: cgu_name, symbol_name_hash, @@ -1170,22 +1174,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let sess = shared_ccx.sess(); - // Get the list of llvm modules we created. We'll do a few wacky - // transforms on them now. - - let llvm_modules: Vec<_> = - modules.iter() - .filter_map(|module| match module.source { - ModuleSource::Translated(llvm) => Some(llvm), - _ => None, - }) - .collect(); - - if sess.target.target.options.is_like_msvc && - sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) { - create_imps(sess, &llvm_modules); - } - // Translate an allocator shim, if any // // If LTO is enabled and we've got some previous LLVM module we translated From 7e09d1e1709d9228cc8e0deba834e6752354f107 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 15:02:53 +0200 Subject: [PATCH 18/29] async-llvm(18): Instantiate OngoingCrateTranslation before starting translation. --- src/librustc/middle/cstore.rs | 10 +- src/librustc_driver/driver.rs | 6 +- src/librustc_incremental/persist/save.rs | 4 +- src/librustc_metadata/cstore_impl.rs | 5 +- src/librustc_metadata/encoder.rs | 7 +- src/librustc_trans/base.rs | 127 +++++++++++++---------- 6 files changed, 84 insertions(+), 75 deletions(-) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 48bddf2f71759..b1f4aa69adb9f 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -50,7 +50,7 @@ pub use self::NativeLibraryKind::*; // lonely orphan structs and enums looking for a better home -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Copy)] pub struct LinkMeta { pub crate_hash: Svh, } @@ -161,15 +161,13 @@ pub struct ExternCrate { } pub struct EncodedMetadata { - pub raw_data: Vec, - pub hashes: EncodedMetadataHashes, + pub raw_data: Vec } impl EncodedMetadata { pub fn new() -> EncodedMetadata { EncodedMetadata { raw_data: Vec::new(), - hashes: EncodedMetadataHashes::new(), } } } @@ -294,7 +292,7 @@ pub trait CrateStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> EncodedMetadata; + -> (EncodedMetadata, EncodedMetadataHashes); fn metadata_encoding_version(&self) -> &[u8]; } @@ -424,7 +422,7 @@ impl CrateStore for DummyCrateStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> EncodedMetadata { + -> (EncodedMetadata, EncodedMetadataHashes) { bug!("encode_metadata") } fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ba4a6c0d67dd8..ee9d30b58fef4 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -206,7 +206,7 @@ pub fn compile_input(sess: &Session, println!("Pre-trans"); tcx.print_debug_stats(); } - let trans = phase_4_translate_to_llvm(tcx, analysis, &incremental_hashes_map, + let trans = phase_4_translate_to_llvm(tcx, analysis, incremental_hashes_map, &outputs); if log_enabled!(::log::LogLevel::Info) { @@ -1051,7 +1051,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, /// be discarded. pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, - incremental_hashes_map: &IncrementalHashesMap, + incremental_hashes_map: IncrementalHashesMap, output_filenames: &OutputFilenames) -> write::OngoingCrateTranslation { let time_passes = tcx.sess.time_passes(); @@ -1063,7 +1063,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let translation = time(time_passes, "translation", - move || trans::trans_crate(tcx, analysis, &incremental_hashes_map, output_filenames)); + move || trans::trans_crate(tcx, analysis, incremental_hashes_map, output_filenames)); translation } diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 1bdd4f851fb13..339e2bdc15734 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -34,7 +34,7 @@ use super::file_format; use super::work_product; pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - incremental_hashes_map: &IncrementalHashesMap, + incremental_hashes_map: IncrementalHashesMap, metadata_hashes: &EncodedMetadataHashes, svh: Svh) { debug!("save_dep_graph()"); @@ -51,7 +51,7 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges()); } - let mut hcx = HashContext::new(tcx, incremental_hashes_map); + let mut hcx = HashContext::new(tcx, &incremental_hashes_map); let preds = Predecessors::new(&query, &mut hcx); let mut current_metadata_hashes = FxHashMap(); diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 25079613e586d..e8b0dea1e8ac0 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -15,7 +15,8 @@ use schema; use rustc::ty::maps::QueryConfig; use rustc::middle::cstore::{CrateStore, CrateSource, LibSource, DepKind, NativeLibrary, MetadataLoader, LinkMeta, - LinkagePreference, LoadedMacro, EncodedMetadata}; + LinkagePreference, LoadedMacro, EncodedMetadata, + EncodedMetadataHashes}; use rustc::hir::def; use rustc::middle::lang_items; use rustc::session::Session; @@ -443,7 +444,7 @@ impl CrateStore for cstore::CStore { tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, reachable: &NodeSet) - -> EncodedMetadata + -> (EncodedMetadata, EncodedMetadataHashes) { encoder::encode_metadata(tcx, link_meta, reachable) } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 5d73abc3ee8b8..c35d8407c9d3c 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1638,7 +1638,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'a, 'tcx> { pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, link_meta: &LinkMeta, exported_symbols: &NodeSet) - -> EncodedMetadata + -> (EncodedMetadata, EncodedMetadataHashes) { let mut cursor = Cursor::new(vec![]); cursor.write_all(METADATA_HEADER).unwrap(); @@ -1681,10 +1681,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result[header + 2] = (pos >> 8) as u8; result[header + 3] = (pos >> 0) as u8; - EncodedMetadata { - raw_data: result, - hashes: metadata_hashes, - } + (EncodedMetadata { raw_data: result }, metadata_hashes) } pub fn get_repr_options<'a, 'tcx, 'gcx>(tcx: &TyCtxt<'a, 'tcx, 'gcx>, did: DefId) -> ReprOptions { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 9e6fe5ab4349f..bd49ad955f190 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -38,7 +38,7 @@ use llvm; use metadata; use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::lang_items::StartFnLangItem; -use rustc::middle::cstore::EncodedMetadata; +use rustc::middle::cstore::{EncodedMetadata, EncodedMetadataHashes}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::dep_graph::AssertDepGraphSafe; use rustc::middle::cstore::LinkMeta; @@ -729,7 +729,8 @@ fn contains_null(s: &str) -> bool { fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, link_meta: &LinkMeta, exported_symbols: &NodeSet) - -> (ContextRef, ModuleRef, EncodedMetadata) { + -> (ContextRef, ModuleRef, + EncodedMetadata, EncodedMetadataHashes) { use std::io::Write; use flate2::Compression; use flate2::write::DeflateEncoder; @@ -759,15 +760,18 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, }).max().unwrap(); if kind == MetadataKind::None { - return (metadata_llcx, metadata_llmod, EncodedMetadata::new()); + return (metadata_llcx, + metadata_llmod, + EncodedMetadata::new(), + EncodedMetadataHashes::new()); } let cstore = &tcx.sess.cstore; - let metadata = cstore.encode_metadata(tcx, - &link_meta, - exported_symbols); + let (metadata, hashes) = cstore.encode_metadata(tcx, + &link_meta, + exported_symbols); if kind == MetadataKind::Uncompressed { - return (metadata_llcx, metadata_llmod, metadata); + return (metadata_llcx, metadata_llmod, metadata, hashes); } assert!(kind == MetadataKind::Compressed); @@ -795,7 +799,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, let directive = CString::new(directive).unwrap(); llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) } - return (metadata_llcx, metadata_llmod, metadata); + return (metadata_llcx, metadata_llmod, metadata, hashes); } // Create a `__imp_ = &symbol` global for every public static `symbol`. @@ -919,7 +923,7 @@ pub fn find_exported_symbols(tcx: TyCtxt, reachable: &NodeSet) -> NodeSet { pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, analysis: ty::CrateAnalysis, - incremental_hashes_map: &IncrementalHashesMap, + incremental_hashes_map: IncrementalHashesMap, output_filenames: &OutputFilenames) -> OngoingCrateTranslation { // Be careful with this krate: obviously it gives access to the @@ -927,19 +931,16 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // `TransCrate`, you need to be careful to register "reads" of the // particular items that will be processed. let krate = tcx.hir.krate(); - let ty::CrateAnalysis { reachable, .. } = analysis; - let check_overflow = tcx.sess.overflow_checks(); - - let link_meta = link::build_link_meta(incremental_hashes_map); - + let link_meta = link::build_link_meta(&incremental_hashes_map); let exported_symbol_node_ids = find_exported_symbols(tcx, &reachable); + let shared_ccx = SharedCrateContext::new(tcx, check_overflow, output_filenames); // Translate the metadata. - let (metadata_llcx, metadata_llmod, metadata) = + let (metadata_llcx, metadata_llmod, metadata, metadata_incr_hashes) = time(tcx.sess.time_passes(), "write metadata", || { write_metadata(tcx, &link_meta, &exported_symbol_node_ids) }); @@ -976,6 +977,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); ongoing_translation.signal_translation_done(); + assert_and_save_dep_graph(tcx, + incremental_hashes_map, + metadata_incr_hashes, + link_meta); + return ongoing_translation; } @@ -989,6 +995,35 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, assert!(codegen_units.len() <= 1 || !tcx.sess.lto()); + let linker_info = LinkerInfo::new(&shared_ccx, &exported_symbols); + let subsystem = attr::first_attr_value_str_by_name(&krate.attrs, + "windows_subsystem"); + let windows_subsystem = subsystem.map(|subsystem| { + if subsystem != "windows" && subsystem != "console" { + tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ + `windows` and `console` are allowed", + subsystem)); + } + subsystem.to_string() + }); + + let no_integrated_as = tcx.sess.opts.cg.no_integrated_as || + (tcx.sess.target.target.options.no_integrated_as && + (output_filenames.outputs.contains_key(&OutputType::Object) || + output_filenames.outputs.contains_key(&OutputType::Exe))); + + let ongoing_translation = write::run_passes( + tcx.sess, + output_filenames, + tcx.crate_name(LOCAL_CRATE), + link_meta, + metadata, + exported_symbols.clone(), + no_builtins, + windows_subsystem, + linker_info, + no_integrated_as); + let translation_items = Arc::new(translation_items); let mut all_stats = Stats::default(); @@ -1209,48 +1244,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, None }; - let linker_info = LinkerInfo::new(&shared_ccx, &exported_symbols); - - let subsystem = attr::first_attr_value_str_by_name(&krate.attrs, - "windows_subsystem"); - let windows_subsystem = subsystem.map(|subsystem| { - if subsystem != "windows" && subsystem != "console" { - tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ - `windows` and `console` are allowed", - subsystem)); - } - subsystem.to_string() - }); - - let outputs = output_filenames; - - let no_integrated_as = sess.opts.cg.no_integrated_as || - (sess.target.target.options.no_integrated_as && - (outputs.outputs.contains_key(&OutputType::Object) || - outputs.outputs.contains_key(&OutputType::Exe))); - - time(sess.time_passes(), - "assert dep graph", - || rustc_incremental::assert_dep_graph(tcx)); - - time(sess.time_passes(), - "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx, - incremental_hashes_map, - &metadata.hashes, - link_meta.crate_hash)); - // --- - let ongoing_translation = write::run_passes( - sess, - outputs, - tcx.crate_name(LOCAL_CRATE), - link_meta, - metadata, - exported_symbols, - no_builtins, - windows_subsystem, - linker_info, - no_integrated_as); + assert_and_save_dep_graph(tcx, + incremental_hashes_map, + metadata_incr_hashes, + link_meta); ongoing_translation.submit_translated_module_to_llvm(sess, metadata_module); @@ -1267,6 +1264,22 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation } +fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + incremental_hashes_map: IncrementalHashesMap, + metadata_incr_hashes: EncodedMetadataHashes, + link_meta: LinkMeta) { + time(tcx.sess.time_passes(), + "assert dep graph", + || rustc_incremental::assert_dep_graph(tcx)); + + time(tcx.sess.time_passes(), + "serialize dep graph", + || rustc_incremental::save_dep_graph(tcx, + incremental_hashes_map, + &metadata_incr_hashes, + link_meta.crate_hash)); +} + #[inline(never)] // give this a place in the profiler fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trans_items: I) where I: Iterator> From 81b789fd879b8dcafe9d6acc06fdc71261c520fb Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 16:02:32 +0200 Subject: [PATCH 19/29] async-llvm(19): Already start LLVM while still translating. --- src/librustc_trans/assert_module_sources.rs | 36 ++++++------ src/librustc_trans/back/write.rs | 5 +- src/librustc_trans/base.rs | 62 ++++++++++----------- 3 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/librustc_trans/assert_module_sources.rs b/src/librustc_trans/assert_module_sources.rs index b5ef4aac34c89..6e661a5a8c6a4 100644 --- a/src/librustc_trans/assert_module_sources.rs +++ b/src/librustc_trans/assert_module_sources.rs @@ -37,11 +37,22 @@ use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED}; const MODULE: &'static str = "module"; const CFG: &'static str = "cfg"; -#[derive(Debug, PartialEq)] -enum Disposition { Reused, Translated } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Disposition { Reused, Translated } + +impl ModuleTranslation { + pub fn disposition(&self) -> (String, Disposition) { + let disposition = match self.source { + ModuleSource::Preexisting(_) => Disposition::Reused, + ModuleSource::Translated(_) => Disposition::Translated, + }; + + (self.name.clone(), disposition) + } +} pub(crate) fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - modules: &[ModuleTranslation]) { + modules: &[(String, Disposition)]) { let _ignore = tcx.dep_graph.in_ignore(); if tcx.sess.opts.incremental.is_none() { @@ -56,7 +67,7 @@ pub(crate) fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, struct AssertModuleSource<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - modules: &'a [ModuleTranslation], + modules: &'a [(String, Disposition)], } impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { @@ -75,15 +86,15 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { } let mname = self.field(attr, MODULE); - let mtrans = self.modules.iter().find(|mtrans| *mtrans.name == *mname.as_str()); + let mtrans = self.modules.iter().find(|&&(ref name, _)| name == mname.as_str()); let mtrans = match mtrans { Some(m) => m, None => { debug!("module name `{}` not found amongst:", mname); - for mtrans in self.modules { + for &(ref name, ref disposition) in self.modules { debug!("module named `{}` with disposition {:?}", - mtrans.name, - self.disposition(mtrans)); + name, + disposition); } self.tcx.sess.span_err( @@ -93,7 +104,7 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { } }; - let mtrans_disposition = self.disposition(mtrans); + let mtrans_disposition = mtrans.1; if disposition != mtrans_disposition { self.tcx.sess.span_err( attr.span, @@ -104,13 +115,6 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { } } - fn disposition(&self, mtrans: &ModuleTranslation) -> Disposition { - match mtrans.source { - ModuleSource::Preexisting(_) => Disposition::Reused, - ModuleSource::Translated(_) => Disposition::Translated, - } - } - fn field(&self, attr: &ast::Attribute, name: &str) -> ast::Name { for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(name) { diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 118853b871230..6c12a4989f5b2 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -678,7 +678,6 @@ pub fn run_passes(sess: &Session, }; // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(sess, sess.opts.cg.passes.clone()); let mut metadata_config = ModuleConfig::new(sess, vec![]); let mut allocator_config = ModuleConfig::new(sess, vec![]); @@ -1615,4 +1614,8 @@ impl OngoingCrateTranslation { pub fn signal_translation_done(&self) { drop(self.coordinator_send.send(Message::TranslationDone)); } + + pub fn check_for_errors(&self, sess: &Session) { + self.shared_emitter_main.check(sess, false); + } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index bd49ad955f190..0137fa086932c 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -1024,24 +1024,34 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info, no_integrated_as); + ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); + let translation_items = Arc::new(translation_items); let mut all_stats = Stats::default(); - let modules: Vec = codegen_units - .into_iter() - .map(|cgu| { - let dep_node = cgu.work_product_dep_node(); - let ((stats, module), _) = - tcx.dep_graph.with_task(dep_node, - AssertDepGraphSafe(&shared_ccx), - AssertDepGraphSafe((cgu, - translation_items.clone(), - exported_symbols.clone())), - module_translation); - all_stats.extend(stats); - module - }) - .collect(); + let mut module_dispositions = tcx.sess.opts.incremental.as_ref().map(|_| Vec::new()); + + for cgu in codegen_units.into_iter() { + ongoing_translation.check_for_errors(tcx.sess); + let dep_node = cgu.work_product_dep_node(); + let ((stats, module), _) = + tcx.dep_graph.with_task(dep_node, + AssertDepGraphSafe(&shared_ccx), + AssertDepGraphSafe((cgu, + translation_items.clone(), + exported_symbols.clone())), + module_translation); + all_stats.extend(stats); + + if let Some(ref mut module_dispositions) = module_dispositions { + module_dispositions.push(module.disposition()); + } + ongoing_translation.submit_translated_module_to_llvm(tcx.sess, module); + } + + if let Some(module_dispositions) = module_dispositions { + assert_module_sources::assert_module_sources(tcx, &module_dispositions); + } fn module_translation<'a, 'tcx>( scx: AssertDepGraphSafe<&SharedCrateContext<'a, 'tcx>>, @@ -1175,8 +1185,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (lcx.into_stats(), module) } - assert_module_sources::assert_module_sources(tcx, &modules); - symbol_names_test::report_symbol_names(tcx); if shared_ccx.sess().trans_stats() { @@ -1207,8 +1215,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - let sess = shared_ccx.sess(); - // Translate an allocator shim, if any // // If LTO is enabled and we've got some previous LLVM module we translated @@ -1244,23 +1250,17 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, None }; - assert_and_save_dep_graph(tcx, - incremental_hashes_map, - metadata_incr_hashes, - link_meta); - - ongoing_translation.submit_translated_module_to_llvm(sess, metadata_module); - - for mtrans in modules { - ongoing_translation.submit_translated_module_to_llvm(sess, mtrans); - } - if let Some(allocator_module) = allocator_module { - ongoing_translation.submit_translated_module_to_llvm(sess, allocator_module); + ongoing_translation.submit_translated_module_to_llvm(tcx.sess, allocator_module); } + ongoing_translation.check_for_errors(tcx.sess); ongoing_translation.signal_translation_done(); + assert_and_save_dep_graph(tcx, + incremental_hashes_map, + metadata_incr_hashes, + link_meta); ongoing_translation } From ab3bc584c0412e31efd7b92ef77b7cebfa555926 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 26 Jul 2017 16:12:34 +0200 Subject: [PATCH 20/29] async-llvm(20): Do some cleanup. --- src/librustc_trans/back/write.rs | 45 ++++++++++++++++---------------- src/librustc_trans/base.rs | 4 +-- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 6c12a4989f5b2..f792da01cde89 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -341,7 +341,7 @@ pub struct CodegenContext { // compiling incrementally pub incr_comp_session_dir: Option, // Channel back to the main control thread to send messages to - pub coordinator_send: Sender, + coordinator_send: Sender, } impl CodegenContext { @@ -660,17 +660,17 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { sess.opts.output_types.contains_key(&OutputType::Exe) } -pub fn run_passes(sess: &Session, - crate_output: &OutputFilenames, - crate_name: Symbol, - link: LinkMeta, - metadata: EncodedMetadata, - exported_symbols: Arc, - no_builtins: bool, - windows_subsystem: Option, - linker_info: LinkerInfo, - no_integrated_as: bool) - -> OngoingCrateTranslation { +pub fn start_async_translation(sess: &Session, + crate_output: &OutputFilenames, + crate_name: Symbol, + link: LinkMeta, + metadata: EncodedMetadata, + exported_symbols: Arc, + no_builtins: bool, + windows_subsystem: Option, + linker_info: LinkerInfo, + no_integrated_as: bool) + -> OngoingCrateTranslation { let output_types_override = if no_integrated_as { OutputTypes::new(&[(OutputType::Assembly, None)]) } else { @@ -1061,7 +1061,7 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) } #[derive(Debug)] -pub enum Message { +enum Message { Token(io::Result), Done { result: Result }, WorkItem(WorkItem), @@ -1069,8 +1069,7 @@ pub enum Message { TranslationDone, } - -pub struct Diagnostic { +struct Diagnostic { msg: String, code: Option, lvl: Level, @@ -1519,14 +1518,14 @@ impl SharedEmitterMain { } pub struct OngoingCrateTranslation { - pub crate_name: Symbol, - pub link: LinkMeta, - pub metadata: EncodedMetadata, - pub exported_symbols: Arc, - pub no_builtins: bool, - pub windows_subsystem: Option, - pub linker_info: LinkerInfo, - pub no_integrated_as: bool, + crate_name: Symbol, + link: LinkMeta, + metadata: EncodedMetadata, + exported_symbols: Arc, + no_builtins: bool, + windows_subsystem: Option, + linker_info: LinkerInfo, + no_integrated_as: bool, output_filenames: OutputFilenames, regular_module_config: ModuleConfig, diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 0137fa086932c..2e6093eb1ca37 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -962,7 +962,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let empty_exported_symbols = ExportedSymbols::empty(); let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols); - let ongoing_translation = write::run_passes( + let ongoing_translation = write::start_async_translation( tcx.sess, output_filenames, tcx.crate_name(LOCAL_CRATE), @@ -1012,7 +1012,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (output_filenames.outputs.contains_key(&OutputType::Object) || output_filenames.outputs.contains_key(&OutputType::Exe))); - let ongoing_translation = write::run_passes( + let ongoing_translation = write::start_async_translation( tcx.sess, output_filenames, tcx.crate_name(LOCAL_CRATE), From 1480be37795bd25b7d7cedf9e1ef5caf985fe38c Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 27 Jul 2017 11:51:27 +0200 Subject: [PATCH 21/29] async-llvm(21): Re-use worker-ids in order to simulate persistent worker threads. --- src/librustc_trans/back/write.rs | 35 ++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index f792da01cde89..086980777e1dd 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1063,7 +1063,10 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) #[derive(Debug)] enum Message { Token(io::Result), - Done { result: Result }, + Done { + result: Result, + worker_id: usize, + }, WorkItem(WorkItem), CheckErrorMessages, TranslationDone, @@ -1179,6 +1182,18 @@ fn start_executing_work(sess: &Session, // the jobserver. thread::spawn(move || { + let mut worker_id_counter = 0; + let mut free_worker_ids = Vec::new(); + let mut get_worker_id = |free_worker_ids: &mut Vec| { + if let Some(id) = free_worker_ids.pop() { + id + } else { + let id = worker_id_counter; + worker_id_counter += 1; + id + } + }; + let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; @@ -1186,17 +1201,19 @@ fn start_executing_work(sess: &Session, let mut translation_done = false; let mut work_items = Vec::new(); let mut tokens = Vec::new(); + let mut running = 0; + while !translation_done || work_items.len() > 0 || running > 0 { // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. while work_items.len() > 0 && running < tokens.len() + 1 { let item = work_items.pop().unwrap(); - let worker_index = work_items.len(); + let worker_id = get_worker_id(&mut free_worker_ids); let cgcx = CodegenContext { - worker: worker_index, + worker: worker_id, .. cgcx.clone() }; @@ -1235,9 +1252,10 @@ fn start_executing_work(sess: &Session, // // Note that if the thread failed that means it panicked, so we // abort immediately. - Message::Done { result: Ok(compiled_module) } => { + Message::Done { result: Ok(compiled_module), worker_id } => { drop(tokens.pop()); running -= 1; + free_worker_ids.push(worker_id); drop(trans_worker_send.send(Message::CheckErrorMessages)); match compiled_module.kind { @@ -1254,7 +1272,7 @@ fn start_executing_work(sess: &Session, } } } - Message::Done { result: Err(()) } => { + Message::Done { result: Err(()), worker_id: _ } => { shared_emitter.fatal("aborting due to worker thread panic"); drop(trans_worker_send.send(Message::CheckErrorMessages)); // Exit the coordinator thread @@ -1288,6 +1306,7 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { struct Bomb { coordinator_send: Sender, result: Option, + worker_id: usize, } impl Drop for Bomb { fn drop(&mut self) { @@ -1296,13 +1315,17 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { None => Err(()) }; - drop(self.coordinator_send.send(Message::Done { result })); + drop(self.coordinator_send.send(Message::Done { + result, + worker_id: self.worker_id, + })); } } let mut bomb = Bomb { coordinator_send: cgcx.coordinator_send.clone(), result: None, + worker_id: cgcx.worker, }; // Execute the work itself, and if it finishes successfully then flag From 88192785233d0fed6cc8702e3067f02208e62a14 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 27 Jul 2017 13:02:31 +0200 Subject: [PATCH 22/29] async-llvm(22): mw invokes mad html skillz to produce graphical LLVM timing reports. --- src/librustc/session/config.rs | 2 + src/librustc_trans/back/write.rs | 26 +++++ src/librustc_trans/base.rs | 14 +++ src/librustc_trans/lib.rs | 1 + src/librustc_trans/time_graph.rs | 181 +++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+) create mode 100644 src/librustc_trans/time_graph.rs diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 8c4cc20deb7b9..4a9fbbe6f157d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1059,6 +1059,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "choose which RELRO level to use"), nll: bool = (false, parse_bool, [UNTRACKED], "run the non-lexical lifetimes MIR pass"), + trans_time_graph: bool = (false, parse_bool, [UNTRACKED], + "generate a graphical HTML report of time spent in trans and LLVM"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 086980777e1dd..a3845cf0e8e7e 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -17,6 +17,7 @@ use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses, AllPasses, Sanitizer}; use rustc::session::Session; +use time_graph::{self, TimeGraph}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; @@ -342,6 +343,9 @@ pub struct CodegenContext { pub incr_comp_session_dir: Option, // Channel back to the main control thread to send messages to coordinator_send: Sender, + // A reference to the TimeGraph so we can register timings. None means that + // measuring is disabled. + time_graph: Option, } impl CodegenContext { @@ -662,6 +666,7 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { pub fn start_async_translation(sess: &Session, crate_output: &OutputFilenames, + time_graph: Option, crate_name: Symbol, link: LinkMeta, metadata: EncodedMetadata, @@ -768,6 +773,7 @@ pub fn start_async_translation(sess: &Session, coordinator_send.clone(), coordinator_receive, client, + time_graph.clone(), exported_symbols.clone()); OngoingCrateTranslation { crate_name, @@ -783,6 +789,7 @@ pub fn start_async_translation(sess: &Session, metadata_module_config: metadata_config, allocator_module_config: allocator_config, + time_graph, output_filenames: crate_output.clone(), coordinator_send, shared_emitter_main, @@ -1084,6 +1091,7 @@ fn start_executing_work(sess: &Session, coordinator_send: Sender, coordinator_receive: Receiver, jobserver: Client, + time_graph: Option, exported_symbols: Arc) -> thread::JoinHandle { // First up, convert our jobserver into a helper thread so we can use normal @@ -1123,6 +1131,7 @@ fn start_executing_work(sess: &Session, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), coordinator_send: coordinator_send, diag_emitter: shared_emitter.clone(), + time_graph, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1295,10 +1304,22 @@ fn start_executing_work(sess: &Session, }) } +pub const TRANS_WORKER_ID: usize = ::std::usize::MAX; +pub const TRANS_WORKER_TIMELINE: time_graph::TimelineId = + time_graph::TimelineId(TRANS_WORKER_ID); +pub const TRANS_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#DE9597", "#FED1D3", "#FDC5C7", "#B46668", "#88494B"]); +const LLVM_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#7DB67A", "#C6EEC4", "#ACDAAA", "#579354", "#3E6F3C"]); + fn spawn_work(cgcx: CodegenContext, work: WorkItem) { let depth = time_depth(); thread::spawn(move || { + let _timing_guard = cgcx.time_graph + .as_ref() + .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND)); set_time_depth(depth); // Set up a destructor which will fire off a message that we're done as @@ -1555,6 +1576,7 @@ pub struct OngoingCrateTranslation { metadata_module_config: ModuleConfig, allocator_module_config: ModuleConfig, + time_graph: Option, coordinator_send: Sender, shared_emitter_main: SharedEmitterMain, future: thread::JoinHandle, @@ -1567,6 +1589,10 @@ impl OngoingCrateTranslation { sess.abort_if_errors(); + if let Some(time_graph) = self.time_graph { + time_graph.dump(&format!("{}-timings", self.crate_name)); + } + copy_module_artifacts_into_incr_comp_cache(sess, &compiled_modules, &self.output_filenames); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 2e6093eb1ca37..0b82ac71c33d3 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -69,6 +69,7 @@ use mir; use monomorphize::{self, Instance}; use partitioning::{self, PartitioningStrategy, CodegenUnit}; use symbol_names_test; +use time_graph; use trans_item::{TransItem, DefPathBasedNames}; use type_::Type; use type_of; @@ -956,6 +957,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); + let time_graph = if tcx.sess.opts.debugging_opts.trans_time_graph { + Some(time_graph::TimeGraph::new()) + } else { + None + }; // Skip crate items and just output metadata in -Z no-trans mode. if tcx.sess.opts.debugging_opts.no_trans || @@ -965,6 +971,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ongoing_translation = write::start_async_translation( tcx.sess, output_filenames, + time_graph.clone(), tcx.crate_name(LOCAL_CRATE), link_meta, metadata, @@ -1015,6 +1022,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ongoing_translation = write::start_async_translation( tcx.sess, output_filenames, + time_graph.clone(), tcx.crate_name(LOCAL_CRATE), link_meta, metadata, @@ -1033,6 +1041,12 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, for cgu in codegen_units.into_iter() { ongoing_translation.check_for_errors(tcx.sess); + + let _timing_guard = time_graph + .as_ref() + .map(|time_graph| time_graph.start(write::TRANS_WORKER_TIMELINE, + write::TRANS_WORK_PACKAGE_KIND)); + let dep_node = cgu.work_product_dep_node(); let ((stats, module), _) = tcx.dep_graph.with_task(dep_node, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 62ff1535be956..83835cb794abf 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -125,6 +125,7 @@ mod mir; mod monomorphize; mod partitioning; mod symbol_names_test; +mod time_graph; mod trans_item; mod tvec; mod type_; diff --git a/src/librustc_trans/time_graph.rs b/src/librustc_trans/time_graph.rs new file mode 100644 index 0000000000000..e0ebe8a0933f1 --- /dev/null +++ b/src/librustc_trans/time_graph.rs @@ -0,0 +1,181 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; +use std::marker::PhantomData; +use std::sync::{Arc, Mutex}; +use std::time::Instant; +use std::io::prelude::*; +use std::fs::File; + +const OUTPUT_WIDTH_IN_PX: u64 = 1000; +const TIME_LINE_HEIGHT_IN_PX: u64 = 7; +const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 10; + +#[derive(Clone)] +struct Timing { + start: Instant, + end: Instant, + work_package_kind: WorkPackageKind, +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +pub struct TimelineId(pub usize); + +#[derive(Clone)] +struct PerThread { + timings: Vec, + open_work_package: Option<(Instant, WorkPackageKind)>, +} + +#[derive(Clone)] +pub struct TimeGraph { + data: Arc>>, +} + +#[derive(Clone, Copy)] +pub struct WorkPackageKind(pub &'static [&'static str]); + +pub struct RaiiToken { + graph: TimeGraph, + timeline: TimelineId, + // The token must not be Send: + _marker: PhantomData<*const ()> +} + + +impl Drop for RaiiToken { + fn drop(&mut self) { + self.graph.end(self.timeline); + } +} + +impl TimeGraph { + pub fn new() -> TimeGraph { + TimeGraph { + data: Arc::new(Mutex::new(HashMap::new())) + } + } + + pub fn start(&self, + timeline: TimelineId, + work_package_kind: WorkPackageKind) -> RaiiToken { + { + let mut table = self.data.lock().unwrap(); + + let mut data = table.entry(timeline).or_insert(PerThread { + timings: Vec::new(), + open_work_package: None, + }); + + assert!(data.open_work_package.is_none()); + data.open_work_package = Some((Instant::now(), work_package_kind)); + } + + RaiiToken { + graph: self.clone(), + timeline, + _marker: PhantomData, + } + } + + fn end(&self, timeline: TimelineId) { + let end = Instant::now(); + + let mut table = self.data.lock().unwrap(); + let mut data = table.get_mut(&timeline).unwrap(); + + if let Some((start, work_package_kind)) = data.open_work_package { + data.timings.push(Timing { + start, + end, + work_package_kind, + }); + } else { + bug!("end timing without start?") + } + + data.open_work_package = None; + } + + pub fn dump(&self, output_filename: &str) { + let table = self.data.lock().unwrap(); + + for data in table.values() { + assert!(data.open_work_package.is_none()); + } + + let mut timelines: Vec = + table.values().map(|data| data.clone()).collect(); + + timelines.sort_by_key(|timeline| timeline.timings[0].start); + + let earliest_instant = timelines[0].timings[0].start; + let latest_instant = timelines.iter() + .map(|timeline| timeline.timings + .last() + .unwrap() + .end) + .max() + .unwrap(); + let max_distance = distance(earliest_instant, latest_instant); + + let mut file = File::create(format!("{}.html", output_filename)).unwrap(); + + writeln!(file, "").unwrap(); + writeln!(file, "").unwrap(); + writeln!(file, "").unwrap(); + + let mut color = 0; + + for (line_index, timeline) in timelines.iter().enumerate() { + let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX; + + for span in &timeline.timings { + let start = distance(earliest_instant, span.start); + let end = distance(earliest_instant, span.end); + + let start = normalize(start, max_distance, OUTPUT_WIDTH_IN_PX); + let end = normalize(end, max_distance, OUTPUT_WIDTH_IN_PX); + + let colors = span.work_package_kind.0; + + writeln!(file, "
", + line_top, + start, + end - start, + TIME_LINE_HEIGHT_IN_PX, + colors[color % colors.len()] + ).unwrap(); + + color += 1; + } + } + + writeln!(file, "").unwrap(); + writeln!(file, "").unwrap(); + } +} + +fn distance(zero: Instant, x: Instant) -> u64 { + + let duration = x.duration_since(zero); + (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div +} + +fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 { + (max_pixels * distance) / max +} + From f5acc392e0b28295ccaff6135e12fab219b0b006 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 27 Jul 2017 16:59:30 +0200 Subject: [PATCH 23/29] async-llvm(23): Let the main thread also do LLVM work in order to reduce memory pressure. --- src/librustc_trans/back/write.rs | 177 +++++++++++++++++++++++++------ src/librustc_trans/base.rs | 139 +++++++++++++----------- 2 files changed, 218 insertions(+), 98 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index a3845cf0e8e7e..649b16f17a929 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -764,7 +764,7 @@ pub fn start_async_translation(sess: &Session, }); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); - let (trans_worker_send, _trans_worker_receive) = channel(); + let (trans_worker_send, trans_worker_receive) = channel(); let (coordinator_send, coordinator_receive) = channel(); let coordinator_thread = start_executing_work(sess, @@ -792,6 +792,7 @@ pub fn start_async_translation(sess: &Session, time_graph, output_filenames: crate_output.clone(), coordinator_send, + trans_worker_receive, shared_emitter_main, future: coordinator_thread } @@ -987,7 +988,7 @@ pub fn dump_incremental_data(trans: &CrateTranslation) { eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len()); } -pub struct WorkItem { +struct WorkItem { mtrans: ModuleTranslation, config: ModuleConfig, output_names: OutputFilenames @@ -1074,9 +1075,11 @@ enum Message { result: Result, worker_id: usize, }, - WorkItem(WorkItem), - CheckErrorMessages, - TranslationDone, + TranslationDone { + llvm_work_item: WorkItem, + is_last: bool + }, + TranslateItem, } struct Diagnostic { @@ -1085,6 +1088,13 @@ struct Diagnostic { lvl: Level, } +#[derive(PartialEq, Clone, Copy, Debug)] +enum TransWorkerState { + Idle, + Translating, + LLVMing, +} + fn start_executing_work(sess: &Session, shared_emitter: SharedEmitter, trans_worker_send: Sender, @@ -1189,7 +1199,6 @@ fn start_executing_work(sess: &Session, // Before that work finishes, however, we may acquire a token. In that case // we actually wastefully acquired the token, so we relinquish it back to // the jobserver. - thread::spawn(move || { let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); @@ -1211,13 +1220,74 @@ fn start_executing_work(sess: &Session, let mut work_items = Vec::new(); let mut tokens = Vec::new(); + let mut trans_worker_state = TransWorkerState::Idle; let mut running = 0; - while !translation_done || work_items.len() > 0 || running > 0 { + while !translation_done || + work_items.len() > 0 || + running > 0 || + trans_worker_state != TransWorkerState::Idle { + + if !translation_done { + if trans_worker_state == TransWorkerState::Idle { + // Translation is not done yet, so there are two things the + // translation worker could do: + // + // (1) Translate another CGU + // (2) Run an already translated CGU through LLVM + // + // Option (2) makes sense if there's already enough work for + // all the other workers. In that case it's better to run + // a CGU through LLVM, so its resources can be freed. + // + // However, it's not trivial to determines what "enough work + // for all the other workers" means because: + // + // (1) We don't know how long the currently working workers + // will need to finish their work package, and + // (2) we don't know how many idle workers would be available + // because that is dynamically decided by the jobserver. + // + // TODO: Come up with a useful heuristic. + if work_items.len() <= 4 { + trans_worker_send.send(Message::TranslateItem).unwrap(); + trans_worker_state = TransWorkerState::Translating; + } else { + let item = work_items.pop().unwrap(); + let cgcx = CodegenContext { + worker: TRANS_WORKER_ID, + .. cgcx.clone() + }; + trans_worker_state = TransWorkerState::LLVMing; + spawn_work(cgcx, item); + } + } + } else { + match trans_worker_state { + TransWorkerState::Idle => { + if let Some(item) = work_items.pop() { + let cgcx = CodegenContext { + worker: TRANS_WORKER_ID, + .. cgcx.clone() + }; + + trans_worker_state = TransWorkerState::LLVMing; + spawn_work(cgcx, item); + } + } + TransWorkerState::Translating => { + bug!("trans worker should not be translating after \ + translation was already completed") + } + TransWorkerState::LLVMing => { + // Already making good use of that token + } + } + } // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() + 1 { + while work_items.len() > 0 && running < tokens.len() { let item = work_items.pop().unwrap(); let worker_id = get_worker_id(&mut free_worker_ids); @@ -1231,7 +1301,7 @@ fn start_executing_work(sess: &Session, } // Relinquish accidentally acquired extra tokens - tokens.truncate(running.saturating_sub(1)); + tokens.truncate(running); match coordinator_receive.recv().unwrap() { // Save the token locally and the next turn of the loop will use @@ -1242,15 +1312,25 @@ fn start_executing_work(sess: &Session, tokens.push(token); } else { shared_emitter.fatal("failed to acquire jobserver token"); - drop(trans_worker_send.send(Message::CheckErrorMessages)); // Exit the coordinator thread panic!() } } - Message::WorkItem(work_item) => { - work_items.push(work_item); - helper.request_token(); + Message::TranslationDone { llvm_work_item, is_last } => { + work_items.insert(0, llvm_work_item); + + if is_last { + // If this is the last, don't request a token because + // the trans worker thread will be free to handle this + // immediately. + translation_done = true; + } else { + helper.request_token(); + } + + assert_eq!(trans_worker_state, TransWorkerState::Translating); + trans_worker_state = TransWorkerState::Idle; } // If a thread exits successfully then we drop a token associated @@ -1262,10 +1342,14 @@ fn start_executing_work(sess: &Session, // Note that if the thread failed that means it panicked, so we // abort immediately. Message::Done { result: Ok(compiled_module), worker_id } => { - drop(tokens.pop()); - running -= 1; - free_worker_ids.push(worker_id); - drop(trans_worker_send.send(Message::CheckErrorMessages)); + if worker_id == TRANS_WORKER_ID { + assert_eq!(trans_worker_state, TransWorkerState::LLVMing); + trans_worker_state = TransWorkerState::Idle; + } else { + drop(tokens.pop()); + running -= 1; + free_worker_ids.push(worker_id); + } match compiled_module.kind { ModuleKind::Regular => { @@ -1283,15 +1367,11 @@ fn start_executing_work(sess: &Session, } Message::Done { result: Err(()), worker_id: _ } => { shared_emitter.fatal("aborting due to worker thread panic"); - drop(trans_worker_send.send(Message::CheckErrorMessages)); // Exit the coordinator thread panic!() } - Message::TranslationDone => { - translation_done = true; - } - msg @ Message::CheckErrorMessages => { - bug!("unexpected message: {:?}", msg); + Message::TranslateItem => { + bug!("the coordinator should not receive translation requests") } } } @@ -1316,10 +1396,6 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { let depth = time_depth(); thread::spawn(move || { - let _timing_guard = cgcx.time_graph - .as_ref() - .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker), - LLVM_WORK_PACKAGE_KIND)); set_time_depth(depth); // Set up a destructor which will fire off a message that we're done as @@ -1362,7 +1438,13 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // we just ignore the result and then send off our message saying that // we're done, which if `execute_work_item` failed is unlikely to be // seen by the main thread, but hey we might as well try anyway. - bomb.result = Some(execute_work_item(&cgcx, work).unwrap()); + bomb.result = { + let _timing_guard = cgcx.time_graph + .as_ref() + .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND)); + Some(execute_work_item(&cgcx, work).unwrap()) + }; }); } @@ -1578,6 +1660,7 @@ pub struct OngoingCrateTranslation { time_graph: Option, coordinator_send: Sender, + trans_worker_receive: Receiver, shared_emitter_main: SharedEmitterMain, future: thread::JoinHandle, } @@ -1645,25 +1728,49 @@ impl OngoingCrateTranslation { pub fn submit_translated_module_to_llvm(&self, sess: &Session, - mtrans: ModuleTranslation) { + mtrans: ModuleTranslation, + is_last: bool) { let module_config = match mtrans.kind { ModuleKind::Regular => self.regular_module_config.clone(sess), ModuleKind::Metadata => self.metadata_module_config.clone(sess), ModuleKind::Allocator => self.allocator_module_config.clone(sess), }; - let work_item = build_work_item(mtrans, - module_config, - self.output_filenames.clone()); + let llvm_work_item = build_work_item(mtrans, + module_config, + self.output_filenames.clone()); - drop(self.coordinator_send.send(Message::WorkItem(work_item))); + drop(self.coordinator_send.send(Message::TranslationDone { + llvm_work_item, + is_last + })); } - pub fn signal_translation_done(&self) { - drop(self.coordinator_send.send(Message::TranslationDone)); + pub fn submit_pre_translated_module_to_llvm(&self, + sess: &Session, + mtrans: ModuleTranslation, + is_last: bool) { + self.wait_for_signal_to_translate_item(); + self.check_for_errors(sess); + self.submit_translated_module_to_llvm(sess, mtrans, is_last); } pub fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } + + pub fn wait_for_signal_to_translate_item(&self) { + match self.trans_worker_receive.recv() { + Ok(Message::TranslateItem) => { + // Nothing to do + } + Ok(message) => { + panic!("unexpected message: {:?}", message) + } + Err(_) => { + // One of the LLVM threads must have panicked, fall through so + // error handling can be reached. + } + } + } } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 0b82ac71c33d3..2d1f43aff571b 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -981,14 +981,15 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info, false); - ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); - ongoing_translation.signal_translation_done(); + ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, metadata_module, true); assert_and_save_dep_graph(tcx, incremental_hashes_map, metadata_incr_hashes, link_meta); + ongoing_translation.check_for_errors(tcx.sess); + return ongoing_translation; } @@ -1032,35 +1033,87 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, linker_info, no_integrated_as); - ongoing_translation.submit_translated_module_to_llvm(tcx.sess, metadata_module); + // Translate an allocator shim, if any + // + // If LTO is enabled and we've got some previous LLVM module we translated + // above, then we can just translate directly into that LLVM module. If not, + // however, we need to create a separate module and trans into that. Note + // that the separate translation is critical for the standard library where + // the rlib's object file doesn't have allocator functions but the dylib + // links in an object file that has allocator functions. When we're + // compiling a final LTO artifact, though, there's no need to worry about + // this as we're not working with this dual "rlib/dylib" functionality. + let allocator_module = if tcx.sess.lto() { + None + } else if let Some(kind) = tcx.sess.allocator_kind.get() { + unsafe { + let (llcx, llmod) = + context::create_context_and_module(tcx.sess, "allocator"); + let modules = ModuleLlvm { + llmod: llmod, + llcx: llcx, + }; + time(tcx.sess.time_passes(), "write allocator module", || { + allocator::trans(tcx, &modules, kind) + }); + + Some(ModuleTranslation { + name: link::ALLOCATOR_MODULE_NAME.to_string(), + symbol_name_hash: 0, // we always rebuild allocator shims + source: ModuleSource::Translated(modules), + kind: ModuleKind::Allocator, + }) + } + } else { + None + }; + + if let Some(allocator_module) = allocator_module { + ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, allocator_module, false); + } + + let codegen_unit_count = codegen_units.len(); + ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, + metadata_module, + codegen_unit_count == 0); let translation_items = Arc::new(translation_items); let mut all_stats = Stats::default(); let mut module_dispositions = tcx.sess.opts.incremental.as_ref().map(|_| Vec::new()); - for cgu in codegen_units.into_iter() { + for (cgu_index, cgu) in codegen_units.into_iter().enumerate() { + ongoing_translation.wait_for_signal_to_translate_item(); ongoing_translation.check_for_errors(tcx.sess); - let _timing_guard = time_graph - .as_ref() - .map(|time_graph| time_graph.start(write::TRANS_WORKER_TIMELINE, - write::TRANS_WORK_PACKAGE_KIND)); - - let dep_node = cgu.work_product_dep_node(); - let ((stats, module), _) = - tcx.dep_graph.with_task(dep_node, - AssertDepGraphSafe(&shared_ccx), - AssertDepGraphSafe((cgu, - translation_items.clone(), - exported_symbols.clone())), - module_translation); - all_stats.extend(stats); - - if let Some(ref mut module_dispositions) = module_dispositions { - module_dispositions.push(module.disposition()); - } - ongoing_translation.submit_translated_module_to_llvm(tcx.sess, module); + let module = { + let _timing_guard = time_graph + .as_ref() + .map(|time_graph| time_graph.start(write::TRANS_WORKER_TIMELINE, + write::TRANS_WORK_PACKAGE_KIND)); + let dep_node = cgu.work_product_dep_node(); + let ((stats, module), _) = + tcx.dep_graph.with_task(dep_node, + AssertDepGraphSafe(&shared_ccx), + AssertDepGraphSafe((cgu, + translation_items.clone(), + exported_symbols.clone())), + module_translation); + all_stats.extend(stats); + + if let Some(ref mut module_dispositions) = module_dispositions { + module_dispositions.push(module.disposition()); + } + + module + }; + + let is_last_cgu = (cgu_index + 1) == codegen_unit_count; + + ongoing_translation.submit_translated_module_to_llvm(tcx.sess, + module, + is_last_cgu); + ongoing_translation.check_for_errors(tcx.sess); } if let Some(module_dispositions) = module_dispositions { @@ -1229,47 +1282,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - // Translate an allocator shim, if any - // - // If LTO is enabled and we've got some previous LLVM module we translated - // above, then we can just translate directly into that LLVM module. If not, - // however, we need to create a separate module and trans into that. Note - // that the separate translation is critical for the standard library where - // the rlib's object file doesn't have allocator functions but the dylib - // links in an object file that has allocator functions. When we're - // compiling a final LTO artifact, though, there's no need to worry about - // this as we're not working with this dual "rlib/dylib" functionality. - let allocator_module = if tcx.sess.lto() { - None - } else if let Some(kind) = tcx.sess.allocator_kind.get() { - unsafe { - let (llcx, llmod) = - context::create_context_and_module(tcx.sess, "allocator"); - let modules = ModuleLlvm { - llmod: llmod, - llcx: llcx, - }; - time(tcx.sess.time_passes(), "write allocator module", || { - allocator::trans(tcx, &modules, kind) - }); - - Some(ModuleTranslation { - name: link::ALLOCATOR_MODULE_NAME.to_string(), - symbol_name_hash: 0, // we always rebuild allocator shims - source: ModuleSource::Translated(modules), - kind: ModuleKind::Allocator, - }) - } - } else { - None - }; - - if let Some(allocator_module) = allocator_module { - ongoing_translation.submit_translated_module_to_llvm(tcx.sess, allocator_module); - } - ongoing_translation.check_for_errors(tcx.sess); - ongoing_translation.signal_translation_done(); assert_and_save_dep_graph(tcx, incremental_hashes_map, From bd36df84a57f2719e99c691e7ed23d0264836d41 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 28 Jul 2017 14:28:08 +0200 Subject: [PATCH 24/29] async-llvm(24): Improve scheduling and documentation. --- src/Cargo.lock | 2 +- src/librustc_trans/Cargo.toml | 2 +- src/librustc_trans/back/write.rs | 279 +++++++++++++++++++++---------- src/librustc_trans/base.rs | 21 +++ src/librustc_trans/lib.rs | 2 +- 5 files changed, 215 insertions(+), 91 deletions(-) diff --git a/src/Cargo.lock b/src/Cargo.lock index 18d97972cd3e5..31742023d46f0 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1517,11 +1517,11 @@ dependencies = [ name = "rustc_trans" version = "0.0.0" dependencies = [ - "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", "jobserver 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index c7db2a9a8ae7d..ed9321cc3f3a1 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["dylib"] test = false [dependencies] -crossbeam = "0.2" +num_cpus = "1.0" flate2 = "0.2" jobserver = "0.1.5" log = "0.3" diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 649b16f17a929..4e68fa8ce40c3 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1077,7 +1077,8 @@ enum Message { }, TranslationDone { llvm_work_item: WorkItem, - is_last: bool + cost: u64, + is_last: bool, }, TranslateItem, } @@ -1089,7 +1090,7 @@ struct Diagnostic { } #[derive(PartialEq, Clone, Copy, Debug)] -enum TransWorkerState { +enum MainThreadWorkerState { Idle, Translating, LLVMing, @@ -1148,16 +1149,110 @@ fn start_executing_work(sess: &Session, // It's here that we manage parallelism, schedule work, and work with // messages coming from clients. // - // Our channel `rx` created above is a channel of messages coming from our - // various worker threads. This includes the jobserver helper thread above - // as well as the work we'll spawn off here. Each turn of this loop starts - // off by trying to spawn as much work as possible. After we've done that we - // then wait for an event and dispatch accordingly once the event is - // received. We're only done once all our work items have been drained and - // nothing is running, at which point we return back up the stack. + // There are a few environmental pre-conditions that shape how the system + // is set up: // - // ## Parallelism management + // - Error reporting only can happen on the main thread because that's the + // only place where we have access to the compiler `Session`. + // - LLVM work can be done on any thread. + // - Translation can only happen on the main thread. + // - Each thread doing substantial work most be in possession of a `Token` + // from the `Jobserver`. + // - The compiler process always holds one `Token`. Any additional `Tokens` + // have to be requested from the `Jobserver`. // + // Error Reporting + // =============== + // The error reporting restriction is handled separately from the rest: We + // set up a `SharedEmitter` the holds an open channel to the main thread. + // When an error occurs on any thread, the shared emitter will send the + // error message to the receiver main thread (`SharedEmitterMain`). The + // main thread will periodically query this error message queue and emit + // any error messages it has received. It might even abort compilation if + // has received a fatal error. In this case we rely on all other threads + // being torn down automatically with the main thread. + // Since the main thread will often be busy doing translation work, error + // reporting will be somewhat delayed, since the message queue can only be + // checked in between to work packages. + // + // Work Processing Infrastructure + // ============================== + // The work processing infrastructure knows three major actors: + // + // - the coordinator thread, + // - the main thread, and + // - LLVM worker threads + // + // The coordinator thread is running a message loop. It instructs the main + // thread about what work to do when, and it will spawn off LLVM worker + // threads as open LLVM WorkItems become available. + // + // The job of the main thread is to translate CGUs into LLVM work package + // (since the main thread is the only thread that can do this). The main + // thread will block until it receives a message from the coordinator, upon + // which it will translate one CGU, send it to the coordinator and block + // again. This way the coordinator can control what the main thread is + // doing. + // + // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is + // available, it will spawn off a new LLVM worker thread and let it process + // that a WorkItem. When a LLVM worker thread is done with its WorkItem, + // it will just shut down, which also frees all resources associated with + // the given LLVM module, and sends a message to the coordinator that the + // has been completed. + // + // Work Scheduling + // =============== + // The scheduler's goal is to minimize the time it takes to complete all + // work there is, however, we also want to keep memory consumption low + // if possible. These two goals are at odds with each other: If memory + // consumption were not an issue, we could just let the main thread produce + // LLVM WorkItems at full speed, assuring maximal utilization of + // Tokens/LLVM worker threads. However, since translation usual is faster + // than LLVM processing, the queue of LLVM WorkItems would fill up and each + // WorkItem potentially holds on to a substantial amount of memory. + // + // So the actual goal is to always produce just enough LLVM WorkItems as + // not to starve our LLVM worker threads. That means, once we have enough + // WorkItems in our queue, we can block the main thread, so it does not + // produce more until we need them. + // + // Doing LLVM Work on the Main Thread + // ---------------------------------- + // Since the main thread owns the compiler processes implicit `Token`, it is + // wasteful to keep it blocked without doing any work. Therefore, what we do + // in this case is: We spawn off an additional LLVM worker thread that helps + // reduce the queue. The work it is doing corresponds to the implicit + // `Token`. The coordinator will mark the main thread as being busy with + // LLVM work. (The actual work happens on another OS thread but we just care + // about `Tokens`, not actual threads). + // + // When any LLVM worker thread finishes while the main thread is marked as + // "busy with LLVM work", we can do a little switcheroo: We give the Token + // of the just finished thread to the LLVM worker thread that is working on + // behalf of the main thread's implicit Token, thus freeing up the main + // thread again. The coordinator can then again decide what the main thread + // should do. This allows the coordinator to make decisions at more points + // in time. + // + // Striking a Balance between Throughput and Memory Consumption + // ------------------------------------------------------------ + // Since our two goals, (1) use as many Tokens as possible and (2) keep + // memory consumption as low as possible, are in conflict with each other, + // we have to find a trade off between them. Right now, the goal is to keep + // all workers busy, which means that no worker should find the queue empty + // when it is ready to start. + // How do we do achieve this? Good question :) We actually never know how + // many `Tokens` are potentially available so it's hard to say how much to + // fill up the queue before switching the main thread to LLVM work. Also we + // currently don't have a means to estimate how long a running LLVM worker + // will still be busy with it's current WorkItem. However, we know the + // maximal count of available Tokens that makes sense (=the number of CPU + // cores), so we can take a conservative guess. The heuristic we use here + // is implemented in the `queue_full_enough()` function. + // + // Some Background on Jobservers + // ----------------------------- // It's worth also touching on the management of parallelism here. We don't // want to just spawn a thread per work item because while that's optimal // parallelism it may overload a system with too many threads or violate our @@ -1170,36 +1265,8 @@ fn start_executing_work(sess: &Session, // and whenever we're done with that work we release the semaphore. In this // manner we can ensure that the maximum number of parallel workers is // capped at any one point in time. - // - // The jobserver protocol is a little unique, however. We, as a running - // process, already have an ephemeral token assigned to us. We're not going - // to be doing any productive work in this thread though so we're going to - // give this token to a worker thread (there's no actual token to give, this - // is just conceptually). As a result you'll see a few `+1` and `-1` - // instances below, and it's about working with this ephemeral token. - // - // To acquire tokens we have our `helper` thread above which is just in a - // loop acquiring tokens and sending them to us. We then store all tokens - // locally in a `tokens` vector once they're acquired. Currently we don't - // literally send a token to a worker thread to assist with management of - // our "ephemeral token". - // - // As a result, our "spawn as much work as possible" basically means that we - // fill up the `running` counter up to the limit of the `tokens` list. - // Whenever we get a new token this'll mean a new unit of work is spawned, - // and then whenever a unit of work finishes we relinquish a token, if we - // had one, to maybe get re-acquired later. - // - // Note that there's a race which may mean that we acquire more tokens than - // we originally anticipated. For example let's say we have 2 units of work. - // First we request one token from the helper thread and then we - // immediately spawn one unit of work with our ephemeral token after. We may - // then finish the first piece of work before the token is acquired, but we - // can continue to spawn the second piece of work with our ephemeral token. - // Before that work finishes, however, we may acquire a token. In that case - // we actually wastefully acquired the token, so we relinquish it back to - // the jobserver. - thread::spawn(move || { + return thread::spawn(move || { + let max_workers = ::num_cpus::get(); let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); let mut get_worker_id = |free_worker_ids: &mut Vec| { @@ -1212,74 +1279,75 @@ fn start_executing_work(sess: &Session, } }; + // This is where we collect codegen units that have gone all the way + // through translation and LLVM. let mut compiled_modules = vec![]; let mut compiled_metadata_module = None; let mut compiled_allocator_module = None; + // This flag tracks whether all items have gone through translations let mut translation_done = false; + + // This is the queue of LLVM work items that still need processing. let mut work_items = Vec::new(); + + // This are the Jobserver Tokens we currently hold. Does not include + // the implicit Token the compiler process owns no matter what. let mut tokens = Vec::new(); - let mut trans_worker_state = TransWorkerState::Idle; + let mut main_thread_worker_state = MainThreadWorkerState::Idle; let mut running = 0; + // Run the message loop while there's still anything that needs message + // processing: while !translation_done || work_items.len() > 0 || running > 0 || - trans_worker_state != TransWorkerState::Idle { + main_thread_worker_state != MainThreadWorkerState::Idle { + // While there are still CGUs to be translated, the coordinator has + // to decide how to utilize the compiler processes implicit Token: + // For translating more CGU or for running them through LLVM. if !translation_done { - if trans_worker_state == TransWorkerState::Idle { - // Translation is not done yet, so there are two things the - // translation worker could do: - // - // (1) Translate another CGU - // (2) Run an already translated CGU through LLVM - // - // Option (2) makes sense if there's already enough work for - // all the other workers. In that case it's better to run - // a CGU through LLVM, so its resources can be freed. - // - // However, it's not trivial to determines what "enough work - // for all the other workers" means because: - // - // (1) We don't know how long the currently working workers - // will need to finish their work package, and - // (2) we don't know how many idle workers would be available - // because that is dynamically decided by the jobserver. - // - // TODO: Come up with a useful heuristic. - if work_items.len() <= 4 { + if main_thread_worker_state == MainThreadWorkerState::Idle { + if !queue_full_enough(work_items.len(), running, max_workers) { + // The queue is not full enough, translate more items: trans_worker_send.send(Message::TranslateItem).unwrap(); - trans_worker_state = TransWorkerState::Translating; + main_thread_worker_state = MainThreadWorkerState::Translating; } else { - let item = work_items.pop().unwrap(); + // The queue is full enough to not let the worker + // threads starve. Use the implicit Token to do some + // LLVM work too. + let (item, _) = work_items.pop().unwrap(); let cgcx = CodegenContext { - worker: TRANS_WORKER_ID, + worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - trans_worker_state = TransWorkerState::LLVMing; + main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } } } else { - match trans_worker_state { - TransWorkerState::Idle => { - if let Some(item) = work_items.pop() { + // In this branch, we know that everything has been translated, + // so it's just a matter of determining whether the implicit + // Token is free to use for LLVM work. + match main_thread_worker_state { + MainThreadWorkerState::Idle => { + if let Some((item, _)) = work_items.pop() { let cgcx = CodegenContext { - worker: TRANS_WORKER_ID, + worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - trans_worker_state = TransWorkerState::LLVMing; + main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } } - TransWorkerState::Translating => { + MainThreadWorkerState::Translating => { bug!("trans worker should not be translating after \ translation was already completed") } - TransWorkerState::LLVMing => { + MainThreadWorkerState::LLVMing => { // Already making good use of that token } } @@ -1288,11 +1356,10 @@ fn start_executing_work(sess: &Session, // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. while work_items.len() > 0 && running < tokens.len() { - let item = work_items.pop().unwrap(); - let worker_id = get_worker_id(&mut free_worker_ids); + let (item, _) = work_items.pop().unwrap(); let cgcx = CodegenContext { - worker: worker_id, + worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; @@ -1310,6 +1377,15 @@ fn start_executing_work(sess: &Session, Message::Token(token) => { if let Ok(token) = token { tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to translation demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; + } } else { shared_emitter.fatal("failed to acquire jobserver token"); // Exit the coordinator thread @@ -1317,8 +1393,21 @@ fn start_executing_work(sess: &Session, } } - Message::TranslationDone { llvm_work_item, is_last } => { - work_items.insert(0, llvm_work_item); + Message::TranslationDone { llvm_work_item, cost, is_last } => { + // We keep the queue sorted by estimated processing cost, + // so that more expensive items are processed earlier. This + // is good for throughput as it gives the main thread more + // time to fill up the queue and it avoids scheduling + // expensive items to the end. + // Note, however, that this is not ideal for memory + // consumption, as LLVM module sizes are not evenly + // distributed. + let insertion_index = + work_items.binary_search_by_key(&cost, |&(_, cost)| cost); + let insertion_index = match insertion_index { + Ok(idx) | Err(idx) => idx + }; + work_items.insert(insertion_index, (llvm_work_item, cost)); if is_last { // If this is the last, don't request a token because @@ -1329,8 +1418,9 @@ fn start_executing_work(sess: &Session, helper.request_token(); } - assert_eq!(trans_worker_state, TransWorkerState::Translating); - trans_worker_state = TransWorkerState::Idle; + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Translating); + main_thread_worker_state = MainThreadWorkerState::Idle; } // If a thread exits successfully then we drop a token associated @@ -1342,15 +1432,14 @@ fn start_executing_work(sess: &Session, // Note that if the thread failed that means it panicked, so we // abort immediately. Message::Done { result: Ok(compiled_module), worker_id } => { - if worker_id == TRANS_WORKER_ID { - assert_eq!(trans_worker_state, TransWorkerState::LLVMing); - trans_worker_state = TransWorkerState::Idle; + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; } else { - drop(tokens.pop()); running -= 1; - free_worker_ids.push(worker_id); } + free_worker_ids.push(worker_id); + match compiled_module.kind { ModuleKind::Regular => { compiled_modules.push(compiled_module); @@ -1381,7 +1470,16 @@ fn start_executing_work(sess: &Session, metadata_module: compiled_metadata_module.unwrap(), allocator_module: compiled_allocator_module, } - }) + }); + + // A heuristic that determines if we have enough LLVM WorkItems in the + // queue so that the main thread can do LLVM work instead of translation + fn queue_full_enough(items_in_queue: usize, + workers_running: usize, + max_workers: usize) -> bool { + // Tune me, plz. + items_in_queue >= max_workers.saturating_sub(workers_running / 2) + } } pub const TRANS_WORKER_ID: usize = ::std::usize::MAX; @@ -1729,6 +1827,7 @@ impl OngoingCrateTranslation { pub fn submit_translated_module_to_llvm(&self, sess: &Session, mtrans: ModuleTranslation, + cost: u64, is_last: bool) { let module_config = match mtrans.kind { ModuleKind::Regular => self.regular_module_config.clone(sess), @@ -1742,6 +1841,7 @@ impl OngoingCrateTranslation { drop(self.coordinator_send.send(Message::TranslationDone { llvm_work_item, + cost, is_last })); } @@ -1752,7 +1852,10 @@ impl OngoingCrateTranslation { is_last: bool) { self.wait_for_signal_to_translate_item(); self.check_for_errors(sess); - self.submit_translated_module_to_llvm(sess, mtrans, is_last); + + // These are generally cheap and won't through off scheduling. + let cost = 0; + self.submit_translated_module_to_llvm(sess, mtrans, cost, is_last); } pub fn check_for_errors(&self, sess: &Session) { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 2d1f43aff571b..e4a7634552843 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -80,6 +80,7 @@ use libc::c_uint; use std::ffi::{CStr, CString}; use std::str; use std::sync::Arc; +use std::time::Instant; use std::i32; use syntax_pos::Span; use syntax::attr; @@ -1082,10 +1083,22 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut all_stats = Stats::default(); let mut module_dispositions = tcx.sess.opts.incremental.as_ref().map(|_| Vec::new()); + // We sort the codegen units by size. This way we can schedule work for LLVM + // a bit more efficiently. Note that "size" is defined rather crudely at the + // moment as it is just the number of TransItems in the CGU, not taking into + // account the size of each TransItem. + let codegen_units = { + let mut codegen_units = codegen_units; + codegen_units.sort_by_key(|cgu| -(cgu.items().len() as isize)); + codegen_units + }; + for (cgu_index, cgu) in codegen_units.into_iter().enumerate() { ongoing_translation.wait_for_signal_to_translate_item(); ongoing_translation.check_for_errors(tcx.sess); + let start_time = Instant::now(); + let module = { let _timing_guard = time_graph .as_ref() @@ -1108,10 +1121,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, module }; + let time_to_translate = Instant::now().duration_since(start_time); + + // We assume that the cost to run LLVM on a CGU is proportional to + // the time we needed for translating it. + let cost = time_to_translate.as_secs() * 1_000_000_000 + + time_to_translate.subsec_nanos() as u64; + let is_last_cgu = (cgu_index + 1) == codegen_unit_count; ongoing_translation.submit_translated_module_to_llvm(tcx.sess, module, + cost, is_last_cgu); ongoing_translation.check_for_errors(tcx.sess); } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 83835cb794abf..5a4a5b95cf90a 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -39,7 +39,6 @@ use syntax_pos::symbol::Symbol; use std::sync::Arc; extern crate flate2; -extern crate crossbeam; extern crate libc; extern crate owning_ref; #[macro_use] extern crate rustc; @@ -55,6 +54,7 @@ extern crate rustc_const_math; extern crate rustc_bitflags; extern crate rustc_demangle; extern crate jobserver; +extern crate num_cpus; #[macro_use] extern crate log; #[macro_use] extern crate syntax; From a9a0ea921b20f64f0253235704889a2950f72535 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 31 Jul 2017 14:51:47 +0200 Subject: [PATCH 25/29] async-llvm(25): Restore -Ztime-passes output for trans and LLVM. --- src/librustc/util/common.rs | 32 +++++++++++++++++++---- src/librustc_trans/back/write.rs | 45 ++++++++++++++++++++++++++++---- src/librustc_trans/base.rs | 14 ++++++++-- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index 17564671a1e36..244b7f3596889 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -57,6 +57,32 @@ pub fn time(do_it: bool, what: &str, f: F) -> T where let rv = f(); let dur = start.elapsed(); + print_time_passes_entry_internal(what, dur); + + TIME_DEPTH.with(|slot| slot.set(old)); + + rv +} + +pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) { + if !do_it { + return + } + + let old = TIME_DEPTH.with(|slot| { + let r = slot.get(); + slot.set(r + 1); + r + }); + + print_time_passes_entry_internal(what, dur); + + TIME_DEPTH.with(|slot| slot.set(old)); +} + +fn print_time_passes_entry_internal(what: &str, dur: Duration) { + let indentation = TIME_DEPTH.with(|slot| slot.get()); + let mem_string = match get_resident() { Some(n) => { let mb = n as f64 / 1_000_000.0; @@ -65,14 +91,10 @@ pub fn time(do_it: bool, what: &str, f: F) -> T where None => "".to_owned(), }; println!("{}time: {}{}\t{}", - repeat(" ").take(old).collect::(), + repeat(" ").take(indentation).collect::(), duration_to_secs_str(dur), mem_string, what); - - TIME_DEPTH.with(|slot| slot.set(old)); - - rv } // Hack up our own formatting for the duration to make it easier for scripts diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 4e68fa8ce40c3..b3b155c88100a 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -23,7 +23,7 @@ use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::SMDiagnosticRef; use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; use rustc::hir::def_id::CrateNum; -use rustc::util::common::{time, time_depth, set_time_depth, path2cstr}; +use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry}; use rustc::util::fs::{link_or_copy, rename_or_copy_remove}; use errors::{self, Handler, Level, DiagnosticBuilder, FatalError}; use errors::emitter::{Emitter}; @@ -44,6 +44,7 @@ use std::str; use std::sync::Arc; use std::sync::mpsc::{channel, Sender, Receiver}; use std::slice; +use std::time::Instant; use std::thread; use libc::{c_uint, c_void, c_char, size_t}; @@ -498,9 +499,9 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, diag_handler.abort_if_errors(); // Finally, run the actual optimization passes - time(config.time_passes, &format!("llvm function passes [{}]", cgcx.worker), || + time(config.time_passes, &format!("llvm function passes [{}]", module_name.unwrap()), || llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(config.time_passes, &format!("llvm module passes [{}]", cgcx.worker), || + time(config.time_passes, &format!("llvm module passes [{}]", module_name.unwrap()), || llvm::LLVMRunPassManager(mpm, llmod)); // Deallocate managers that we're now done with @@ -563,7 +564,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr()); } - time(config.time_passes, &format!("codegen passes [{}]", cgcx.worker), + time(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()), || -> Result<(), FatalError> { if config.emit_ir { let out = output_names.temp_path(OutputType::LlvmAssembly, module_name); @@ -756,6 +757,11 @@ pub fn start_async_translation(sess: &Session, metadata_config.set_flags(sess, no_builtins); allocator_config.set_flags(sess, no_builtins); + // Exclude metadata and allocator modules from time_passes output, since + // they throw off the "LLVM passes" measurement. + metadata_config.time_passes = false; + allocator_config.time_passes = false; + let client = sess.jobserver_from_env.clone().unwrap_or_else(|| { // Pick a "reasonable maximum" if we don't otherwise have a jobserver in // our environment, capping out at 32 so we don't take everything down @@ -1266,6 +1272,9 @@ fn start_executing_work(sess: &Session, // manner we can ensure that the maximum number of parallel workers is // capped at any one point in time. return thread::spawn(move || { + // We pretend to be within the top-level LLVM time-passes task here: + set_time_depth(1); + let max_workers = ::num_cpus::get(); let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); @@ -1298,6 +1307,8 @@ fn start_executing_work(sess: &Session, let mut main_thread_worker_state = MainThreadWorkerState::Idle; let mut running = 0; + let mut llvm_start_time = None; + // Run the message loop while there's still anything that needs message // processing: while !translation_done || @@ -1323,6 +1334,7 @@ fn start_executing_work(sess: &Session, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; + maybe_start_llvm_timer(&item, &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } @@ -1338,7 +1350,7 @@ fn start_executing_work(sess: &Session, worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() }; - + maybe_start_llvm_timer(&item, &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } @@ -1358,6 +1370,8 @@ fn start_executing_work(sess: &Session, while work_items.len() > 0 && running < tokens.len() { let (item, _) = work_items.pop().unwrap(); + maybe_start_llvm_timer(&item, &mut llvm_start_time); + let cgcx = CodegenContext { worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() @@ -1465,6 +1479,16 @@ fn start_executing_work(sess: &Session, } } + if let Some(llvm_start_time) = llvm_start_time { + let total_llvm_time = Instant::now().duration_since(llvm_start_time); + // This is the top-level timing for all of LLVM, set the time-depth + // to zero. + set_time_depth(0); + print_time_passes_entry(cgcx.time_passes, + "LLVM passes", + total_llvm_time); + } + CompiledModules { modules: compiled_modules, metadata_module: compiled_metadata_module.unwrap(), @@ -1480,6 +1504,17 @@ fn start_executing_work(sess: &Session, // Tune me, plz. items_in_queue >= max_workers.saturating_sub(workers_running / 2) } + + fn maybe_start_llvm_timer(work_item: &WorkItem, + llvm_start_time: &mut Option) { + // We keep track of the -Ztime-passes output manually, + // since the closure-based interface does not fit well here. + if work_item.config.time_passes { + if llvm_start_time.is_none() { + *llvm_start_time = Some(Instant::now()); + } + } + } } pub const TRANS_WORKER_ID: usize = ::std::usize::MAX; diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index e4a7634552843..70283ea55c5c0 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -43,7 +43,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::dep_graph::AssertDepGraphSafe; use rustc::middle::cstore::LinkMeta; use rustc::hir::map as hir_map; -use rustc::util::common::time; +use rustc::util::common::{time, print_time_passes_entry}; use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType}; use rustc::session::Session; use rustc_incremental::{self, IncrementalHashesMap}; @@ -80,7 +80,7 @@ use libc::c_uint; use std::ffi::{CStr, CString}; use std::str; use std::sync::Arc; -use std::time::Instant; +use std::time::{Instant, Duration}; use std::i32; use syntax_pos::Span; use syntax::attr; @@ -1093,6 +1093,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, codegen_units }; + let mut total_trans_time = Duration::new(0, 0); + for (cgu_index, cgu) in codegen_units.into_iter().enumerate() { ongoing_translation.wait_for_signal_to_translate_item(); ongoing_translation.check_for_errors(tcx.sess); @@ -1128,6 +1130,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let cost = time_to_translate.as_secs() * 1_000_000_000 + time_to_translate.subsec_nanos() as u64; + total_trans_time += time_to_translate; + let is_last_cgu = (cgu_index + 1) == codegen_unit_count; ongoing_translation.submit_translated_module_to_llvm(tcx.sess, @@ -1137,6 +1141,12 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation.check_for_errors(tcx.sess); } + // Since the main thread is sometimes blocked during trans, we keep track + // -Ztime-passes output manually. + print_time_passes_entry(tcx.sess.time_passes(), + "translate to LLVM IR", + total_trans_time); + if let Some(module_dispositions) = module_dispositions { assert_module_sources::assert_module_sources(tcx, &module_dispositions); } From cacc31f8a348a97da8681c0e55dd106818b7c8cd Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 31 Jul 2017 15:41:41 +0200 Subject: [PATCH 26/29] async-llvm(26): Print error when failing to acquire Jobserver token. --- src/librustc_trans/back/write.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b3b155c88100a..85860f0e33a34 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1389,21 +1389,25 @@ fn start_executing_work(sess: &Session, // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. Message::Token(token) => { - if let Ok(token) = token { - tokens.push(token); - - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - // If the main thread token is used for LLVM work - // at the moment, we turn that thread into a regular - // LLVM worker thread, so the main thread is free - // to react to translation demand. - main_thread_worker_state = MainThreadWorkerState::Idle; - running += 1; + match token { + Ok(token) => { + tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to translation demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; + } + } + Err(e) => { + let msg = &format!("failed to acquire jobserver token: {}", e); + shared_emitter.fatal(msg); + // Exit the coordinator thread + panic!() } - } else { - shared_emitter.fatal("failed to acquire jobserver token"); - // Exit the coordinator thread - panic!() } } From b1e043e9e965392b9c018c328ec580e7bd78af24 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 31 Jul 2017 18:51:39 +0200 Subject: [PATCH 27/29] async-llvm(27): Move #[rustc_error] check to an earlier point in order to restore some test expections. --- src/librustc_trans/base.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 70283ea55c5c0..14c73de64bc79 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -650,9 +650,23 @@ pub fn set_link_section(ccx: &CrateContext, } } +// check for the #[rustc_error] annotation, which forces an +// error in trans. This is used to write compile-fail tests +// that actually test that compilation succeeds without +// reporting an error. +fn check_for_rustc_errors_attr(tcx: TyCtxt) { + if let Some((id, span)) = *tcx.sess.entry_fn.borrow() { + let main_def_id = tcx.hir.local_def_id(id); + + if tcx.has_attr(main_def_id, "rustc_error") { + tcx.sess.span_fatal(span, "compilation successful"); + } + } +} + /// Create the `main` function which will initialise the rust runtime and call /// users main function. -pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { +fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (main_def_id, span) = match *ccx.sess().entry_fn.borrow() { Some((id, span)) => { (ccx.tcx().hir.local_def_id(id), span) @@ -660,14 +674,6 @@ pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { None => return, }; - // check for the #[rustc_error] annotation, which forces an - // error in trans. This is used to write compile-fail tests - // that actually test that compilation succeeds without - // reporting an error. - if ccx.tcx().has_attr(main_def_id, "rustc_error") { - ccx.tcx().sess.span_fatal(span, "compilation successful"); - } - let instance = Instance::mono(ccx.tcx(), main_def_id); if !ccx.codegen_unit().contains_item(&TransItem::Fn(instance)) { @@ -928,6 +934,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, incremental_hashes_map: IncrementalHashesMap, output_filenames: &OutputFilenames) -> OngoingCrateTranslation { + check_for_rustc_errors_attr(tcx); + // Be careful with this krate: obviously it gives access to the // entire contents of the krate. So if you push any subtasks of // `TransCrate`, you need to be careful to register "reads" of the From b8d441350b28a6325934584ae07e3437d5cf9ad3 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 1 Aug 2017 11:04:24 +0200 Subject: [PATCH 28/29] async-llvm(28): Make some error messages more informative. --- src/librustc_trans/back/write.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 85860f0e33a34..0d5fe6c0ae95f 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -1323,13 +1323,16 @@ fn start_executing_work(sess: &Session, if main_thread_worker_state == MainThreadWorkerState::Idle { if !queue_full_enough(work_items.len(), running, max_workers) { // The queue is not full enough, translate more items: - trans_worker_send.send(Message::TranslateItem).unwrap(); + if let Err(_) = trans_worker_send.send(Message::TranslateItem) { + panic!("Could not send Message::TranslateItem to main thread") + } main_thread_worker_state = MainThreadWorkerState::Translating; } else { // The queue is full enough to not let the worker // threads starve. Use the implicit Token to do some // LLVM work too. - let (item, _) = work_items.pop().unwrap(); + let (item, _) = work_items.pop() + .expect("queue empty - queue_full_enough() broken?"); let cgcx = CodegenContext { worker: get_worker_id(&mut free_worker_ids), .. cgcx.clone() @@ -1406,7 +1409,7 @@ fn start_executing_work(sess: &Session, let msg = &format!("failed to acquire jobserver token: {}", e); shared_emitter.fatal(msg); // Exit the coordinator thread - panic!() + panic!("{}", msg) } } } @@ -1475,7 +1478,7 @@ fn start_executing_work(sess: &Session, Message::Done { result: Err(()), worker_id: _ } => { shared_emitter.fatal("aborting due to worker thread panic"); // Exit the coordinator thread - panic!() + panic!("aborting due to worker thread panic") } Message::TranslateItem => { bug!("the coordinator should not receive translation requests") @@ -1493,9 +1496,12 @@ fn start_executing_work(sess: &Session, total_llvm_time); } + let compiled_metadata_module = compiled_metadata_module + .expect("Metadata module not compiled?"); + CompiledModules { modules: compiled_modules, - metadata_module: compiled_metadata_module.unwrap(), + metadata_module: compiled_metadata_module, allocator_module: compiled_allocator_module, } }); @@ -1506,6 +1512,7 @@ fn start_executing_work(sess: &Session, workers_running: usize, max_workers: usize) -> bool { // Tune me, plz. + items_in_queue > 0 && items_in_queue >= max_workers.saturating_sub(workers_running / 2) } @@ -1805,7 +1812,12 @@ pub struct OngoingCrateTranslation { impl OngoingCrateTranslation { pub fn join(self, sess: &Session) -> CrateTranslation { self.shared_emitter_main.check(sess, true); - let compiled_modules = self.future.join().unwrap(); + let compiled_modules = match self.future.join() { + Ok(compiled_modules) => compiled_modules, + Err(_) => { + sess.fatal("Error during translation/LLVM phase."); + } + }; sess.abort_if_errors(); From 6468cad977d4c81d30ba000633eaa43bc18591f9 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 1 Aug 2017 15:57:38 +0200 Subject: [PATCH 29/29] async-llvm(29): Adapt run-make/llvm-phase test case to LLVM module not being available in memory. --- src/test/run-make/llvm-phase/test.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/run-make/llvm-phase/test.rs b/src/test/run-make/llvm-phase/test.rs index a75dc7e57a9a2..7a63871f19e38 100644 --- a/src/test/run-make/llvm-phase/test.rs +++ b/src/test/run-make/llvm-phase/test.rs @@ -54,11 +54,7 @@ impl<'a> CompilerCalls<'a> for JitCalls { state.session.abort_if_errors(); let trans = state.trans.unwrap(); assert_eq!(trans.modules.len(), 1); - let rs_llmod = match trans.modules[0].source { - ModuleSource::Preexisting(_) => unimplemented!(), - ModuleSource::Translated(llvm) => llvm.llmod, - }; - unsafe { rustc_llvm::LLVMDumpModule(rs_llmod) }; + println!("name of compiled module = {}", trans.modules[0].name); }); cc }