Skip to content

Commit e384365

Browse files
committed
Auto merge of #114669 - cjgillot:metadata-wp, r=petrochenkov
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 0c4fa26 + 17ce06a commit e384365

File tree

8 files changed

+123
-35
lines changed

8 files changed

+123
-35
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4133,6 +4133,7 @@ dependencies = [
41334133
"rustc_fs_util",
41344134
"rustc_hir",
41354135
"rustc_hir_pretty",
4136+
"rustc_incremental",
41364137
"rustc_index",
41374138
"rustc_macros",
41384139
"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: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_hir as hir;
1616
use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId, LocalDefIdSet};
1717
use rustc_hir::definitions::DefPathData;
1818
use rustc_hir_pretty::id_to_string;
19+
use rustc_middle::dep_graph::WorkProductId;
1920
use rustc_middle::middle::dependency_format::Linkage;
2021
use rustc_middle::middle::exported_symbols::metadata_symbol_name;
2122
use rustc_middle::mir::interpret;
@@ -2307,6 +2308,8 @@ pub struct EncodedMetadata {
23072308
// This is an optional stub metadata containing only the crate header.
23082309
// The header should be very small, so we load it directly into memory.
23092310
stub_metadata: Option<Vec<u8>>,
2311+
// The path containing the metadata, to record as work product.
2312+
path: Option<Box<Path>>,
23102313
// We need to carry MaybeTempDir to avoid deleting the temporary
23112314
// directory while accessing the Mmap.
23122315
_temp_dir: Option<MaybeTempDir>,
@@ -2322,14 +2325,24 @@ impl EncodedMetadata {
23222325
let file = std::fs::File::open(&path)?;
23232326
let file_metadata = file.metadata()?;
23242327
if file_metadata.len() == 0 {
2325-
return Ok(Self { full_metadata: None, stub_metadata: None, _temp_dir: None });
2328+
return Ok(Self {
2329+
full_metadata: None,
2330+
stub_metadata: None,
2331+
path: None,
2332+
_temp_dir: None,
2333+
});
23262334
}
23272335
let full_mmap = unsafe { Some(Mmap::map(file)?) };
23282336

23292337
let stub =
23302338
if let Some(stub_path) = stub_path { Some(std::fs::read(stub_path)?) } else { None };
23312339

2332-
Ok(Self { full_metadata: full_mmap, stub_metadata: stub, _temp_dir: temp_dir })
2340+
Ok(Self {
2341+
full_metadata: full_mmap,
2342+
stub_metadata: stub,
2343+
path: Some(path.into()),
2344+
_temp_dir: temp_dir,
2345+
})
23332346
}
23342347

23352348
#[inline]
@@ -2341,6 +2354,11 @@ impl EncodedMetadata {
23412354
pub fn stub_or_full(&self) -> &[u8] {
23422355
self.stub_metadata.as_deref().unwrap_or(self.full())
23432356
}
2357+
2358+
#[inline]
2359+
pub fn path(&self) -> Option<&Path> {
2360+
self.path.as_deref()
2361+
}
23442362
}
23452363

23462364
impl<S: Encoder> Encodable<S> for EncodedMetadata {
@@ -2365,17 +2383,53 @@ impl<D: Decoder> Decodable<D> for EncodedMetadata {
23652383
None
23662384
};
23672385

2368-
Self { full_metadata, stub_metadata: stub, _temp_dir: None }
2386+
Self { full_metadata, stub_metadata: stub, path: None, _temp_dir: None }
23692387
}
23702388
}
23712389

2390+
#[instrument(level = "trace", skip(tcx))]
23722391
pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
2373-
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
2374-
23752392
// Since encoding metadata is not in a query, and nothing is cached,
23762393
// there's no need to do dep-graph tracking for any of it.
23772394
tcx.dep_graph.assert_ignored();
23782395

