Skip to content

Commit 372bbda

Browse files
committed
Auto merge of #114669 - cjgillot:metadata-wp, r=<try>
Make metadata a workproduct and reuse it This PR aims to skip the generation of metadata by reusing the infrastructure that already exists for compiled codegen-units, namely "workproducts". This can yield substantial gains (~10%) when we can demonstrate that metadata does not change between an incremental session and the next. This is the case if the crate is unchanged, or if all the changes are in upstream crates and have no effect on it. This latter case is most interesting, as it arises regularly for users with several crates in their workspace. TODO: - [x] Materialize the fact that metadata encoding relies on the relative order of definitions; - [x] Refactor the handling of doc links.
2 parents d14d202 + 79714d5 commit 372bbda

File tree

10 files changed

+143
-61
lines changed

10 files changed

+143
-61
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4059,6 +4059,7 @@ dependencies = [
40594059
"rustc_fs_util",
40604060
"rustc_hir",
40614061
"rustc_hir_pretty",
4062+
"rustc_incremental",
40624063
"rustc_index",
40634064
"rustc_macros",
40644065
"rustc_middle",

compiler/rustc_interface/src/queries.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,24 @@ impl Linker {
4545
}
4646

4747
pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) {
48-
let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || {
48+
let (codegen_results, mut work_products) = sess.time("finish_ongoing_codegen", || {
4949
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)
5050
});
5151
sess.timings.end_section(sess.dcx(), TimingSection::Codegen);
5252

53+
if sess.opts.incremental.is_some()
54+
&& let Some(path) = self.metadata.path()
55+
&& let Some((id, product)) =
56+
rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
57+
sess,
58+
"metadata",
59+
&[("rmeta", path)],
60+
&[],
61+
)
62+
{
63+
work_products.insert(id, product);
64+
}
65+
5366
sess.dcx().abort_if_errors();
5467

5568
let _timer = sess.timer("link");

compiler/rustc_metadata/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
2020
rustc_fs_util = { path = "../rustc_fs_util" }
2121
rustc_hir = { path = "../rustc_hir" }
2222
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
23+
rustc_incremental = { path = "../rustc_incremental" }
2324
rustc_index = { path = "../rustc_index" }
2425
rustc_macros = { path = "../rustc_macros" }
2526
rustc_middle = { path = "../rustc_middle" }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 82 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15961596
let table = tcx.associated_types_for_impl_traits_in_associated_fn(def_id);
15971597
record_defaulted_array!(self.tables.associated_types_for_impl_traits_in_associated_fn[def_id] <- table);
15981598
}
1599+
if let DefKind::Mod = tcx.def_kind(def_id) {
1600+
record!(self.tables.doc_link_resolutions[def_id] <- tcx.doc_link_resolutions(def_id));
1601+
record_array!(self.tables.doc_link_traits_in_scope[def_id] <- tcx.doc_link_traits_in_scope(def_id));
1602+
}
15991603
}
16001604

16011605
for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls {
@@ -1604,14 +1608,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
16041608
def_id.index
16051609
}));
16061610
}
1607-
1608-
for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions {
1609-
record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map);
1610-
}
1611-
1612-
for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope {
1613-
record_array!(self.tables.doc_link_traits_in_scope[def_id.to_def_id()] <- traits);
1614-
}
16151611
}
16161612

