From 6d614ddc2ebc25d3987b1efc84c0c7fea00ce325 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 13 Sep 2017 20:26:39 -0700 Subject: [PATCH] rustc: Move codegen to a query This commit moves the actual code generation in the compiler behind a query keyed by a codegen unit's name. This ended up entailing quite a few internal refactorings to enable this, along with a few cut corners: * The `OutputFilenames` structure is now tracked in the `TyCtxt` as it affects a whole bunch of trans and such. This is now behind a query and threaded into the construction of the `TyCtxt`. * The `TyCtxt` now has a channel "out the back" intended to send data to worker threads in rustc_trans. This is used as a sort of side effect of the codegen query but morally what's happening here is the return value of the query (currently unit but morally a path) is only valid once the background threads have all finished. * Dispatching work items to the codegen threads was refactored to only rely on data in `TyCtxt`, which mostly just involved refactoring where data was stored, moving it from the translation thread to the controller thread's `CodegenContext` or the like. * A new thread locals was introduced in trans to work around the query system. This is used in the implementation of `assert_module_sources` which looks like an artifact of the old query system and will presumably go away once red/green is up and running. --- src/librustc/dep_graph/dep_node.rs | 4 + src/librustc/middle/trans.rs | 31 ++ src/librustc/ty/context.rs | 10 + src/librustc/ty/maps.rs | 39 +- src/librustc_driver/driver.rs | 25 +- src/librustc_driver/lib.rs | 1 + src/librustc_driver/pretty.rs | 14 +- src/librustc_driver/test.rs | 14 + src/librustc_trans/back/write.rs | 248 ++++++------- src/librustc_trans/base.rs | 433 ++++++++++++----------- src/librustc_trans/builder.rs | 9 +- src/librustc_trans/context.rs | 58 +-- src/librustc_trans/debuginfo/metadata.rs | 4 +- src/librustc_trans/glue.rs | 3 +- src/librustc_trans/monomorphize.rs | 2 +- src/librustc_trans/partitioning.rs | 9 +- src/librustdoc/core.rs | 8 +- 17 files changed, 484 insertions(+), 428 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index e3a9e96986432..06469c16bc22e 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -71,6 +71,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use ich::StableHashingContext; use std::fmt; use std::hash::Hash; +use syntax_pos::symbol::InternedString; // erase!() just makes tokens go away. It's used to specify which macro argument // is repeated (i.e. which sub-expression of the macro we are in) but don't need @@ -580,6 +581,9 @@ define_dep_nodes!( <'tcx> [] ExportName(DefId), [] ContainsExternIndicator(DefId), [] IsTranslatedFunction(DefId), + [] CodegenUnit(InternedString), + [] CompileCodegenUnit(InternedString), + [] OutputFilenames, ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { diff --git a/src/librustc/middle/trans.rs b/src/librustc/middle/trans.rs index cf8e9cbbd3d31..9a50125754846 100644 --- a/src/librustc/middle/trans.rs +++ b/src/librustc/middle/trans.rs @@ -77,3 +77,34 @@ impl<'tcx> CodegenUnit<'tcx> { &mut self.items } } + +#[derive(Clone, Default)] +pub struct Stats { + pub n_glues_created: usize, + pub n_null_glues: usize, + pub n_real_glues: usize, + pub n_fns: usize, + pub n_inlines: usize, + pub n_closures: usize, + pub n_llvm_insns: usize, + pub llvm_insns: FxHashMap, + // (ident, llvm-instructions) + pub fn_stats: Vec<(String, usize)>, +} + +impl Stats { + pub fn extend(&mut self, stats: Stats) { + self.n_glues_created += stats.n_glues_created; + self.n_null_glues += stats.n_null_glues; + self.n_real_glues += stats.n_real_glues; + self.n_fns += stats.n_fns; + self.n_inlines += stats.n_inlines; + self.n_closures += stats.n_closures; + self.n_llvm_insns += stats.n_llvm_insns; + + for (k, v) in stats.llvm_insns { + *self.llvm_insns.entry(k).or_insert(0) += v; + } + self.fn_stats.extend(stats.fn_stats); + } +} diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e932c9728db3e..945a081442750 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -13,6 +13,7 @@ use dep_graph::DepGraph; use errors::DiagnosticBuilder; use session::Session; +use session::config::OutputFilenames; use middle; use hir::{TraitCandidate, HirId, ItemLocalId}; use hir::def::{Def, Export}; @@ -65,6 +66,7 @@ use std::ops::Deref; use std::iter; use std::rc::Rc; use std::sync::mpsc; +use std::sync::Arc; use syntax::abi; use syntax::ast::{self, Name, NodeId}; use syntax::attr; @@ -910,6 +912,8 @@ pub struct GlobalCtxt<'tcx> { /// when satisfying the query for a particular codegen unit. Internally in /// the query it'll send data along this channel to get processed later. pub tx_to_llvm_workers: mpsc::Sender>, + + output_filenames: Arc, } impl<'tcx> GlobalCtxt<'tcx> { @@ -1035,6 +1039,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { hir: hir_map::Map<'tcx>, crate_name: &str, tx: mpsc::Sender>, + output_filenames: &OutputFilenames, f: F) -> R where F: for<'b> FnOnce(TyCtxt<'b, 'tcx, 'tcx>) -> R { @@ -1156,6 +1161,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { stability_interner: RefCell::new(FxHashSet()), all_traits: RefCell::new(None), tx_to_llvm_workers: tx, + output_filenames: Arc::new(output_filenames.clone()), }, f) } @@ -2229,4 +2235,8 @@ pub fn provide(providers: &mut ty::maps::Providers) { assert_eq!(cnum, LOCAL_CRATE); Rc::new(tcx.cstore.postorder_cnums_untracked()) }; + providers.output_filenames = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.output_filenames.clone() + }; } diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index 307921a333507..bf17b82535cc1 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -24,10 +24,11 @@ use middle::resolve_lifetime::{Region, ObjectLifetimeDefault}; use middle::stability::{self, DeprecationEntry}; use middle::lang_items::{LanguageItems, LangItem}; use middle::exported_symbols::SymbolExportLevel; -use middle::trans::CodegenUnit; +use middle::trans::{CodegenUnit, Stats}; use mir; use mir::transform::{MirSuite, MirPassIndex}; use session::CompileResult; +use session::config::OutputFilenames; use traits::specialization_graph; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; use ty::layout::{Layout, LayoutError}; @@ -52,6 +53,7 @@ use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::symbol::InternedString; use syntax::attr; use syntax::ast; use syntax::symbol::Symbol; @@ -180,6 +182,15 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { } } +impl Key for InternedString { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _tcx: TyCtxt) -> Span { + DUMMY_SP + } +} + trait Value<'tcx>: Sized { fn from_cycle_error<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self; } @@ -760,6 +771,24 @@ impl<'tcx> QueryDescription for queries::collect_and_partition_translation_items } } +impl<'tcx> QueryDescription for queries::codegen_unit<'tcx> { + fn describe(_tcx: TyCtxt, _: InternedString) -> String { + format!("codegen_unit") + } +} + +impl<'tcx> QueryDescription for queries::compile_codegen_unit<'tcx> { + fn describe(_tcx: TyCtxt, _: InternedString) -> String { + format!("compile_codegen_unit") + } +} + +impl<'tcx> QueryDescription for queries::output_filenames<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("output_filenames") + } +} + // If enabled, send a message to the profile-queries thread macro_rules! profq_msg { ($tcx:expr, $msg:expr) => { @@ -1395,6 +1424,10 @@ define_maps! { <'tcx> [] fn export_name: ExportName(DefId) -> Option, [] fn contains_extern_indicator: ContainsExternIndicator(DefId) -> bool, [] fn is_translated_function: IsTranslatedFunction(DefId) -> bool, + [] fn codegen_unit: CodegenUnit(InternedString) -> Arc>, + [] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats, + [] fn output_filenames: output_filenames_node(CrateNum) + -> Arc, } fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> { @@ -1512,3 +1545,7 @@ fn all_crate_nums_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { fn collect_and_partition_translation_items_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::CollectAndPartitionTranslationItems } + +fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { + DepConstructor::OutputFilenames +} diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 0adcfa79039d6..32a160bcffcef 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -193,6 +193,7 @@ pub fn compile_input(sess: &Session, &resolutions, &expanded_crate, &hir_map.krate(), + &outputs, &crate_name), Ok(())); } @@ -216,6 +217,7 @@ pub fn compile_input(sess: &Session, &arena, &arenas, &crate_name, + &outputs, |tcx, analysis, incremental_hashes_map, rx, result| { { // Eventually, we will want to track plugins. @@ -246,8 +248,7 @@ pub fn compile_input(sess: &Session, let trans = phase_4_translate_to_llvm(tcx, incremental_hashes_map, - rx, - &outputs); + rx); if log_enabled!(::log::LogLevel::Info) { println!("Post-trans"); @@ -261,7 +262,7 @@ pub fn compile_input(sess: &Session, } } - Ok((outputs, trans, tcx.dep_graph.clone())) + Ok((outputs.clone(), trans, tcx.dep_graph.clone())) })?? }; @@ -486,6 +487,7 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { resolutions: &'a Resolutions, krate: &'a ast::Crate, hir_crate: &'a hir::Crate, + output_filenames: &'a OutputFilenames, crate_name: &'a str) -> Self { CompileState { @@ -498,6 +500,7 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { resolutions: Some(resolutions), expanded_crate: Some(krate), hir_crate: Some(hir_crate), + output_filenames: Some(output_filenames), out_file: out_file.as_ref().map(|s| &**s), ..CompileState::empty(input, session, out_dir) } @@ -913,6 +916,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, arena: &'tcx DroplessArena, arenas: &'tcx GlobalArenas<'tcx>, name: &str, + output_filenames: &OutputFilenames, f: F) -> Result where F: for<'a> FnOnce(TyCtxt<'a, 'tcx, 'tcx>, @@ -922,11 +926,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, CompileResult) -> R { macro_rules! try_with_f { - ($e: expr, ($t: expr, $a: expr, $h: expr)) => { + ($e: expr, ($($t:tt)*)) => { match $e { Ok(x) => x, Err(x) => { - f($t, $a, $h, Err(x)); + f($($t)*, Err(x)); return Err(x); } } @@ -1047,6 +1051,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, hir_map, name, tx, + output_filenames, |tcx| { let incremental_hashes_map = time(time_passes, @@ -1062,7 +1067,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || stability::check_unstable_api_usage(tcx)); // passes are timed inside typeck - try_with_f!(typeck::check_crate(tcx), (tcx, analysis, incremental_hashes_map)); + try_with_f!(typeck::check_crate(tcx), + (tcx, analysis, incremental_hashes_map, rx)); time(time_passes, "const checking", @@ -1106,7 +1112,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis if sess.err_count() > 0 { - return Ok(f(tcx, analysis, incremental_hashes_map, sess.compile_status())); + return Ok(f(tcx, analysis, incremental_hashes_map, rx, sess.compile_status())); } time(time_passes, "death checking", || middle::dead::check_crate(tcx)); @@ -1125,8 +1131,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>, incremental_hashes_map: IncrementalHashesMap, - rx: mpsc::Receiver>, - output_filenames: &OutputFilenames) + rx: mpsc::Receiver>) -> write::OngoingCrateTranslation { let time_passes = tcx.sess.time_passes(); @@ -1136,7 +1141,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, incremental_hashes_map, rx, output_filenames) + trans::trans_crate(tcx, incremental_hashes_map, rx) }); if tcx.sess.profile_queries() { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 522b9eb22320e..044f4a5eaf512 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -645,6 +645,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { ppm, state.arena.unwrap(), state.arenas.unwrap(), + state.output_filenames.unwrap(), opt_uii.clone(), state.out_file); }; diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 82dda2d2aa162..cd153b820776e 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -23,7 +23,7 @@ use rustc::cfg::graphviz::LabelledCFG; use rustc::dep_graph::DepGraph; use rustc::middle::cstore::CrateStore; use rustc::session::Session; -use rustc::session::config::Input; +use rustc::session::config::{Input, OutputFilenames}; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; @@ -205,6 +205,7 @@ impl PpSourceMode { resolutions: &Resolutions, arena: &'tcx DroplessArena, arenas: &'tcx GlobalArenas<'tcx>, + output_filenames: &OutputFilenames, id: &str, f: F) -> A @@ -235,7 +236,8 @@ impl PpSourceMode { arena, arenas, id, - |tcx, _, _, _| { + output_filenames, + |tcx, _, _, _, _| { let empty_tables = ty::TypeckTables::empty(None); let annotation = TypedAnnotation { tcx, @@ -888,6 +890,7 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, ppm: PpMode, arena: &'tcx DroplessArena, arenas: &'tcx GlobalArenas<'tcx>, + output_filenames: &OutputFilenames, opt_uii: Option, ofile: Option<&Path>) { let dep_graph = DepGraph::new(false); @@ -902,6 +905,7 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, crate_name, arena, arenas, + output_filenames, ppm, opt_uii, ofile); @@ -940,6 +944,7 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, resolutions, arena, arenas, + output_filenames, crate_name, move |annotation, krate| { debug!("pretty printing source code {:?}", s); @@ -964,6 +969,7 @@ pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session, resolutions, arena, arenas, + output_filenames, crate_name, move |annotation, _| { debug!("pretty printing source code {:?}", s); @@ -1007,6 +1013,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, crate_name: &str, arena: &'tcx DroplessArena, arenas: &'tcx GlobalArenas<'tcx>, + output_filenames: &OutputFilenames, ppm: PpMode, uii: Option, ofile: Option<&Path>) { @@ -1028,7 +1035,8 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, arena, arenas, crate_name, - |tcx, _, _, _| { + output_filenames, + |tcx, _, _, _, _| { match ppm { PpmMir | PpmMirCFG => { if let Some(nodeid) = nodeid { diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index d0edcbc326098..34f4e0e7b0c95 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -10,6 +10,9 @@ //! # Standalone Tests for the Inference Module +use std::path::PathBuf; +use std::sync::mpsc; + use driver; use rustc_lint; use rustc_resolve::MakeGlobMap; @@ -26,6 +29,7 @@ use rustc_metadata::cstore::CStore; use rustc::hir::map as hir_map; use rustc::mir::transform::Passes; use rustc::session::{self, config}; +use rustc::session::config::{OutputFilenames, OutputTypes}; use std::rc::Rc; use syntax::ast; use syntax::abi::Abi; @@ -133,6 +137,14 @@ fn test_env(source_string: &str, // run just enough stuff to build a tcx: let named_region_map = resolve_lifetime::krate(&sess, &*cstore, &hir_map); + let (tx, _rx) = mpsc::channel(); + let outputs = OutputFilenames { + out_directory: PathBuf::new(), + out_filestem: String::new(), + single_output_file: None, + extra: String::new(), + outputs: OutputTypes::new(&[]), + }; TyCtxt::create_and_enter(&sess, &*cstore, ty::maps::Providers::default(), @@ -144,6 +156,8 @@ fn test_env(source_string: &str, named_region_map.unwrap(), hir_map, "test_crate", + tx, + &outputs, |tcx| { tcx.infer_ctxt().enter(|infcx| { let mut region_scope_tree = region::ScopeTree::default(); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 2535583cc6fbb..ef6bf2504f312 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -203,8 +203,6 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef { /// Module-specific configuration for `optimize_and_codegen`. pub struct ModuleConfig { - /// LLVM TargetMachine to use for codegen. - tm: TargetMachineRef, /// Names of additional optimization passes to run. passes: Vec, /// Some(level) to optimize at a certain level, or None to run @@ -237,12 +235,9 @@ pub struct ModuleConfig { obj_is_bitcode: bool, } -unsafe impl Send for ModuleConfig { } - impl ModuleConfig { - fn new(sess: &Session, passes: Vec) -> ModuleConfig { + fn new(passes: Vec) -> ModuleConfig { ModuleConfig { - tm: create_target_machine(sess), passes, opt_level: None, opt_size: None, @@ -290,40 +285,6 @@ 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) @@ -337,6 +298,11 @@ pub struct CodegenContext { pub opts: Arc, pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, + output_filenames: Arc, + regular_module_config: Arc, + metadata_module_config: Arc, + allocator_module_config: Arc, + // Handler to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, // LLVM passes added by plugins. @@ -359,6 +325,14 @@ impl CodegenContext { fn create_diag_handler(&self) -> Handler { Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) } + + fn config(&self, kind: ModuleKind) -> &ModuleConfig { + match kind { + ModuleKind::Regular => &self.regular_module_config, + ModuleKind::Metadata => &self.metadata_module_config, + ModuleKind::Allocator => &self.allocator_module_config, + } + } } struct HandlerFreeVars<'a> { @@ -418,8 +392,8 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo unsafe fn optimize_and_codegen(cgcx: &CodegenContext, diag_handler: &Handler, mtrans: ModuleTranslation, - config: ModuleConfig, - output_names: OutputFilenames) + tm: TargetMachineRef, + config: &ModuleConfig) -> Result { let (llmod, llcx) = match mtrans.source { @@ -429,8 +403,6 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } }; - let tm = config.tm; - let fv = HandlerFreeVars { cgcx, diag_handler, @@ -444,7 +416,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, let module_name = Some(&module_name[..]); if config.emit_no_opt_bc { - let out = output_names.temp_path_ext("no-opt.bc", module_name); + let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); let out = path2cstr(&out); llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); } @@ -517,7 +489,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, if cgcx.lto { time(cgcx.time_passes, "all lto passes", || { let temp_no_opt_bc_filename = - output_names.temp_path_ext("no-opt.lto.bc", module_name); + cgcx.output_filenames.temp_path_ext("no-opt.lto.bc", module_name); lto::run(cgcx, diag_handler, llmod, @@ -526,7 +498,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, &temp_no_opt_bc_filename) })?; if config.emit_lto_bc { - let out = output_names.temp_path_ext("lto.bc", module_name); + let out = cgcx.output_filenames.temp_path_ext("lto.bc", module_name); let out = path2cstr(&out); llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); } @@ -562,8 +534,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, let write_obj = config.emit_obj && !config.obj_is_bitcode; let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; - let bc_out = output_names.temp_path(OutputType::Bitcode, module_name); - let obj_out = output_names.temp_path(OutputType::Object, module_name); + let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); if write_bc { let bc_out_c = path2cstr(&bc_out); @@ -573,7 +545,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, 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); + let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); let out = path2cstr(&out); extern "C" fn demangle_callback(input_ptr: *const c_char, @@ -614,7 +586,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } if config.emit_asm { - let path = output_names.temp_path(OutputType::Assembly, module_name); + let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); // We can't use the same module for asm and binary output, because that triggers // various errors like invalid IR or broken binaries, so we might have to clone the @@ -672,13 +644,13 @@ fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { } pub fn start_async_translation(tcx: TyCtxt, - crate_output: &OutputFilenames, time_graph: Option, link: LinkMeta, metadata: EncodedMetadata, coordinator_receive: Receiver>) -> OngoingCrateTranslation { let sess = tcx.sess; + let crate_output = tcx.output_filenames(LOCAL_CRATE); let crate_name = tcx.crate_name(LOCAL_CRATE); let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, @@ -699,13 +671,6 @@ pub fn start_async_translation(tcx: TyCtxt, let linker_info = LinkerInfo::new(tcx); let crate_info = CrateInfo::new(tcx); - let mut exported_symbols = FxHashMap(); - exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE)); - for &cnum in tcx.crates().iter() { - exported_symbols.insert(cnum, tcx.exported_symbols(cnum)); - } - let exported_symbols = Arc::new(exported_symbols); - let output_types_override = if no_integrated_as { OutputTypes::new(&[(OutputType::Assembly, None)]) } else { @@ -713,9 +678,9 @@ pub fn start_async_translation(tcx: TyCtxt, }; // 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![]); + let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(vec![]); + let mut allocator_config = ModuleConfig::new(vec![]); if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { match *sanitizer { @@ -801,15 +766,17 @@ pub fn start_async_translation(tcx: TyCtxt, let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (trans_worker_send, trans_worker_receive) = channel(); - let coordinator_thread = start_executing_work(sess, + let coordinator_thread = start_executing_work(tcx, &crate_info, shared_emitter, trans_worker_send, - tcx.tx_to_llvm_workers.clone(), coordinator_receive, client, time_graph.clone(), - exported_symbols.clone()); + Arc::new(modules_config), + Arc::new(metadata_config), + Arc::new(allocator_config)); + OngoingCrateTranslation { crate_name, link, @@ -819,16 +786,12 @@ pub fn start_async_translation(tcx: TyCtxt, no_integrated_as, crate_info, - regular_module_config: modules_config, - metadata_module_config: metadata_config, - allocator_module_config: allocator_config, - time_graph, - output_filenames: crate_output.clone(), coordinator_send: tcx.tx_to_llvm_workers.clone(), trans_worker_receive, shared_emitter_main, - future: coordinator_thread + future: coordinator_thread, + output_filenames: tcx.output_filenames(LOCAL_CRATE), } } @@ -1029,8 +992,7 @@ pub fn dump_incremental_data(trans: &CrateTranslation) { struct WorkItem { mtrans: ModuleTranslation, - config: ModuleConfig, - output_names: OutputFilenames + tm: TargetMachine, } impl fmt::Debug for WorkItem { @@ -1039,15 +1001,15 @@ impl fmt::Debug for WorkItem { } } -fn build_work_item(mtrans: ModuleTranslation, - config: ModuleConfig, - output_names: OutputFilenames) - -> WorkItem -{ - WorkItem { - mtrans, - config, - output_names, +struct TargetMachine(TargetMachineRef); + +unsafe impl Send for TargetMachine {} + +impl Drop for TargetMachine { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustDisposeTargetMachine(self.0); + } } } @@ -1056,6 +1018,7 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) { let diag_handler = cgcx.create_diag_handler(); let module_name = work_item.mtrans.name.clone(); + let config = cgcx.config(work_item.mtrans.kind); let pre_existing = match work_item.mtrans.source { ModuleSource::Translated(_) => None, @@ -1068,7 +1031,7 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) .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 obj_out = cgcx.output_filenames.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 {}", @@ -1091,8 +1054,8 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) 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, + emit_bc: config.emit_bc, + emit_obj: config.emit_obj, }) } else { debug!("llvm-optimizing {:?}", module_name); @@ -1101,8 +1064,8 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) optimize_and_codegen(cgcx, &diag_handler, work_item.mtrans, - work_item.config, - work_item.output_names) + work_item.tm.0, + config) } } } @@ -1117,8 +1080,8 @@ enum Message { TranslationDone { llvm_work_item: WorkItem, cost: u64, - is_last: bool, }, + TranslationComplete, TranslateItem, } @@ -1135,16 +1098,26 @@ enum MainThreadWorkerState { LLVMing, } -fn start_executing_work(sess: &Session, +fn start_executing_work(tcx: TyCtxt, crate_info: &CrateInfo, shared_emitter: SharedEmitter, trans_worker_send: Sender, - coordinator_send: Sender>, coordinator_receive: Receiver>, jobserver: Client, time_graph: Option, - exported_symbols: Arc) + modules_config: Arc, + metadata_config: Arc, + allocator_config: Arc) -> thread::JoinHandle { + let coordinator_send = tcx.tx_to_llvm_workers.clone(); + let mut exported_symbols = FxHashMap(); + exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE)); + for &cnum in tcx.crates().iter() { + exported_symbols.insert(cnum, tcx.exported_symbols(cnum)); + } + let exported_symbols = Arc::new(exported_symbols); + let sess = tcx.sess; + // 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 @@ -1183,6 +1156,10 @@ fn start_executing_work(sess: &Session, coordinator_send, diag_emitter: shared_emitter.clone(), time_graph, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + regular_module_config: modules_config, + metadata_module_config: metadata_config, + allocator_module_config: allocator_config, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1332,7 +1309,7 @@ fn start_executing_work(sess: &Session, let mut translation_done = false; // This is the queue of LLVM work items that still need processing. - let mut work_items = Vec::new(); + let mut work_items = Vec::<(WorkItem, u64)>::new(); // This are the Jobserver Tokens we currently hold. Does not include // the implicit Token the compiler process owns no matter what. @@ -1371,7 +1348,8 @@ 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); + maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } @@ -1387,7 +1365,8 @@ 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); + maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + &mut llvm_start_time); main_thread_worker_state = MainThreadWorkerState::LLVMing; spawn_work(cgcx, item); } else { @@ -1417,7 +1396,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); + maybe_start_llvm_timer(cgcx.config(item.mtrans.kind), + &mut llvm_start_time); let cgcx = CodegenContext { worker: get_worker_id(&mut free_worker_ids), @@ -1459,7 +1439,7 @@ fn start_executing_work(sess: &Session, } } - Message::TranslationDone { llvm_work_item, cost, is_last } => { + Message::TranslationDone { llvm_work_item, cost } => { // 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 @@ -1475,15 +1455,14 @@ fn start_executing_work(sess: &Session, }; work_items.insert(insertion_index, (llvm_work_item, cost)); - 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(); - } + helper.request_token(); + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Translating); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + Message::TranslationComplete => { + translation_done = true; assert_eq!(main_thread_worker_state, MainThreadWorkerState::Translating); main_thread_worker_state = MainThreadWorkerState::Idle; @@ -1561,11 +1540,11 @@ fn start_executing_work(sess: &Session, items_in_queue >= max_workers.saturating_sub(workers_running / 2) } - fn maybe_start_llvm_timer(work_item: &WorkItem, + fn maybe_start_llvm_timer(config: &ModuleConfig, 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 config.time_passes { if llvm_start_time.is_none() { *llvm_start_time = Some(Instant::now()); } @@ -1840,17 +1819,12 @@ pub struct OngoingCrateTranslation { linker_info: LinkerInfo, no_integrated_as: bool, crate_info: CrateInfo, - - output_filenames: OutputFilenames, - regular_module_config: ModuleConfig, - metadata_module_config: ModuleConfig, - allocator_module_config: ModuleConfig, - time_graph: Option, coordinator_send: Sender>, trans_worker_receive: Receiver, shared_emitter_main: SharedEmitterMain, future: thread::JoinHandle, + output_filenames: Arc, } impl OngoingCrateTranslation { @@ -1918,38 +1892,21 @@ impl OngoingCrateTranslation { trans } - 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), - ModuleKind::Metadata => self.metadata_module_config.clone(sess), - ModuleKind::Allocator => self.allocator_module_config.clone(sess), - }; - - let llvm_work_item = build_work_item(mtrans, - module_config, - self.output_filenames.clone()); - - drop(self.coordinator_send.send(Box::new(Message::TranslationDone { - llvm_work_item, - cost, - is_last - }))); - } - pub fn submit_pre_translated_module_to_llvm(&self, - sess: &Session, - mtrans: ModuleTranslation, - is_last: bool) { + tcx: TyCtxt, + mtrans: ModuleTranslation) { self.wait_for_signal_to_translate_item(); - self.check_for_errors(sess); + self.check_for_errors(tcx.sess); // These are generally cheap and won't through off scheduling. let cost = 0; - self.submit_translated_module_to_llvm(sess, mtrans, cost, is_last); + submit_translated_module_to_llvm(tcx, mtrans, cost); + } + + pub fn translation_finished(&self, tcx: TyCtxt) { + self.wait_for_signal_to_translate_item(); + self.check_for_errors(tcx.sess); + drop(self.coordinator_send.send(Box::new(Message::TranslationComplete))); } pub fn check_for_errors(&self, sess: &Session) { @@ -1971,3 +1928,16 @@ impl OngoingCrateTranslation { } } } + +pub fn submit_translated_module_to_llvm(tcx: TyCtxt, + mtrans: ModuleTranslation, + cost: u64) { + let llvm_work_item = WorkItem { + mtrans, + tm: TargetMachine(create_target_machine(tcx.sess)), + }; + drop(tcx.tx_to_llvm_workers.send(Box::new(Message::TranslationDone { + llvm_work_item, + cost, + }))); +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1b802f7420132..d86f88d4c7da0 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -28,7 +28,7 @@ use super::ModuleSource; use super::ModuleTranslation; use super::ModuleKind; -use assert_module_sources; +use assert_module_sources::{self, Disposition}; use back::link; use back::symbol_export; use back::write::{self, OngoingCrateTranslation}; @@ -37,7 +37,7 @@ use llvm; use metadata; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::middle::lang_items::StartFnLangItem; -use rustc::middle::trans::{Linkage, Visibility}; +use rustc::middle::trans::{Linkage, Visibility, Stats}; use rustc::middle::cstore::{EncodedMetadata, EncodedMetadataHashes}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; @@ -45,7 +45,7 @@ use rustc::dep_graph::AssertDepGraphSafe; use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; use rustc::hir::map as hir_map; use rustc::util::common::{time, print_time_passes_entry}; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames}; +use rustc::session::config::{self, NoDebugInfo}; use rustc::session::Session; use rustc_incremental::{self, IncrementalHashesMap}; use abi; @@ -61,7 +61,7 @@ use common::CrateContext; use common::{type_is_zero_size, val_ty}; use common; use consts; -use context::{self, LocalCrateContext, SharedCrateContext, Stats}; +use context::{self, LocalCrateContext, SharedCrateContext}; use debuginfo; use declare; use machine; @@ -80,6 +80,7 @@ use CrateInfo; use libc::c_uint; use std::any::Any; +use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::str; use std::sync::Arc; @@ -87,6 +88,7 @@ use std::time::{Instant, Duration}; use std::i32; use std::sync::mpsc; use syntax_pos::Span; +use syntax_pos::symbol::InternedString; use syntax::attr; use rustc::hir; use syntax::ast; @@ -101,7 +103,7 @@ pub struct StatRecorder<'a, 'tcx: 'a> { impl<'a, 'tcx> StatRecorder<'a, 'tcx> { pub fn new(ccx: &'a CrateContext<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { - let istart = ccx.stats().n_llvm_insns.get(); + let istart = ccx.stats().borrow().n_llvm_insns; StatRecorder { ccx, name: Some(name), @@ -113,12 +115,12 @@ impl<'a, 'tcx> StatRecorder<'a, 'tcx> { impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { fn drop(&mut self) { if self.ccx.sess().trans_stats() { - let iend = self.ccx.stats().n_llvm_insns.get(); - self.ccx.stats().fn_stats.borrow_mut() - .push((self.name.take().unwrap(), iend - self.istart)); - self.ccx.stats().n_fns.set(self.ccx.stats().n_fns.get() + 1); + let mut stats = self.ccx.stats().borrow_mut(); + let iend = stats.n_llvm_insns; + stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); + stats.n_fns += 1; // Reset LLVM insn count to avoid compound costs. - self.ccx.stats().n_llvm_insns.set(self.istart); + stats.n_llvm_insns = self.istart; } } } @@ -590,7 +592,7 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance None => bug!("Instance `{:?}` not already declared", instance) }; - ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); + ccx.stats().borrow_mut().n_closures += 1; // The `uwtable` attribute according to LLVM is: // @@ -935,18 +937,14 @@ pub fn find_exported_symbols(tcx: TyCtxt) -> NodeSet { pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, incremental_hashes_map: IncrementalHashesMap, - rx: mpsc::Receiver>, - output_filenames: &OutputFilenames) + rx: mpsc::Receiver>) -> OngoingCrateTranslation { check_for_rustc_errors_attr(tcx); - let check_overflow = tcx.sess.overflow_checks(); let link_meta = link::build_link_meta(&incremental_hashes_map); let exported_symbol_node_ids = find_exported_symbols(tcx); - let shared_ccx = SharedCrateContext::new(tcx, - check_overflow, - output_filenames); + let shared_ccx = SharedCrateContext::new(tcx); // Translate the metadata. let (metadata_llcx, metadata_llmod, metadata, metadata_incr_hashes) = time(tcx.sess.time_passes(), "write metadata", || { @@ -974,13 +972,13 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, !tcx.sess.opts.output_types.should_trans() { let ongoing_translation = write::start_async_translation( tcx, - output_filenames, time_graph.clone(), link_meta, metadata, rx); - ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, metadata_module, true); + ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); + ongoing_translation.translation_finished(tcx); assert_and_save_dep_graph(tcx, incremental_hashes_map, @@ -1002,7 +1000,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ongoing_translation = write::start_async_translation( tcx, - output_filenames, time_graph.clone(), link_meta, metadata, @@ -1044,16 +1041,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; if let Some(allocator_module) = allocator_module { - ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, allocator_module, false); + ongoing_translation.submit_pre_translated_module_to_llvm(tcx, allocator_module); } - let codegen_unit_count = codegen_units.len(); - ongoing_translation.submit_pre_translated_module_to_llvm(tcx.sess, - metadata_module, - codegen_unit_count == 0); - - let mut all_stats = Stats::default(); - let mut module_dispositions = tcx.sess.opts.incremental.as_ref().map(|_| Vec::new()); + ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); // 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 @@ -1066,217 +1057,57 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, }; let mut total_trans_time = Duration::new(0, 0); + let mut all_stats = Stats::default(); - for (cgu_index, cgu) in codegen_units.into_iter().enumerate() { + for cgu in codegen_units.into_iter() { 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 start_time = Instant::now(); + all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); + total_trans_time += start_time.elapsed(); - 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), - module_translation); - all_stats.extend(stats); - - if let Some(ref mut module_dispositions) = module_dispositions { - module_dispositions.push(module.disposition()); - } - - 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; - - 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, - module, - cost, - is_last_cgu); ongoing_translation.check_for_errors(tcx.sess); } + ongoing_translation.translation_finished(tcx); + // 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); - } - - fn module_translation<'a, 'tcx>( - scx: AssertDepGraphSafe<&SharedCrateContext<'a, 'tcx>>, - args: AssertDepGraphSafe>>) - -> (Stats, ModuleTranslation) - { - // FIXME(#40304): We ought to be using the id as a key and some queries, I think. - let AssertDepGraphSafe(scx) = scx; - let AssertDepGraphSafe(cgu) = args; - - let cgu_name = cgu.name().to_string(); - let cgu_id = cgu.work_product_id(); - let symbol_name_hash = cgu.compute_symbol_name_hash(scx); - - // Check whether there is a previous work-product we can - // re-use. Not only must the file exist, and the inputs not - // be dirty, but the hash of the symbols we will generate must - // be the same. - let previous_work_product = - scx.dep_graph().previous_work_product(&cgu_id).and_then(|work_product| { - if work_product.input_hash == symbol_name_hash { - debug!("trans_reuse_previous_work_products: reusing {:?}", work_product); - Some(work_product) - } else { - if scx.sess().opts.debugging_opts.incremental_info { - eprintln!("incremental: CGU `{}` invalidated because of \ - changed partitioning hash.", - cgu.name()); - } - debug!("trans_reuse_previous_work_products: \ - not reusing {:?} because hash changed to {:?}", - work_product, symbol_name_hash); - None - } - }); - - if let Some(buf) = previous_work_product { - // Don't need to translate this module. - let module = ModuleTranslation { - name: cgu_name, - symbol_name_hash, - source: ModuleSource::Preexisting(buf.clone()), - kind: ModuleKind::Regular, - }; - return (Stats::default(), module); - } - - // Instantiate translation items without filling out definitions yet... - let lcx = LocalCrateContext::new(scx, cgu); - let module = { - let ccx = CrateContext::new(scx, &lcx); - let trans_items = ccx.codegen_unit() - .items_in_deterministic_order(ccx.tcx()); - for &(trans_item, (linkage, visibility)) in &trans_items { - trans_item.predefine(&ccx, linkage, visibility); - } - - // ... and now that we have everything pre-defined, fill out those definitions. - for &(trans_item, _) in &trans_items { - trans_item.define(&ccx); - } - - // If this codegen unit contains the main function, also create the - // wrapper here - maybe_create_entry_wrapper(&ccx); - - // Run replace-all-uses-with for statics that need it - for &(old_g, new_g) in ccx.statics_to_rauw().borrow().iter() { - unsafe { - let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); - llvm::LLVMReplaceAllUsesWith(old_g, bitcast); - llvm::LLVMDeleteGlobal(old_g); - } - } - - // Create the llvm.used variable - // This variable has type [N x i8*] and is stored in the llvm.metadata section - if !ccx.used_statics().borrow().is_empty() { - let name = CString::new("llvm.used").unwrap(); - let section = CString::new("llvm.metadata").unwrap(); - let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow()); - - unsafe { - let g = llvm::LLVMAddGlobal(ccx.llmod(), - val_ty(array).to_ref(), - name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); - } - } - - // Finalize debuginfo - if ccx.sess().opts.debuginfo != NoDebugInfo { - 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); - } - }); - } - } - - // 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, - source: ModuleSource::Translated(llvm_module), - kind: ModuleKind::Regular, - } - }; - - (lcx.into_stats(), module) + if tcx.sess.opts.incremental.is_some() { + DISPOSITIONS.with(|d| { + assert_module_sources::assert_module_sources(tcx, &d.borrow()); + }); } symbol_names_test::report_symbol_names(tcx); if shared_ccx.sess().trans_stats() { println!("--- trans stats ---"); - println!("n_glues_created: {}", all_stats.n_glues_created.get()); - println!("n_null_glues: {}", all_stats.n_null_glues.get()); - println!("n_real_glues: {}", all_stats.n_real_glues.get()); + println!("n_glues_created: {}", all_stats.n_glues_created); + println!("n_null_glues: {}", all_stats.n_null_glues); + println!("n_real_glues: {}", all_stats.n_real_glues); - println!("n_fns: {}", all_stats.n_fns.get()); - println!("n_inlines: {}", all_stats.n_inlines.get()); - println!("n_closures: {}", all_stats.n_closures.get()); + println!("n_fns: {}", all_stats.n_fns); + println!("n_inlines: {}", all_stats.n_inlines); + println!("n_closures: {}", all_stats.n_closures); println!("fn stats:"); - all_stats.fn_stats.borrow_mut().sort_by(|&(_, insns_a), &(_, insns_b)| { - insns_b.cmp(&insns_a) - }); - for tuple in all_stats.fn_stats.borrow().iter() { - match *tuple { - (ref name, insns) => { - println!("{} insns, {}", insns, *name); - } - } + all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); + for &(ref name, insns) in all_stats.fn_stats.iter() { + println!("{} insns, {}", insns, *name); } } if shared_ccx.sess().count_llvm_insns() { - for (k, v) in all_stats.llvm_insns.borrow().iter() { + for (k, v) in all_stats.llvm_insns.iter() { println!("{:7} {}", *v, *k); } } @@ -1290,6 +1121,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ongoing_translation } +// FIXME(#42293) hopefully once red/green is enabled we're testing everything +// via a method that doesn't require this! +thread_local!(static DISPOSITIONS: RefCell> = Default::default()); + fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, incremental_hashes_map: IncrementalHashesMap, metadata_incr_hashes: EncodedMetadataHashes, @@ -1524,11 +1359,185 @@ fn is_translated_function(tcx: TyCtxt, id: DefId) -> bool { }) } +fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cgu: InternedString) -> Stats { + // FIXME(#42293) needs red/green tracking to avoid failing a bunch of + // existing tests + let cgu = tcx.dep_graph.with_ignore(|| { + tcx.codegen_unit(cgu) + }); + + let start_time = Instant::now(); + let dep_node = cgu.work_product_dep_node(); + let ((stats, module), _) = + tcx.dep_graph.with_task(dep_node, + AssertDepGraphSafe(tcx), + AssertDepGraphSafe(cgu), + module_translation); + let time_to_translate = start_time.elapsed(); + + if tcx.sess.opts.incremental.is_some() { + DISPOSITIONS.with(|d| { + d.borrow_mut().push(module.disposition()); + }); + } + + // 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; + + write::submit_translated_module_to_llvm(tcx, + module, + cost); + return stats; + + fn module_translation<'a, 'tcx>( + tcx: AssertDepGraphSafe>, + args: AssertDepGraphSafe>>) + -> (Stats, ModuleTranslation) + { + // FIXME(#40304): We ought to be using the id as a key and some queries, I think. + let AssertDepGraphSafe(tcx) = tcx; + let AssertDepGraphSafe(cgu) = args; + + let cgu_name = cgu.name().to_string(); + let cgu_id = cgu.work_product_id(); + let symbol_name_hash = cgu.compute_symbol_name_hash(tcx); + + // Check whether there is a previous work-product we can + // re-use. Not only must the file exist, and the inputs not + // be dirty, but the hash of the symbols we will generate must + // be the same. + let previous_work_product = + tcx.dep_graph.previous_work_product(&cgu_id).and_then(|work_product| { + if work_product.input_hash == symbol_name_hash { + debug!("trans_reuse_previous_work_products: reusing {:?}", work_product); + Some(work_product) + } else { + if tcx.sess.opts.debugging_opts.incremental_info { + eprintln!("incremental: CGU `{}` invalidated because of \ + changed partitioning hash.", + cgu.name()); + } + debug!("trans_reuse_previous_work_products: \ + not reusing {:?} because hash changed to {:?}", + work_product, symbol_name_hash); + None + } + }); + + if let Some(buf) = previous_work_product { + // Don't need to translate this module. + let module = ModuleTranslation { + name: cgu_name, + symbol_name_hash, + source: ModuleSource::Preexisting(buf.clone()), + kind: ModuleKind::Regular, + }; + return (Stats::default(), module); + } + + // Instantiate translation items without filling out definitions yet... + let scx = SharedCrateContext::new(tcx); + let lcx = LocalCrateContext::new(&scx, cgu); + let module = { + let ccx = CrateContext::new(&scx, &lcx); + let trans_items = ccx.codegen_unit() + .items_in_deterministic_order(ccx.tcx()); + for &(trans_item, (linkage, visibility)) in &trans_items { + trans_item.predefine(&ccx, linkage, visibility); + } + + // ... and now that we have everything pre-defined, fill out those definitions. + for &(trans_item, _) in &trans_items { + trans_item.define(&ccx); + } + + // If this codegen unit contains the main function, also create the + // wrapper here + maybe_create_entry_wrapper(&ccx); + + // Run replace-all-uses-with for statics that need it + for &(old_g, new_g) in ccx.statics_to_rauw().borrow().iter() { + unsafe { + let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); + llvm::LLVMReplaceAllUsesWith(old_g, bitcast); + llvm::LLVMDeleteGlobal(old_g); + } + } + + // Create the llvm.used variable + // This variable has type [N x i8*] and is stored in the llvm.metadata section + if !ccx.used_statics().borrow().is_empty() { + let name = CString::new("llvm.used").unwrap(); + let section = CString::new("llvm.metadata").unwrap(); + let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow()); + + unsafe { + let g = llvm::LLVMAddGlobal(ccx.llmod(), + val_ty(array).to_ref(), + name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + } + } + + // Finalize debuginfo + if ccx.sess().opts.debuginfo != NoDebugInfo { + 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); + } + }); + } + } + + // 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, + source: ModuleSource::Translated(llvm_module), + kind: ModuleKind::Regular, + } + }; + + (lcx.into_stats(), module) + } +} + pub fn provide_local(providers: &mut Providers) { providers.collect_and_partition_translation_items = collect_and_partition_translation_items; providers.is_translated_function = is_translated_function; + + providers.codegen_unit = |tcx, name| { + let (_, all) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); + all.iter() + .find(|cgu| *cgu.name() == name) + .cloned() + .expect(&format!("failed to find cgu with name {:?}", name)) + }; + providers.compile_codegen_unit = compile_codegen_unit; } pub fn provide_extern(providers: &mut Providers) { diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 8a585e72f59ed..41a238ea8e3fa 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -101,11 +101,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn count_insn(&self, category: &str) { if self.ccx.sess().trans_stats() { - self.ccx.stats().n_llvm_insns.set(self.ccx.stats().n_llvm_insns.get() + 1); + self.ccx.stats().borrow_mut().n_llvm_insns += 1; } if self.ccx.sess().count_llvm_insns() { - let mut h = self.ccx.stats().llvm_insns.borrow_mut(); - *h.entry(category.to_string()).or_insert(0) += 1; + *self.ccx.stats() + .borrow_mut() + .llvm_insns + .entry(category.to_string()) + .or_insert(0) += 1; } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index a1666fa523195..8b18bf2e1ff1f 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -24,10 +24,11 @@ use monomorphize::Instance; use partitioning::CodegenUnit; use type_::Type; use rustc_data_structures::base_n; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames}; +use rustc::middle::trans::Stats; use rustc::session::Session; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::session::config::{self, NoDebugInfo}; use rustc::ty::layout::{LayoutCx, LayoutError, LayoutTyper, TyLayout}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::FxHashMap; use std::ffi::{CStr, CString}; @@ -40,36 +41,6 @@ use std::marker::PhantomData; use syntax::symbol::InternedString; use abi::Abi; -#[derive(Clone, Default)] -pub struct Stats { - pub n_glues_created: Cell, - pub n_null_glues: Cell, - pub n_real_glues: Cell, - pub n_fns: Cell, - pub n_inlines: Cell, - pub n_closures: Cell, - pub n_llvm_insns: Cell, - pub llvm_insns: RefCell>, - // (ident, llvm-instructions) - pub fn_stats: RefCell >, -} - -impl Stats { - pub fn extend(&mut self, stats: Stats) { - self.n_glues_created.set(self.n_glues_created.get() + stats.n_glues_created.get()); - self.n_null_glues.set(self.n_null_glues.get() + stats.n_null_glues.get()); - self.n_real_glues.set(self.n_real_glues.get() + stats.n_real_glues.get()); - self.n_fns.set(self.n_fns.get() + stats.n_fns.get()); - self.n_inlines.set(self.n_inlines.get() + stats.n_inlines.get()); - self.n_closures.set(self.n_closures.get() + stats.n_closures.get()); - self.n_llvm_insns.set(self.n_llvm_insns.get() + stats.n_llvm_insns.get()); - self.llvm_insns.borrow_mut().extend( - stats.llvm_insns.borrow().iter() - .map(|(key, value)| (key.clone(), value.clone()))); - self.fn_stats.borrow_mut().append(&mut *stats.fn_stats.borrow_mut()); - } -} - /// The shared portion of a `CrateContext`. There is one `SharedCrateContext` /// per crate. The data here is shared between all compilation units of the /// crate, so it must not contain references to any LLVM data structures @@ -77,10 +48,7 @@ impl Stats { pub struct SharedCrateContext<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, check_overflow: bool, - use_dll_storage_attrs: bool, - - output_filenames: &'a OutputFilenames, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -90,7 +58,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { pub struct LocalCrateContext<'a, 'tcx: 'a> { llmod: ModuleRef, llcx: ContextRef, - stats: Stats, + stats: RefCell, codegen_unit: Arc>, /// Cache instances of monomorphic and polymorphic items @@ -253,10 +221,7 @@ pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (Cont } impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { - pub fn new(tcx: TyCtxt<'b, 'tcx, 'tcx>, - check_overflow: bool, - output_filenames: &'b OutputFilenames) - -> SharedCrateContext<'b, 'tcx> { + pub fn new(tcx: TyCtxt<'b, 'tcx, 'tcx>) -> SharedCrateContext<'b, 'tcx> { // An interesting part of Windows which MSVC forces our hand on (and // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` // attributes in LLVM IR as well as native dependencies (in C these @@ -302,11 +267,12 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { // start) and then strongly recommending static linkage on MSVC! let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + let check_overflow = tcx.sess.overflow_checks(); + SharedCrateContext { tcx, check_overflow, use_dll_storage_attrs, - output_filenames, } } @@ -337,10 +303,6 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { pub fn use_dll_storage_attrs(&self) -> bool { self.use_dll_storage_attrs } - - pub fn output_filenames(&self) -> &OutputFilenames { - self.output_filenames - } } impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { @@ -375,7 +337,7 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { let local_ccx = LocalCrateContext { llmod, llcx, - stats: Stats::default(), + stats: RefCell::new(Stats::default()), codegen_unit, instances: RefCell::new(FxHashMap()), vtables: RefCell::new(FxHashMap()), @@ -440,7 +402,7 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> { } pub fn into_stats(self) -> Stats { - self.stats + self.stats.into_inner() } } @@ -525,7 +487,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().lltypes } - pub fn stats<'a>(&'a self) -> &'a Stats { + pub fn stats<'a>(&'a self) -> &'a RefCell { &self.local().stats } diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 333f7b1c029c1..8a89bfee4ac26 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -822,9 +822,9 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext, let gcov_cu_info = [ path_to_mdstring(debug_context.llcontext, - &scc.output_filenames().with_extension("gcno")), + &scc.tcx().output_filenames(LOCAL_CRATE).with_extension("gcno")), path_to_mdstring(debug_context.llcontext, - &scc.output_filenames().with_extension("gcda")), + &scc.tcx().output_filenames(LOCAL_CRATE).with_extension("gcda")), cu_desc_metadata, ]; let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext, diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 1572f1dc230c1..453b98a1d74f7 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -20,9 +20,8 @@ use llvm::{ValueRef}; use llvm; use meth; use monomorphize; -use rustc::traits; use rustc::ty::layout::LayoutTyper; -use rustc::ty::{self, Ty, TypeFoldable, TyCtxt}; +use rustc::ty::{self, Ty}; use value::Value; pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index e62924a54d757..2be7a81b1cd49 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -187,7 +187,7 @@ pub fn resolve<'a, 'tcx>( _ => { if Some(def_id) == tcx.lang_items().drop_in_place_fn() { let ty = substs.type_at(0); - if common::type_needs_drop(tcx, ty) { + if type_needs_drop(tcx, ty) { debug!(" => nontrivial drop glue"); ty::InstanceDef::DropGlue(def_id, Some(ty)) } else { diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 65cf24e8c6e03..9b617c35d9319 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -104,7 +104,6 @@ use collector::InliningMap; use common; -use context::SharedCrateContext; use rustc::dep_graph::{DepNode, WorkProductId}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; @@ -155,13 +154,11 @@ pub trait CodegenUnitExt<'tcx> { self.work_product_id().to_dep_node() } - fn compute_symbol_name_hash<'a>(&self, - scx: &SharedCrateContext<'a, 'tcx>) - -> u64 { + fn compute_symbol_name_hash<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> u64 { let mut state = IchHasher::new(); - let all_items = self.items_in_deterministic_order(scx.tcx()); + let all_items = self.items_in_deterministic_order(tcx); for (item, (linkage, visibility)) in all_items { - let symbol_name = item.symbol_name(scx.tcx()); + let symbol_name = item.symbol_name(tcx); symbol_name.len().hash(&mut state); symbol_name.hash(&mut state); linkage.hash(&mut state); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fd0167be2b98f..0c0748cf673c1 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -176,6 +176,11 @@ pub fn run_core(search_paths: SearchPaths, let arena = DroplessArena::new(); let arenas = GlobalArenas::new(); let hir_map = hir_map::map_crate(&mut hir_forest, defs); + let output_filenames = driver::build_output_filenames(&input, + &None, + &None, + &[], + &sess); abort_on_err(driver::phase_3_run_analysis_passes(&sess, &*cstore, @@ -185,7 +190,8 @@ pub fn run_core(search_paths: SearchPaths, &arena, &arenas, &name, - |tcx, analysis, _, result| { + &output_filenames, + |tcx, analysis, _, _, result| { if let Err(_) = result { sess.fatal("Compilation failed, aborting rustdoc"); }