From b1d0cde18cf6b56cb40bb7ac5c0fcc6f1c76fae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 03:53:42 +0100 Subject: [PATCH 1/6] Add FlexScope --- src/librustc/ty/context.rs | 6 +++++- src/librustc_data_structures/sync.rs | 21 +++++++++++++++++++++ src/librustc_interface/passes.rs | 4 +++- src/librustc_interface/queries.rs | 7 +++++-- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index a2e5edb67fc67..09dd5e88ecf87 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -58,7 +58,7 @@ use rustc_data_structures::sharded::ShardedHashMap; use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; -use rustc_data_structures::sync::{Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{FlexScope, Lock, Lrc, WorkerLocal}; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; use rustc_session::node_id::NodeMap; @@ -942,6 +942,8 @@ pub struct GlobalCtxt<'tcx> { interners: CtxtInterners<'tcx>, + pub scope: &'tcx FlexScope<'tcx>, + cstore: Box, pub sess: &'tcx Session, @@ -1125,6 +1127,7 @@ impl<'tcx> TyCtxt<'tcx> { on_disk_query_result_cache: query::OnDiskCache<'tcx>, crate_name: &str, output_filenames: &OutputFilenames, + scope: &'tcx FlexScope<'tcx>, ) -> GlobalCtxt<'tcx> { let data_layout = TargetDataLayout::parse(&s.target.target).unwrap_or_else(|err| { s.fatal(&err); @@ -1174,6 +1177,7 @@ impl<'tcx> TyCtxt<'tcx> { lint_store, cstore, arena, + scope, interners, dep_graph, prof: s.prof.clone(), diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index fa98b4d72dda2..4e5da22ad7837 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -160,6 +160,26 @@ cfg_if! { (oper_a(), oper_b()) } + pub struct FlexScope<'scope> { + marker: PhantomData &'scope ()>, + } + + impl<'scope> FlexScope<'scope> { + pub fn new() -> Self { + Self { + marker: PhantomData, + } + } + + pub fn activate(&self, f: impl FnOnce() -> R) -> R { + f() + } + + pub fn spawn(&self, f: impl FnOnce() + 'scope) { + f(); + } + } + pub struct SerialScope; impl SerialScope { @@ -362,6 +382,7 @@ cfg_if! { use std; use std::thread; pub use rayon::{join, scope}; + pub use rayon_core::FlexScope; /// Runs a list of blocks in parallel. The first block is executed immediately on /// the current thread. Use that for the longest running block. diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 432f79bba030a..b5e666f5974ff 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -21,7 +21,7 @@ use rustc_builtin_macros; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_codegen_utils::link::filename_for_metadata; -use rustc_data_structures::sync::{par_iter, Lrc, Once, ParallelIterator, WorkerLocal}; +use rustc_data_structures::sync::{par_iter, FlexScope, Lrc, Once, ParallelIterator, WorkerLocal}; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; use rustc_errors::PResult; use rustc_expand::base::ExtCtxt; @@ -721,6 +721,7 @@ pub fn create_global_ctxt<'tcx>( global_ctxt: &'tcx Once>, all_arenas: &'tcx AllArenas, arena: &'tcx WorkerLocal>, + scope: &'tcx FlexScope<'tcx>, ) -> QueryContext<'tcx> { let sess = &compiler.session(); let defs = mem::take(&mut resolver_outputs.definitions); @@ -759,6 +760,7 @@ pub fn create_global_ctxt<'tcx>( query_result_on_disk_cache, &crate_name, &outputs, + scope, ) }); diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs index 2de0e1ecccc83..20db0dced79ac 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -12,7 +12,7 @@ use rustc::ty::steal::Steal; use rustc::ty::{AllArenas, GlobalCtxt, ResolverOutputs}; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_data_structures::sync::{Lrc, Once, WorkerLocal}; +use rustc_data_structures::sync::{FlexScope, Lrc, Once, WorkerLocal}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_incremental::DepGraphFuture; use std::any::Any; @@ -69,6 +69,7 @@ pub struct Queries<'tcx> { all_arenas: AllArenas, arena: WorkerLocal>, + scope: FlexScope<'tcx>, dep_graph_future: Query>, parse: Query, @@ -88,6 +89,7 @@ impl<'tcx> Queries<'tcx> { compiler, gcx: Once::new(), all_arenas: AllArenas::new(), + scope: FlexScope::new(), arena: WorkerLocal::new(|_| Arena::default()), dep_graph_future: Default::default(), parse: Default::default(), @@ -266,6 +268,7 @@ impl<'tcx> Queries<'tcx> { &self.gcx, &self.all_arenas, &self.arena, + &self.scope, )) }) } @@ -329,7 +332,7 @@ impl Compiler { F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T, { let queries = Queries::new(&self); - let ret = f(&queries); + let ret = queries.scope.activate(|| f(&queries)); if self.session().opts.debugging_opts.query_stats { if let Ok(gcx) = queries.global_ctxt() { From 0e94a547c35976908fdedb5eedda3f7fe50e98e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 04:44:08 +0100 Subject: [PATCH 2/6] Add Future type --- src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/sync.rs | 2 + src/librustc_data_structures/sync/future.rs | 57 +++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/librustc_data_structures/sync/future.rs diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index d1b7ee9e83e76..c63e6666c016f 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -23,6 +23,7 @@ #![feature(integer_atomics)] #![feature(test)] #![feature(associated_type_bounds)] +#![feature(wait_until)] #![cfg_attr(unix, feature(libc))] #![allow(rustc::default_hash_types)] diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 4e5da22ad7837..59bcca67d0c30 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -26,6 +26,8 @@ use std::ops::{Deref, DerefMut}; pub use std::sync::atomic::Ordering; pub use std::sync::atomic::Ordering::SeqCst; +pub mod future; + cfg_if! { if #[cfg(not(parallel_compiler))] { pub auto trait Send {} diff --git a/src/librustc_data_structures/sync/future.rs b/src/librustc_data_structures/sync/future.rs new file mode 100644 index 0000000000000..f2fab1e99d18e --- /dev/null +++ b/src/librustc_data_structures/sync/future.rs @@ -0,0 +1,57 @@ +use crate::sync::{self, FlexScope}; +use std::mem::ManuallyDrop; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::Arc; +use std::sync::Condvar; +use std::sync::Mutex; +use std::thread; + +struct FutureData<'a, R> { + // ManuallyDrop is needed here to ensure the destructor of FutureData cannot refer to 'a + func: Mutex R + sync::Send + 'a>>>>, + result: Mutex>>, + waiter: Condvar, +} +pub struct Future<'a, R> { + data: Arc>, +} + +impl<'a, R: sync::Send + 'a> Future<'a, R> { + pub fn spawn_in_scope(scope: &FlexScope<'a>, f: impl FnOnce() -> R + sync::Send + 'a) -> Self { + let data = Arc::new(FutureData { + func: Mutex::new(ManuallyDrop::new(Some(Box::new(f)))), + result: Mutex::new(None), + waiter: Condvar::new(), + }); + let result = Self { data: data.clone() }; + scope.spawn(move || { + if let Some(func) = data.func.lock().unwrap().take() { + // Execute the function if it has not yet been joined + let r = panic::catch_unwind(AssertUnwindSafe(func)); + *data.result.lock().unwrap() = Some(r); + data.waiter.notify_all(); + } + }); + result + } + + pub fn join(self) -> R { + if let Some(func) = self.data.func.lock().unwrap().take() { + // The function was not executed yet by Rayon, just run it + func() + } else { + // The function has started executing, wait for it to complete + let mut result = self + .data + .waiter + .wait_until(self.data.result.lock().unwrap(), |result| result.is_some()) + .unwrap(); + match result.take().unwrap() { + Ok(r) => { + return r; + } + Err(err) => panic::resume_unwind(err), + } + } + } +} From f43c0148844db62c1ee9cb22c7f30d5549f02dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 04:44:19 +0100 Subject: [PATCH 3/6] Rayon WIP --- Cargo.lock | 3 +-- Cargo.toml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 274c1eefbfb52..3d2acdcfb8e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3295,8 +3295,7 @@ dependencies = [ [[package]] name = "rustc-rayon-core" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2427831f0053ea3ea73559c8eabd893133a51b251d142bacee53c62a288cb3" +source = "git+https://github.com/Zoxc/rayon.git?branch=single-lifetime-scope#f5d9d8b52dbfc64728d3a42ed3f7292e2eb6ee6b" dependencies = [ "crossbeam-deque", "crossbeam-queue", diff --git a/Cargo.toml b/Cargo.toml index a242f090fbc07..af21c4d06097f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ cargo = { path = "src/tools/cargo" } # that we're shipping as well (to ensure that the rustfmt in RLS and the # `rustfmt` executable are the same exact version). rustfmt-nightly = { path = "src/tools/rustfmt" } +rustc-rayon-core = { git = "https://github.com/Zoxc/rayon.git", branch = "single-lifetime-scope" } # See comments in `src/tools/rustc-workspace-hack/README.md` for what's going on # here From d2e4ab060a3e50bc63597a39de11504bffc3dd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 05:25:11 +0100 Subject: [PATCH 4/6] Run name resolution in parallel with gated feature checking --- src/librustc_interface/passes.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index b5e666f5974ff..16eb9cea3e0a4 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -401,19 +401,24 @@ fn configure_and_expand_inner<'a>( println!("{}", json::as_json(&krate)); } - sess.time("name resolution", || { - resolver.resolve_crate(&krate); - }); - - // Needs to go *after* expansion to be able to check the results of macro expansion. - sess.time("complete gated feature checking", || { - syntax::feature_gate::check_crate( - &krate, - &sess.parse_sess, - &sess.features_untracked(), - sess.opts.unstable_features, - ); - }); + parallel!( + { + sess.time("name resolution", || { + resolver.resolve_crate(&krate); + }); + }, + { + // Needs to go *after* expansion to be able to check the results of macro expansion. + sess.time("complete gated feature checking", || { + syntax::feature_gate::check_crate( + &krate, + &sess.parse_sess, + &sess.features_untracked(), + sess.opts.unstable_features, + ); + }) + } + ); // Add all buffered lints from the `ParseSess` to the `Session`. sess.parse_sess.buffered_lints.with_lock(|buffered_lints| { From 330d0751a448a2c991e8e97f262e335a7269d598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 06:03:59 +0100 Subject: [PATCH 5/6] Validate the HIR map in the background --- src/librustc/hir/map/mod.rs | 19 +++++++++++++------ src/librustc_interface/passes.rs | 4 +++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 0a165accb7d52..c1d340c6e6f13 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -9,6 +9,7 @@ use crate::middle::cstore::CrateStoreDyn; use crate::ty::query::Providers; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::FlexScope; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::itemlikevisit::ItemLikeVisitor; @@ -1235,20 +1236,26 @@ pub fn map_crate<'hir>( collector.finalize_and_compute_crate_hash(crate_disambiguator, cstore, cmdline_args) }; - let map = Map { + Map { forest, dep_graph: forest.dep_graph.clone(), crate_hash, map, hir_to_node_id, definitions, - }; + } +} - sess.time("validate HIR map", || { - hir_id_validator::check_crate(&map); +pub fn validate_map<'hir>( + sess: &'hir rustc_session::Session, + map: &'hir Map<'hir>, + scope: &'hir FlexScope<'hir>, +) { + scope.spawn(move || { + sess.time("validate HIR map", || { + hir_id_validator::check_crate(&map); + }) }); - - map } /// Identical to the `PpAnn` implementation for `hir::Crate`, diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 16eb9cea3e0a4..451c813be5c63 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -769,8 +769,10 @@ pub fn create_global_ctxt<'tcx>( ) }); - // Do some initialization of the DepGraph that can only be done with the tcx available. ty::tls::enter_global(&gcx, |tcx| { + map::validate_map(sess, tcx.hir(), scope); + + // Do some initialization of the DepGraph that can only be done with the tcx available. tcx.sess.time("dep graph tcx init", || rustc_incremental::dep_graph_tcx_init(tcx)); }); From cac3f11001c96c7f70d173dbcd672884e889cb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 7 Jan 2020 06:23:04 +0100 Subject: [PATCH 6/6] Run early lint checks in the background --- src/librustc_driver/lib.rs | 8 +++++++- src/librustc_interface/passes.rs | 32 ++++++++++++++++++------------- src/librustc_interface/queries.rs | 25 +++++++++++++----------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index dece0a55edd18..3de73d772e6ad 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -388,10 +388,15 @@ pub fn run_compiler( // (needed by the RLS) })?; } else { - // Drop AST after creating GlobalCtxt to free memory + // Drop a reference to the AST mem::drop(queries.expansion()?.take()); } + queries.global_ctxt()?; + + // Drop a reference to the AST by waiting on the lint future. + queries.lower_to_hir()?.take().1.join(); + queries.global_ctxt()?.peek_mut().enter(|tcx| tcx.analysis(LOCAL_CRATE))?; if callbacks.after_analysis(compiler, queries) == Compilation::Stop { @@ -399,6 +404,7 @@ pub fn run_compiler( } if sess.opts.debugging_opts.save_analysis { + // Drop AST to free memory mem::drop(queries.expansion()?.take()); } diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 451c813be5c63..4a579f66f487b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -21,6 +21,7 @@ use rustc_builtin_macros; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_codegen_utils::link::filename_for_metadata; +use rustc_data_structures::sync::future::Future; use rustc_data_structures::sync::{par_iter, FlexScope, Lrc, Once, ParallelIterator, WorkerLocal}; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; use rustc_errors::PResult; @@ -433,12 +434,13 @@ fn configure_and_expand_inner<'a>( pub fn lower_to_hir<'res, 'tcx>( sess: &'tcx Session, - lint_store: &lint::LintStore, + lint_store: Lrc, resolver: &'res mut Resolver<'_>, dep_graph: &'res DepGraph, - krate: &'res ast::Crate, + krate: Lrc, arena: &'tcx Arena<'tcx>, -) -> Result> { + scope: &'tcx FlexScope<'tcx>, +) -> Result<(map::Forest<'tcx>, Future<'tcx, ()>)> { // Lower AST to HIR. let hir_forest = sess.time("lowering AST -> HIR", || { let hir_crate = rustc_ast_lowering::lower_crate( @@ -457,15 +459,19 @@ pub fn lower_to_hir<'res, 'tcx>( map::Forest::new(hir_crate, &dep_graph) }); - sess.time("early lint checks", || { - rustc_lint::check_ast_crate( - sess, - lint_store, - &krate, - false, - Some(std::mem::take(resolver.lint_buffer())), - rustc_lint::BuiltinCombinedEarlyLintPass::new(), - ) + let lint_buffer = std::mem::take(resolver.lint_buffer()); + + let lints = Future::spawn_in_scope(scope, move || { + sess.time("early lint checks", || { + rustc_lint::check_ast_crate( + sess, + &lint_store, + &krate, + false, + Some(lint_buffer), + rustc_lint::BuiltinCombinedEarlyLintPass::new(), + ) + }) }); // Discard hygiene data, which isn't required after lowering to HIR. @@ -473,7 +479,7 @@ pub fn lower_to_hir<'res, 'tcx>( rustc_span::hygiene::clear_syntax_context_map(); } - Ok(hir_forest) + Ok((hir_forest, lints)) } // Returns all the paths that correspond to generated files. diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs index 20db0dced79ac..6c35d0a8a8e53 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -12,6 +12,7 @@ use rustc::ty::steal::Steal; use rustc::ty::{AllArenas, GlobalCtxt, ResolverOutputs}; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_data_structures::sync::future::Future; use rustc_data_structures::sync::{FlexScope, Lrc, Once, WorkerLocal}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_incremental::DepGraphFuture; @@ -75,9 +76,9 @@ pub struct Queries<'tcx> { parse: Query, crate_name: Query, register_plugins: Query<(ast::Crate, Lrc)>, - expansion: Query<(ast::Crate, Steal>>, Lrc)>, + expansion: Query<(Lrc, Steal>>, Lrc)>, dep_graph: Query, - lower_to_hir: Query<(&'tcx map::Forest<'tcx>, Steal)>, + lower_to_hir: Query<(&'tcx map::Forest<'tcx>, Future<'tcx, ()>, Steal)>, prepare_outputs: Query, global_ctxt: Query>, ongoing_codegen: Query>, @@ -174,7 +175,7 @@ impl<'tcx> Queries<'tcx> { pub fn expansion( &self, - ) -> Result<&Query<(ast::Crate, Steal>>, Lrc)>> { + ) -> Result<&Query<(Lrc, Steal>>, Lrc)>> { self.expansion.compute(|| { let crate_name = self.crate_name()?.peek().clone(); let (krate, lint_store) = self.register_plugins()?.take(); @@ -186,7 +187,7 @@ impl<'tcx> Queries<'tcx> { &crate_name, ) .map(|(krate, resolver)| { - (krate, Steal::new(Rc::new(RefCell::new(resolver))), lint_store) + (Lrc::new(krate), Steal::new(Rc::new(RefCell::new(resolver))), lint_store) }) }) } @@ -213,25 +214,26 @@ impl<'tcx> Queries<'tcx> { pub fn lower_to_hir( &'tcx self, - ) -> Result<&Query<(&'tcx map::Forest<'tcx>, Steal)>> { + ) -> Result<&Query<(&'tcx map::Forest<'tcx>, Future<'tcx, ()>, Steal)>> { self.lower_to_hir.compute(|| { let expansion_result = self.expansion()?; let peeked = expansion_result.peek(); - let krate = &peeked.0; + let krate = peeked.0.clone(); let resolver = peeked.1.steal(); - let lint_store = &peeked.2; - let hir = resolver.borrow_mut().access(|resolver| { + let lint_store = peeked.2.clone(); + let (hir, lints) = resolver.borrow_mut().access(|resolver| { passes::lower_to_hir( self.session(), lint_store, resolver, &*self.dep_graph()?.peek(), - &krate, + krate, &self.arena, + &self.scope, ) })?; let hir = self.arena.alloc(hir); - Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver)))) + Ok((hir, lints, Steal::new(BoxedResolver::to_resolver_outputs(resolver)))) }) } @@ -257,7 +259,7 @@ impl<'tcx> Queries<'tcx> { let outputs = self.prepare_outputs()?.peek().clone(); let lint_store = self.expansion()?.peek().2.clone(); let hir = self.lower_to_hir()?.peek(); - let (ref hir_forest, ref resolver_outputs) = &*hir; + let (ref hir_forest, _, ref resolver_outputs) = &*hir; Ok(passes::create_global_ctxt( self.compiler, lint_store, @@ -361,6 +363,7 @@ impl Compiler { queries.global_ctxt()?; // Drop AST after creating GlobalCtxt to free memory. + queries.lower_to_hir()?.take().1.join(); mem::drop(queries.expansion()?.take()); queries.ongoing_codegen()?;