From c66b84457f203285134f92c1c141716a030266eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 4 Dec 2018 16:26:34 +0100 Subject: [PATCH] Tweak query code for performance --- src/librustc/dep_graph/dep_node.rs | 33 +++++--- src/librustc/hir/map/mod.rs | 4 + src/librustc/ich/hcx.rs | 1 + src/librustc/lib.rs | 2 + src/librustc/session/mod.rs | 20 ++++- src/librustc/ty/context.rs | 21 +++-- src/librustc/ty/query/job.rs | 69 +++++++++------- src/librustc/ty/query/mod.rs | 6 +- src/librustc/ty/query/plumbing.rs | 87 +++++++++++++-------- src/librustc_data_structures/fingerprint.rs | 1 + src/librustc_data_structures/lib.rs | 22 ++++++ src/libsyntax/parse/mod.rs | 1 + 12 files changed, 185 insertions(+), 82 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index f0c6196412a..f8478a433a5 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -162,7 +162,9 @@ pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { } } - #[inline] + // FIXME: Make `is_anon`, `is_input`, `is_eval_always` and `has_params` properties + // of queries + #[inline(always)] pub fn is_anon(&self) -> bool { match *self { $( @@ -171,8 +173,8 @@ pub fn is_anon(&self) -> bool { } } - #[inline] - pub fn is_input(&self) -> bool { + #[inline(always)] + pub fn is_input_inlined(&self) -> bool { match *self { $( DepKind :: $variant => { contains_input_attr!($($attr),*) } @@ -180,7 +182,11 @@ pub fn is_input(&self) -> bool { } } - #[inline] + pub fn is_input(&self) -> bool { + self.is_input_inlined() + } + + #[inline(always)] pub fn is_eval_always(&self) -> bool { match *self { $( @@ -190,8 +196,8 @@ pub fn is_eval_always(&self) -> bool { } #[allow(unreachable_code)] - #[inline] - pub fn has_params(&self) -> bool { + #[inline(always)] + pub fn has_params_inlined(&self) -> bool { match *self { $( DepKind :: $variant => { @@ -212,6 +218,10 @@ pub fn has_params(&self) -> bool { )* } } + + pub fn has_params(&self) -> bool { + self.has_params_inlined() + } } pub enum DepConstructor<$tcx> { @@ -230,6 +240,7 @@ pub struct DepNode { impl DepNode { #[allow(unreachable_code, non_snake_case)] + #[inline(always)] pub fn new<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, dep: DepConstructor<'gcx>) -> DepNode @@ -299,7 +310,7 @@ pub fn new<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, /// Construct a DepNode from the given DepKind and DefPathHash. This /// method will assert that the given DepKind actually requires a /// single DefId/DefPathHash parameter. - #[inline] + #[inline(always)] pub fn from_def_path_hash(kind: DepKind, def_path_hash: DefPathHash) -> DepNode { @@ -313,9 +324,9 @@ pub fn from_def_path_hash(kind: DepKind, /// Create a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually /// does not require any parameters. - #[inline] + #[inline(always)] pub fn new_no_params(kind: DepKind) -> DepNode { - assert!(!kind.has_params()); + assert!(!kind.has_params_inlined()); DepNode { kind, hash: Fingerprint::ZERO, @@ -418,14 +429,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { impl DefPathHash { - #[inline] + #[inline(always)] pub fn to_dep_node(self, kind: DepKind) -> DepNode { DepNode::from_def_path_hash(kind, self) } } impl DefId { - #[inline] + #[inline(always)] pub fn to_dep_node(self, tcx: TyCtxt<'_, '_, '_>, kind: DepKind) -> DepNode { DepNode::from_def_path_hash(kind, tcx.def_path_hash(self)) } diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index b98e279aef4..9b84e0570f2 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -159,6 +159,10 @@ pub fn krate<'hir>(&'hir self) -> &'hir Crate { self.dep_graph.read(DepNode::new_no_params(DepKind::Krate)); &self.krate } + + pub fn untracked_krate<'hir>(&'hir self) -> &'hir Crate { + &self.krate + } } /// Represents a mapping from Node IDs to AST elements and their parent diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 2e56308daf7..799c2df8a53 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -86,6 +86,7 @@ impl<'a> StableHashingContext<'a> { // The `krate` here is only used for mapping BodyIds to Bodies. // Don't use it for anything else or you'll run the risk of // leaking data out of the tracking system. + #[inline] pub fn new(sess: &'a Session, krate: &'a hir::Crate, definitions: &'a Definitions, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4324cfc7b5f..b76fb0ed08c 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -60,10 +60,12 @@ #![feature(slice_sort_by_cached_key)] #![feature(specialization)] #![feature(unboxed_closures)] +#![feature(thread_local)] #![feature(trace_macros)] #![feature(trusted_len)] #![feature(vec_remove_item)] #![feature(step_trait)] +#![feature(stmt_expr_attributes)] #![feature(integer_atomics)] #![feature(test)] #![feature(in_band_lifetimes)] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 12b5646e7f1..180019ac387 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -131,6 +131,9 @@ pub struct Session { /// Used by -Z profile-queries in util::common pub profile_channel: Lock>>, + /// Used by -Z self-profile + pub self_profiling_active: bool, + /// Used by -Z self-profile pub self_profiling: Lock, @@ -823,10 +826,17 @@ pub fn incr_comp_session_dir_opt(&self) -> Option> { } } + #[inline(never)] + #[cold] + fn profiler_active ()>(&self, f: F) { + let mut profiler = self.self_profiling.borrow_mut(); + f(&mut profiler); + } + + #[inline(always)] pub fn profiler ()>(&self, f: F) { - if self.opts.debugging_opts.self_profile || self.opts.debugging_opts.profile_json { - let mut profiler = self.self_profiling.borrow_mut(); - f(&mut profiler); + if unlikely!(self.self_profiling_active) { + self.profiler_active(f) } } @@ -1145,6 +1155,9 @@ pub fn build_session_( CguReuseTracker::new_disabled() }; + let self_profiling_active = sopts.debugging_opts.self_profile || + sopts.debugging_opts.profile_json; + let sess = Session { target: target_cfg, host, @@ -1177,6 +1190,7 @@ pub fn build_session_( imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), cgu_reuse_tracker, + self_profiling_active, self_profiling: Lock::new(SelfProfiler::new()), profile_channel: Lock::new(None), perf_stats: PerfStats { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e27d7349877..185424152bb 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1336,8 +1336,9 @@ pub fn crate_data_as_rc_any(self, cnum: CrateNum) -> Lrc { self.cstore.crate_data_as_rc_any(cnum) } + #[inline(always)] pub fn create_stable_hashing_context(self) -> StableHashingContext<'a> { - let krate = self.dep_graph.with_ignore(|| self.hir().krate()); + let krate = self.gcx.hir_map.forest.untracked_krate(); StableHashingContext::new(self.sess, krate, @@ -1925,7 +1926,17 @@ fn get_tlv() -> usize { /// A thread local variable which stores a pointer to the current ImplicitCtxt #[cfg(not(parallel_queries))] - thread_local!(static TLV: Cell = Cell::new(0)); + // Accessing `thread_local` in another crate is bugged, so we have + // two accessors `set_raw_tlv` and `get_tlv` which do not have an + // inline attribute to prevent that + #[thread_local] + static TLV: Cell = Cell::new(0); + + /// This is used to set the pointer to the current ImplicitCtxt. + #[cfg(not(parallel_queries))] + fn set_raw_tlv(value: usize) { + TLV.set(value) + } /// Sets TLV to `value` during the call to `f`. /// It is restored to its previous value after. @@ -1933,15 +1944,15 @@ fn get_tlv() -> usize { #[cfg(not(parallel_queries))] fn set_tlv R, R>(value: usize, f: F) -> R { let old = get_tlv(); - let _reset = OnDrop(move || TLV.with(|tlv| tlv.set(old))); - TLV.with(|tlv| tlv.set(value)); + let _reset = OnDrop(move || set_raw_tlv(old)); + set_raw_tlv(value); f() } /// This is used to get the pointer to the current ImplicitCtxt. #[cfg(not(parallel_queries))] fn get_tlv() -> usize { - TLV.with(|tlv| tlv.get()) + TLV.get() } /// This is a callback from libsyntax as it cannot access the implicit state diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index 1439e41bb31..994a80fe4cd 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs @@ -18,6 +18,11 @@ use ty::tls; use ty::query::Query; use ty::query::plumbing::CycleError; +#[cfg(not(parallel_queries))] +use ty::query::{ + plumbing::TryGetJob, + config::QueryDescription, +}; use ty::context::TyCtxt; use errors::Diagnostic; use std::process; @@ -83,36 +88,44 @@ pub fn new(info: QueryInfo<'tcx>, parent: Option>>) -> Self { /// /// For single threaded rustc there's no concurrent jobs running, so if we are waiting for any /// query that means that there is a query cycle, thus this always running a cycle error. + #[cfg(not(parallel_queries))] + #[inline(never)] + #[cold] + pub(super) fn await<'lcx, 'a, D: QueryDescription<'tcx>>( + &self, + tcx: TyCtxt<'_, 'tcx, 'lcx>, + span: Span, + ) -> TryGetJob<'a, 'tcx, D> { + TryGetJob::JobCompleted(Err(Box::new(self.find_cycle_in_stack(tcx, span)))) + } + + /// Awaits for the query job to complete. + /// + /// For single threaded rustc there's no concurrent jobs running, so if we are waiting for any + /// query that means that there is a query cycle, thus this always running a cycle error. + #[cfg(parallel_queries)] pub(super) fn await<'lcx>( &self, tcx: TyCtxt<'_, 'tcx, 'lcx>, span: Span, - ) -> Result<(), CycleError<'tcx>> { - #[cfg(not(parallel_queries))] - { - self.find_cycle_in_stack(tcx, span) - } - - #[cfg(parallel_queries)] - { - tls::with_related_context(tcx, move |icx| { - let mut waiter = Lrc::new(QueryWaiter { - query: icx.query.clone(), - span, - cycle: Lock::new(None), - condvar: Condvar::new(), - }); - self.latch.await(&waiter); - // FIXME: Get rid of this lock. We have ownership of the QueryWaiter - // although another thread may still have a Lrc reference so we cannot - // use Lrc::get_mut - let mut cycle = waiter.cycle.lock(); - match cycle.take() { - None => Ok(()), - Some(cycle) => Err(cycle) - } - }) - } + ) -> Result<(), Box>> { + tls::with_related_context(tcx, move |icx| { + let mut waiter = Lrc::new(QueryWaiter { + query: icx.query.clone(), + span, + cycle: Lock::new(None), + condvar: Condvar::new(), + }); + self.latch.await(&waiter); + // FIXME: Get rid of this lock. We have ownership of the QueryWaiter + // although another thread may still have a Lrc reference so we cannot + // use Lrc::get_mut + let mut cycle = waiter.cycle.lock(); + match cycle.take() { + None => Ok(()), + Some(cycle) => Err(Box::new(cycle)) + } + }) } #[cfg(not(parallel_queries))] @@ -120,7 +133,7 @@ fn find_cycle_in_stack<'lcx>( &self, tcx: TyCtxt<'_, 'tcx, 'lcx>, span: Span, - ) -> Result<(), CycleError<'tcx>> { + ) -> CycleError<'tcx> { // Get the current executing query (waiter) and find the waitee amongst its parents let mut current_job = tls::with_related_context(tcx, |icx| icx.query.clone()); let mut cycle = Vec::new(); @@ -140,7 +153,7 @@ fn find_cycle_in_stack<'lcx>( let usage = job.parent.as_ref().map(|parent| { (job.info.span, parent.info.query.clone()) }); - return Err(CycleError { usage, cycle }); + return CycleError { usage, cycle }; } current_job = job.parent.clone(); diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 5cd06fb8a52..f760ebbd759 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -705,21 +705,21 @@ pub fn try_adt_sized_constraint( self, span: Span, key: DefId, - ) -> Result<&'tcx [Ty<'tcx>], DiagnosticBuilder<'a>> { + ) -> Result<&'tcx [Ty<'tcx>], Box>> { self.try_get_query::>(span, key) } pub fn try_needs_drop_raw( self, span: Span, key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result> { + ) -> Result>> { self.try_get_query::>(span, key) } pub fn try_optimized_mir( self, span: Span, key: DefId, - ) -> Result<&'tcx mir::Mir<'tcx>, DiagnosticBuilder<'a>> { + ) -> Result<&'tcx mir::Mir<'tcx>, Box>> { self.try_get_query::>(span, key) } } diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 5f33d466c4a..157e0fba9de 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -153,8 +153,14 @@ pub(super) fn try_get( }; mem::drop(lock); - if let Err(cycle) = job.await(tcx, span) { - return TryGetJob::JobCompleted(Err(cycle)); + #[cfg(not(parallel_queries))] + return job.await(tcx, span); + + #[cfg(parallel_queries)] + { + if let Err(cycle) = job.await(tcx, span) { + return TryGetJob::JobCompleted(Err(cycle)); + } } } } @@ -241,12 +247,16 @@ pub(super) enum TryGetJob<'a, 'tcx: 'a, D: QueryDescription<'tcx> + 'a> { /// The query was already completed. /// Returns the result of the query and its dep node index /// if it succeeded or a cycle error if it failed - JobCompleted(Result<(D::Value, DepNodeIndex), CycleError<'tcx>>), + JobCompleted(Result<(D::Value, DepNodeIndex), Box>>), } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { - pub(super) fn report_cycle(self, CycleError { usage, cycle: stack }: CycleError<'gcx>) - -> DiagnosticBuilder<'a> + #[inline(never)] + #[cold] + pub(super) fn report_cycle( + self, + box CycleError { usage, cycle: stack }: Box> + ) -> Box> { assert!(!stack.is_empty()); @@ -280,7 +290,7 @@ pub(super) fn report_cycle(self, CycleError { usage, cycle: stack }: CycleError< &format!("cycle used when {}", query.describe(self))); } - return err + return Box::new(err) }) } @@ -345,11 +355,12 @@ pub(super) fn try_mark_green_and_read(self, dep_node: &DepNode) -> Option>( self, span: Span, key: Q::Key) - -> Result> + -> Result>> { debug!("ty::queries::{}::try_get_with(key={:?}, span={:?})", Q::NAME, @@ -409,7 +420,7 @@ fn try_get_with>( return Ok(result); } - if !dep_node.kind.is_input() { + if !dep_node.kind.is_input_inlined() { if let Some(dep_node_index) = self.try_mark_green_and_read(&dep_node) { profq_msg!(self, ProfileQueriesMsg::CacheHit); self.sess.profiler(|p| p.record_query_hit(Q::CATEGORY)); @@ -436,7 +447,7 @@ fn load_from_disk_and_cache_in_memory>( job: JobOwner<'a, 'gcx, Q>, dep_node_index: DepNodeIndex, dep_node: &DepNode - ) -> Result> + ) -> Result>> { // Note this function can be called concurrently from the same query // We must ensure that this is handled correctly @@ -522,7 +533,7 @@ fn force_query_with_job>( key: Q::Key, job: JobOwner<'_, 'gcx, Q>, dep_node: DepNode) - -> Result<(Q::Value, DepNodeIndex), CycleError<'gcx>> { + -> Result<(Q::Value, DepNodeIndex), Box>> { // If the following assertion triggers, it can have two reasons: // 1. Something is wrong with DepNode creation, either here or // in DepGraph::try_mark_green() @@ -585,7 +596,7 @@ pub(super) fn ensure_query>(self, key: Q::Key) -> () { // Ensuring an "input" or anonymous query makes no sense assert!(!dep_node.kind.is_anon()); - assert!(!dep_node.kind.is_input()); + assert!(!dep_node.kind.is_input_inlined()); if self.try_mark_green_and_read(&dep_node).is_none() { // A None return from `try_mark_green_and_read` means that this is either // a new dep node or that the dep node has already been marked red. @@ -611,37 +622,55 @@ fn force_query>( key: Q::Key, span: Span, dep_node: DepNode - ) -> Result<(Q::Value, DepNodeIndex), CycleError<'gcx>> { + ) { + profq_msg!( + self, + ProfileQueriesMsg::QueryBegin(span.data(), profq_query_msg!(Q::NAME, self, key)) + ); + // We may be concurrently trying both execute and force a query // Ensure that only one of them runs the query let job = match JobOwner::try_get(self, span, &key) { TryGetJob::NotYetStarted(job) => job, - TryGetJob::JobCompleted(result) => return result, + TryGetJob::JobCompleted(_) => return, }; - self.force_query_with_job::(key, job, dep_node) + if let Err(e) = self.force_query_with_job::(key, job, dep_node) { + self.report_cycle(e).emit(); + } } pub(super) fn try_get_query>( self, span: Span, key: Q::Key, - ) -> Result> { + ) -> Result>> { match self.try_get_with::(span, key) { Ok(e) => Ok(e), Err(e) => Err(self.report_cycle(e)), } } + // FIXME: Try uninlining this + #[inline(always)] pub(super) fn get_query>( self, span: Span, key: Q::Key, ) -> Q::Value { - self.try_get_query::(span, key).unwrap_or_else(|mut e| { - e.emit(); - Q::handle_cycle_error(self) + self.try_get_with::(span, key).unwrap_or_else(|e| { + self.emit_error::(e) }) } + + #[inline(never)] + #[cold] + fn emit_error>( + self, + e: Box>, + ) -> Q::Value { + self.report_cycle(e).emit(); + Q::handle_cycle_error(self) + } } macro_rules! handle_cycle_error { @@ -806,15 +835,18 @@ pub fn $name R, R>(f: F) -> R { } impl<$tcx> QueryAccessors<$tcx> for queries::$name<$tcx> { + #[inline(always)] fn query(key: Self::Key) -> Query<'tcx> { Query::$name(key) } + #[inline(always)] fn query_cache<'a>(tcx: TyCtxt<'a, $tcx, '_>) -> &'a Lock> { &tcx.queries.$name } #[allow(unused)] + #[inline(always)] fn to_dep_node(tcx: TyCtxt<'_, $tcx, '_>, key: &Self::Key) -> DepNode { use dep_graph::DepConstructor::*; @@ -861,6 +893,7 @@ pub struct TyCtxtAt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> Deref for TyCtxtAt<'a, 'gcx, 'tcx> { type Target = TyCtxt<'a, 'gcx, 'tcx>; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.tcx } @@ -869,6 +902,7 @@ fn deref(&self) -> &Self::Target { impl<'a, $tcx, 'lcx> TyCtxt<'a, $tcx, 'lcx> { /// Return a transparent wrapper for `TyCtxt` which uses /// `span` as the location of queries performed through it. + #[inline(always)] pub fn at(self, span: Span) -> TyCtxtAt<'a, $tcx, 'lcx> { TyCtxtAt { tcx: self, @@ -877,6 +911,7 @@ pub fn at(self, span: Span) -> TyCtxtAt<'a, $tcx, 'lcx> { } $($(#[$attr])* + #[inline(always)] pub fn $name(self, key: $K) -> $V { self.at(DUMMY_SP).$name(key) })* @@ -884,6 +919,7 @@ pub fn $name(self, key: $K) -> $V { impl<'a, $tcx, 'lcx> TyCtxtAt<'a, $tcx, 'lcx> { $($(#[$attr])* + #[inline(always)] pub fn $name(self, key: $K) -> $V { self.tcx.get_query::>(self.span, key) })* @@ -1023,20 +1059,7 @@ macro_rules! krate { macro_rules! force { ($query:ident, $key:expr) => { { - use $crate::util::common::{ProfileQueriesMsg, profq_msg}; - - profq_msg!(tcx, - ProfileQueriesMsg::QueryBegin( - DUMMY_SP.data(), - profq_query_msg!(::ty::query::queries::$query::NAME, tcx, $key), - ) - ); - - if let Err(e) = tcx.force_query::<::ty::query::queries::$query<'_>>( - $key, DUMMY_SP, *dep_node - ) { - tcx.report_cycle(e).emit(); - } + tcx.force_query::<::ty::query::queries::$query<'_>>($key, DUMMY_SP, *dep_node); } } }; diff --git a/src/librustc_data_structures/fingerprint.rs b/src/librustc_data_structures/fingerprint.rs index aa9ddda2b93..f8638213b3a 100644 --- a/src/librustc_data_structures/fingerprint.rs +++ b/src/librustc_data_structures/fingerprint.rs @@ -86,6 +86,7 @@ fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { } impl stable_hasher::StableHasherResult for Fingerprint { + #[inline] fn finish(hasher: stable_hasher::StableHasher) -> Self { let (_0, _1) = hasher.finalize(); Fingerprint(_0, _1) diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 8e0ecb70c68..bc2b8f1d652 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -30,6 +30,8 @@ #![feature(allow_internal_unstable)] #![feature(vec_resize_with)] #![feature(hash_raw_entry)] +#![feature(stmt_expr_attributes)] +#![feature(core_intrinsics)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] @@ -58,6 +60,26 @@ pub use rustc_serialize::hex::ToHex; +#[macro_export] +macro_rules! likely { + ($e:expr) => { + #[allow(unused_unsafe)] + { + unsafe { std::intrinsics::likely($e) } + } + } +} + +#[macro_export] +macro_rules! unlikely { + ($e:expr) => { + #[allow(unused_unsafe)] + { + unsafe { std::intrinsics::unlikely($e) } + } + } +} + pub mod macros; pub mod svh; pub mod base_n; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index e3cccacb3c3..200b1cecc03 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -81,6 +81,7 @@ pub fn with_span_handler(handler: Handler, source_map: Lrc) -> ParseS } } + #[inline] pub fn source_map(&self) -> &SourceMap { &self.source_map }