2396+
// Generate the metadata stub manually, as that is a small file compared to full metadata.
2397+
if let Some(ref_path) = ref_path {
2398+
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub");
2399+
2400+
with_encode_metadata_header(tcx, ref_path, |ecx| {
2401+
let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
2402+
name: tcx.crate_name(LOCAL_CRATE),
2403+
triple: tcx.sess.opts.target_triple.clone(),
2404+
hash: tcx.crate_hash(LOCAL_CRATE),
2405+
is_proc_macro_crate: false,
2406+
is_stub: true,
2407+
});
2408+
header.position.get()
2409+
})
2410+
}
2411+
2412+
let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
2413+
2414+
let dep_node = tcx.metadata_dep_node();
2415+
2416+
// If the metadata dep-node is green, try to reuse the saved work product.
2417+
if tcx.dep_graph.is_fully_enabled()
2418+
&& let work_product_id = WorkProductId::from_cgu_name("metadata")
2419+
&& let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id)
2420+
&& tcx.try_mark_green(&dep_node)
2421+
{
2422+
let saved_path = &work_product.saved_files["rmeta"];
2423+
let incr_comp_session_dir = tcx.sess.incr_comp_session_dir_opt().unwrap();
2424+
let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, saved_path);
2425+
debug!("copying preexisting metadata from {source_file:?} to {path:?}");
2426+
match rustc_fs_util::link_or_copy(&source_file, path) {
2427+
Ok(_) => {}
2428+
Err(err) => tcx.dcx().emit_fatal(FailCreateFileEncoder { err }),
2429+
};
2430+
return;
2431+
};
2432+
23792433
if tcx.sess.threads() != 1 {
23802434
// Prefetch some queries used by metadata encoding.
23812435
// This is not necessary for correctness, but is only done for performance reasons.
@@ -2389,35 +2443,32 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {
23892443
);
23902444
}
23912445

2392-
with_encode_metadata_header(tcx, path, |ecx| {
2393-
// Encode all the entries and extra information in the crate,
2394-
// culminating in the `CrateRoot` which points to all of it.
2395-
let root = ecx.encode_crate_root();
2396-
2397-
// Flush buffer to ensure backing file has the correct size.
2398-
ecx.opaque.flush();
2399-
// Record metadata size for self-profiling
2400-
tcx.prof.artifact_size(
2401-
"crate_metadata",
2402-
"crate_metadata",
2403-
ecx.opaque.file().metadata().unwrap().len(),
2404-
);
2405-
2406-
root.position.get()
2407-
});
2446+
// Perform metadata encoding inside a task, so the dep-graph can check if any encoded
2447+
// information changes, and maybe reuse the work product.
2448+
tcx.dep_graph.with_task(
2449+
dep_node,
2450+
tcx,
2451+
path,
2452+
|tcx, path| {
2453+
with_encode_metadata_header(tcx, path, |ecx| {
2454+
// Encode all the entries and extra information in the crate,
2455+
// culminating in the `CrateRoot` which points to all of it.
2456+
let root = ecx.encode_crate_root();
2457+
2458+
// Flush buffer to ensure backing file has the correct size.
2459+
ecx.opaque.flush();
2460+
// Record metadata size for self-profiling
2461+
tcx.prof.artifact_size(
2462+
"crate_metadata",
2463+
"crate_metadata",
2464+
ecx.opaque.file().metadata().unwrap().len(),
2465+
);
24082466

2409-
if let Some(ref_path) = ref_path {
2410-
with_encode_metadata_header(tcx, ref_path, |ecx| {
2411-
let header: LazyValue<CrateHeader> = ecx.lazy(CrateHeader {
2412-
name: tcx.crate_name(LOCAL_CRATE),
2413-
triple: tcx.sess.opts.target_triple.clone(),
2414-
hash: tcx.crate_hash(LOCAL_CRATE),
2415-
is_proc_macro_crate: false,
2416-
is_stub: true,
2417-
});
2418-
header.position.get()
2419-
});
2420-
}
2467+
root.position.get()
2468+
})
2469+
},
2470+
None,
2471+
);
24212472
}
24222473

24232474
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/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3364,6 +3364,10 @@ impl<'tcx> TyCtxt<'tcx> {
33643364
self.resolver_for_lowering_raw(()).0
33653365
}
33663366

3367+
pub fn metadata_dep_node(self) -> crate::dep_graph::DepNode {
3368+
crate::dep_graph::make_metadata(self)
3369+
}
3370+
33673371
/// Given an `impl_id`, return the trait it implements.
33683372
/// Return `None` if this is an inherent impl.
33693373
pub fn impl_trait_ref(

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)*]),

0 commit comments

Comments
 (0)