16171613
#[instrument(level = "trace", skip(self))]
@@ -2283,6 +2279,8 @@ pub struct EncodedMetadata {
22832279
// This is an optional stub metadata containing only the crate header.
22842280
// The header should be very small, so we load it directly into memory.
22852281
stub_metadata: Option<Vec<u8>>,
2282+
// The path containing the metadata, to record as work product.
2283+
path: Option<Box<Path>>,
22862284
// We need to carry MaybeTempDir to avoid deleting the temporary
22872285
// directory while accessing the Mmap.
22882286
_temp_dir: Option<MaybeTempDir>,
@@ -2298,14 +2296,24 @@ impl EncodedMetadata {
22982296
let file = std::fs::File::open(&path)?;
22992297
let file_metadata = file.metadata()?;
23002298
if file_metadata.len() == 0 {
2301-
return Ok(Self { full_metadata: None, stub_metadata: None, _temp_dir: None });
2299+
return Ok(Self {
2300+
full_metadata: None,
2301+
stub_metadata: None,
2302+
path: None,
2303+
_temp_dir: None,
2304+
});
23022305
}
23032306
let full_mmap = unsafe { Some(Mmap::map(file)?) };
23042307

23052308
let stub =
23062309
if let Some(stub_path) = stub_path { Some(std::fs::read(stub_path)?) } else { None };
23072310

2308-
Ok(Self { full_metadata: full_mmap, stub_metadata: stub, _temp_dir: temp_dir })
2311+
Ok(Self {
2312+
full_metadata: full_mmap,
2313+
stub_metadata: stub,
2314+
path: Some(path.into()),
2315+
_temp_dir: temp_dir,
2316+
})
23092317
}
23102318

23112319
#[inline]
@@ -2317,6 +2325,11 @@ impl EncodedMetadata {
23172325
pub fn stub_or_full(&self) -> &[u8] {
23182326
self.stub_metadata.as_deref().unwrap_or(self.full())
23192327
}
2328+
2329+
#[inline]
2330+
pub fn path(&self) -> Option<&Path> {
2331+
self.path.as_deref()
2332+
}
23202333
}
23212334

23222335
impl<S: Encoder> Encodable<S> for EncodedMetadata {
@@ -2341,11 +2354,47 @@ impl<D: Decoder> Decodable<D> for EncodedMetadata {
23412354
None
23422355
};
23432356

2344-
Self { full_metadata, stub_metadata: stub, _temp_dir: None }
2357+
Self { full_metadata, stub_metadata: stub, path: None, _temp_dir: None }
23452358
}
23462359
}
23472360

2361+
#[instrument(level = "trace", skip(tcx))]
23482362
pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
2363+
if let Some(ref_path) = ref_path {
2364+
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub");
2365+
2366+
with_encode_metadata_header(tcx, ref_path, |ecx| {
2367+
let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
2368+
name: tcx.crate_name(LOCAL_CRATE),
2369+
triple: tcx.sess.opts.target_triple.clone(),
2370+
hash: tcx.crate_hash(LOCAL_CRATE),
2371+
is_proc_macro_crate: false,
2372+
is_stub: true,
2373+
});
2374+
header.position.get()
2375+
});
2376+
}
2377+
2378+
let dep_node = tcx.metadata_dep_node();
2379+
2380+
if tcx.dep_graph.is_fully_enabled()
2381+
&& let work_product_id = &rustc_middle::dep_graph::WorkProductId::from_cgu_name("metadata")
2382+
&& let Some(work_product) = tcx.dep_graph.previous_work_product(work_product_id)
2383+
&& tcx.try_mark_green(&dep_node)
2384+
{
2385+
let saved_path = &work_product.saved_files["rmeta"];
2386+
let incr_comp_session_dir = tcx.sess.incr_comp_session_dir_opt().unwrap();
2387+
let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, saved_path);
2388+
debug!("copying preexisting metadata from {source_file:?} to {path:?}");
2389+
match rustc_fs_util::link_or_copy(&source_file, path) {
2390+
Ok(_) => {}
2391+
Err(err) => {
2392+
tcx.dcx().emit_fatal(FailCreateFileEncoder { err });
2393+
}
2394+
};
2395+
return;
2396+
};
2397+
23492398
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
23502399

23512400
// Since encoding metadata is not in a query, and nothing is cached,
@@ -2359,35 +2408,30 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
23592408
join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE));
23602409
}
23612410

2362-
with_encode_metadata_header(tcx, path, |ecx| {
2363-
// Encode all the entries and extra information in the crate,
2364-
// culminating in the `CrateRoot` which points to all of it.
2365-
let root = ecx.encode_crate_root();
2366-
2367-
// Flush buffer to ensure backing file has the correct size.
2368-
ecx.opaque.flush();
2369-
// Record metadata size for self-profiling
2370-
tcx.prof.artifact_size(
2371-
"crate_metadata",
2372-
"crate_metadata",
2373-
ecx.opaque.file().metadata().unwrap().len(),
2374-
);
2375-
2376-
root.position.get()
2377-
});
2411+
tcx.dep_graph.with_task(
2412+
dep_node,
2413+
tcx,
2414+
path,
2415+
|tcx, path| {
2416+
with_encode_metadata_header(tcx, path, |ecx| {
2417+
// Encode all the entries and extra information in the crate,
2418+
// culminating in the `CrateRoot` which points to all of it.
2419+
let root = ecx.encode_crate_root();
2420+
2421+
// Flush buffer to ensure backing file has the correct size.
2422+
ecx.opaque.flush();
2423+
// Record metadata size for self-profiling
2424+
tcx.prof.artifact_size(
2425+
"crate_metadata",
2426+
"crate_metadata",
2427+
ecx.opaque.file().metadata().unwrap().len(),
2428+
);
23782429

2379-
if let Some(ref_path) = ref_path {
2380-
with_encode_metadata_header(tcx, ref_path, |ecx| {
2381-
let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
2382-
name: tcx.crate_name(LOCAL_CRATE),
2383-
triple: tcx.sess.opts.target_triple.clone(),
2384-
hash: tcx.crate_hash(LOCAL_CRATE),
2385-
is_proc_macro_crate: false,
2386-
is_stub: true,
2430+
root.position.get()
23872431
});
2388-
header.position.get()
2389-
});
2390-
}
2432+
},
2433+
None,
2434+
);
23912435
}
23922436

