diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 1c6b623bbf267..bdbb169bc64f1 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -242,6 +242,20 @@ pub(crate) fn eval_to_valtree<'tcx>( cid: GlobalId<'tcx>, ) -> EvalToValTreeResult<'tcx> { crate::assert_typing_mode(typing_env.typing_mode()); + + // Fast path: a trivial scalar constant of leaf type — e.g. a literal + // array length or const-generic argument, the most common type-level + // consts — would otherwise spin up two interpreter contexts (one to + // allocate and validate, one to read back) just to rebuild a value we + // already know. Build the leaf valtree directly. + if cid.promoted.is_none() + && let Some((mir::ConstValue::Scalar(Scalar::Int(int)), ty)) = + tcx.trivial_const(cid.instance.def_id()) + && ty.is_primitive() + { + return Ok(ty::ValTree::from_scalar_int(tcx, int)); + } + let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?; // FIXME Need to provide a span to `eval_to_valtree` diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f2a6c2747f380..421a4f1b39474 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2357,6 +2357,13 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] fn check_false_global_bounds(&mut self) { let tcx = self.ocx.infcx.tcx; + + // No predicates, no possible false global bound; skip building the + // elaborator. This is the case for most items. + if tcx.predicates_of(self.body_def_id).predicates.is_empty() { + return; + } + let mut span = tcx.def_span(self.body_def_id); let empty_env = ty::ParamEnv::empty(); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index ceb044c102f69..3f3c698efad0c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -208,7 +208,7 @@ use std::cell::OnceCell; use std::ops::ControlFlow; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync::{Lock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; @@ -287,17 +287,25 @@ impl<'tcx> UsageMap<'tcx> { } /// Internally iterate over all inlined items used by `item`. + /// + /// `inlined_memo` caches `instantiation_mode` per item: it is a pure + /// function of the item, but it performs several query lookups, and an + /// inlined item reachable from N roots would otherwise recompute it N + /// times during partitioning. pub(crate) fn for_each_inlined_used_item( &self, tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, + inlined_memo: &mut FxHashMap, bool>, mut f: F, ) where F: FnMut(MonoItem<'tcx>), { let used_items = self.used_map.get(&item).unwrap(); for used_item in used_items.iter() { - let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy; + let is_inlined = *inlined_memo.entry(*used_item).or_insert_with(|| { + used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy + }); if is_inlined { f(*used_item); } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index bf4a2bdd15107..b04db15701770 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -98,7 +98,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::par_join; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::LangItem; @@ -216,6 +216,7 @@ where let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); let cgu_name_cache = &mut UnordMap::default(); + let mut inlined_memo = FxHashMap::default(); for mono_item in mono_items { // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items // are handled at the bottom of the loop based on reachability, with one exception. @@ -264,7 +265,13 @@ where // external crates, and local functions the definition of which is // marked with `#[inline]`. let mut reachable_inlined_items = FxIndexSet::default(); - get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items); + get_reachable_inlined_items( + cx.tcx, + mono_item, + cx.usage_map, + &mut inlined_memo, + &mut reachable_inlined_items, + ); // Add those inlined items. It's possible an inlined item is reachable // from multiple root items within a CGU, which is fine, it just means @@ -301,14 +308,18 @@ where tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, usage_map: &UsageMap<'tcx>, + inlined_memo: &mut FxHashMap, bool>, visited: &mut FxIndexSet>, ) { - usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { - let is_new = visited.insert(inlined_item); - if is_new { - get_reachable_inlined_items(tcx, inlined_item, usage_map, visited); - } - }); + let mut worklist = vec![item]; + while let Some(item) = worklist.pop() { + usage_map.for_each_inlined_used_item(tcx, item, inlined_memo, |inlined_item| { + let is_new = visited.insert(inlined_item); + if is_new { + worklist.push(inlined_item); + } + }); + } } }