23932437
fn with_encode_metadata_header(

compiler/rustc_middle/src/dep_graph/dep_node.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ rustc_with_all_queries!(define_dep_nodes![
9898
[] fn TraitSelect() -> (),
9999
[] fn CompileCodegenUnit() -> (),
100100
[] fn CompileMonoItem() -> (),
101+
[] fn Metadata() -> (),
101102
]);
102103

103104
// WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys.
@@ -115,6 +116,12 @@ pub(crate) fn make_compile_mono_item<'tcx>(
115116
DepNode::construct(tcx, dep_kinds::CompileMonoItem, mono_item)
116117
}
117118

119+
// WARNING: `construct` is generic and does not know that `Metadata` takes `()`s as keys.
120+
// Be very careful changing this type signature!
121+
pub(crate) fn make_metadata(tcx: TyCtxt<'_>) -> DepNode {
122+
DepNode::construct(tcx, dep_kinds::Metadata, &())
123+
}
124+
118125
pub trait DepNodeExt: Sized {
119126
fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>;
120127

compiler/rustc_middle/src/dep_graph/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::ty::{self, TyCtxt};
99
mod dep_node;
1010

1111
pub use dep_node::{DepKind, DepNode, DepNodeExt, dep_kinds, label_strs};
12-
pub(crate) use dep_node::{make_compile_codegen_unit, make_compile_mono_item};
12+
pub(crate) use dep_node::{make_compile_codegen_unit, make_compile_mono_item, make_metadata};
1313
pub use rustc_query_system::dep_graph::debug::{DepNodeFilter, EdgeFilter};
1414
pub use rustc_query_system::dep_graph::{
1515
DepContext, DepGraphQuery, DepNodeIndex, Deps, SerializedDepGraph, SerializedDepNodeIndex,

compiler/rustc_middle/src/query/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,16 @@ rustc_queries! {
204204
desc { "getting the resolver for lowering" }
205205
}
206206

207+
/// Named module children from all kinds of items, including imports.
208+
/// In addition to regular items this list also includes struct and variant constructors, and
209+
/// items inside `extern {}` blocks because all of them introduce names into parent module.
210+
///
211+
/// Module here is understood in name resolution sense - it can be a `mod` item,
212+
/// or a crate root, or an enum, or a trait.
213+
query module_children_local(key: LocalDefId) -> &'tcx [ModChild] {
214+
desc { |tcx| "module exports for `{}`", tcx.def_path_str(key) }
215+
}
216+
207217
/// Return the span for a definition.
208218
///
209219
/// Contrary to `def_span` below, this query returns the full absolute span of the definition.

compiler/rustc_middle/src/ty/context.rs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ use crate::arena::Arena;
6363
use crate::dep_graph::{DepGraph, DepKindStruct};
6464
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, CanonicalVarKinds};
6565
use crate::lint::lint_level;
66-
use crate::metadata::ModChild;
6766
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
6867
use crate::middle::{resolve_bound_vars, stability};
6968
use crate::mir::interpret::{self, Allocation, ConstAllocation};
@@ -2089,9 +2088,8 @@ impl<'tcx> TyCtxt<'tcx> {
20892088
}
20902089

20912090
pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> {
2092-
// Create a dependency to the red node to be sure we re-execute this when the amount of
2093-
// definitions change.
2094-
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
2091+
// Depend on the `analysis` query to ensure compilation if finished.
2092+
self.ensure_ok().analysis(());
20952093

20962094
let definitions = &self.untracked.definitions;
20972095
gen {
@@ -2111,9 +2109,8 @@ impl<'tcx> TyCtxt<'tcx> {
21112109
}
21122110

21132111
pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
2114-
// Create a dependency to the crate to be sure we re-execute this when the amount of
2115-
// definitions change.
2116-
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
2112+
// Depend on the `analysis` query to ensure compilation if finished.
2113+
self.ensure_ok().analysis(());
21172114

21182115
// Freeze definitions once we start iterating on them, to prevent adding new ones
21192116
// while iterating. If some query needs to add definitions, it should be `ensure`d above.
@@ -3380,23 +3377,14 @@ impl<'tcx> TyCtxt<'tcx> {
33803377
self.opt_rpitit_info(def_id).is_some()
33813378
}
33823379

3383-
/// Named module children from all kinds of items, including imports.
3384-
/// In addition to regular items this list also includes struct and variant constructors, and
3385-
/// items inside `extern {}` blocks because all of them introduce names into parent module.
3386-
///
3387-
/// Module here is understood in name resolution sense - it can be a `mod` item,
3388-
/// or a crate root, or an enum, or a trait.
3389-
///
3390-
/// This is not a query, making it a query causes perf regressions
3391-
/// (probably due to hashing spans in `ModChild`ren).
3392-
pub fn module_children_local(self, def_id: LocalDefId) -> &'tcx [ModChild] {
3393-
self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..])
3394-
}
3395-
33963380
pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc<ast::Crate>)> {
33973381
self.resolver_for_lowering_raw(()).0
33983382
}
33993383

3384+
pub fn metadata_dep_node(self) -> crate::dep_graph::DepNode {
3385+
crate::dep_graph::make_metadata(self)
3386+
}
3387+
34003388
/// Given an `impl_id`, return the trait it implements.
34013389
/// Return `None` if this is an inherent impl.
34023390
pub fn impl_trait_ref(
@@ -3444,6 +3432,8 @@ pub struct DeducedParamAttrs {
34443432
}
34453433

34463434
pub fn provide(providers: &mut Providers) {
3435+
providers.module_children_local =
3436+
|tcx, def_id| tcx.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..]);
34473437
providers.maybe_unused_trait_imports =
34483438
|tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports;
34493439
providers.names_imported_by_glob_use = |tcx, id| {

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,17 @@ macro_rules! define_queries {
923923
}
924924
}
925925

926+
pub(crate) fn Metadata<'tcx>() -> DepKindStruct<'tcx> {
927+
DepKindStruct {
928+
is_anon: false,
929+
is_eval_always: false,
930+
fingerprint_style: FingerprintStyle::Unit,
931+
force_from_dep_node: None,
932+
try_load_from_on_disk_cache: None,
933+
name: &"Metadata",
934+
}
935+
}
936+
926937
$(pub(crate) fn $name<'tcx>()-> DepKindStruct<'tcx> {
927938
$crate::plumbing::query_callback::<query_impl::$name::QueryType<'tcx>>(
928939
is_anon!([$($modifiers)*]),

compiler/rustc_resolve/src/late.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,12 +1559,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
15591559
}
15601560

15611561
fn with_mod_rib<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
1562-
let module = self.r.expect_module(self.r.local_def_id(id).to_def_id());
1562+
let def_id = self.r.local_def_id(id);
1563+
let module = self.r.expect_module(def_id.to_def_id());
15631564
// Move down in the graph.
15641565
let orig_module = replace(&mut self.parent_scope.module, module);
15651566
self.with_rib(ValueNS, RibKind::Module(module), |this| {
15661567
this.with_rib(TypeNS, RibKind::Module(module), |this| {
15671568
let ret = f(this);
1569+
this.r.doc_link_resolutions.entry(def_id).or_default();
1570+
this.r.doc_link_traits_in_scope.entry(def_id).or_default();
15681571
this.parent_scope.module = orig_module;
15691572
ret
15701573
})
@@ -5247,6 +5250,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
52475250
BuiltinLintDiag::UnusedLabel,
52485251
);
52495252
}
5253+
self.doc_link_resolutions.entry(CRATE_DEF_ID).or_default();
5254+
self.doc_link_traits_in_scope.entry(CRATE_DEF_ID).or_default();
52505255
}
52515256
}
52525257

0 commit comments

Comments
 (0)