2023-10-26 08:39:49 +11:00
|
|
|
//! Code to load the dep-graph from files.
|
2016-03-28 17:42:39 -04:00
|
|
|
|
2022-09-26 15:42:12 +01:00
|
|
|
use std::path::{Path, PathBuf};
|
2023-09-24 01:34:45 +02:00
|
|
|
use std::sync::Arc;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2021-03-16 21:39:03 +01:00
|
|
|
use rustc_data_structures::memmap::Mmap;
|
2023-05-07 19:52:19 -04:00
|
|
|
use rustc_data_structures::unord::UnordMap;
|
2025-02-21 21:36:58 -05:00
|
|
|
use rustc_hashes::Hash64;
|
2023-09-20 15:38:18 +02:00
|
|
|
use rustc_middle::dep_graph::{DepGraph, DepsType, SerializedDepGraph, WorkProductMap};
|
2023-03-25 09:46:19 +01:00
|
|
|
use rustc_middle::query::on_disk_cache::OnDiskCache;
|
2021-03-02 22:38:49 +01:00
|
|
|
use rustc_serialize::Decodable;
|
2021-10-31 17:05:48 -05:00
|
|
|
use rustc_serialize::opaque::MemDecoder;
|
2024-02-28 22:42:31 +01:00
|
|
|
use rustc_session::Session;
|
2021-10-31 17:05:48 -05:00
|
|
|
use rustc_session::config::IncrementalStateAssertion;
|
2024-07-10 17:38:52 +02:00
|
|
|
use rustc_span::Symbol;
|
2024-05-22 13:58:19 +10:00
|
|
|
use tracing::{debug, warn};
|
2016-03-28 17:42:39 -04:00
|
|
|
|
|
|
|
|
use super::data::*;
|
2016-08-11 19:02:39 -04:00
|
|
|
use super::fs::*;
|
2023-09-20 15:38:18 +02:00
|
|
|
use super::save::build_dep_graph;
|
2017-01-16 17:54:20 -05:00
|
|
|
use super::{file_format, work_product};
|
2024-02-28 22:42:31 +01:00
|
|
|
use crate::errors;
|
2016-03-28 17:42:39 -04:00
|
|
|
|
2021-10-31 17:05:48 -05:00
|
|
|
#[derive(Debug)]
|
2021-10-29 12:14:17 -05:00
|
|
|
/// Represents the result of an attempt to load incremental compilation data.
|
2017-12-07 16:05:29 +01:00
|
|
|
pub enum LoadResult<T> {
|
2021-10-29 12:14:17 -05:00
|
|
|
/// Loading was successful.
|
|
|
|
|
Ok {
|
|
|
|
|
#[allow(missing_docs)]
|
|
|
|
|
data: T,
|
|
|
|
|
},
|
|
|
|
|
/// The file either didn't exist or was produced by an incompatible compiler version.
|
2017-12-07 16:05:29 +01:00
|
|
|
DataOutOfDate,
|
2022-09-26 15:42:12 +01:00
|
|
|
/// Loading the dep graph failed.
|
|
|
|
|
LoadDepGraph(PathBuf, std::io::Error),
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-24 19:24:58 +02:00
|
|
|
impl<T: Default> LoadResult<T> {
|
2021-10-29 12:14:17 -05:00
|
|
|
/// Accesses the data returned in [`LoadResult::Ok`].
|
2021-05-24 19:24:58 +02:00
|
|
|
pub fn open(self, sess: &Session) -> T {
|
2021-10-31 17:05:48 -05:00
|
|
|
// Check for errors when using `-Zassert-incremental-state`
|
|
|
|
|
match (sess.opts.assert_incr_state, &self) {
|
|
|
|
|
(Some(IncrementalStateAssertion::NotLoaded), LoadResult::Ok { .. }) => {
|
2023-12-18 22:21:37 +11:00
|
|
|
sess.dcx().emit_fatal(errors::AssertNotLoaded);
|
2021-10-31 17:05:48 -05:00
|
|
|
}
|
|
|
|
|
(
|
|
|
|
|
Some(IncrementalStateAssertion::Loaded),
|
2023-11-04 20:16:03 +00:00
|
|
|
LoadResult::LoadDepGraph(..) | LoadResult::DataOutOfDate,
|
2021-10-31 17:05:48 -05:00
|
|
|
) => {
|
2023-12-18 22:21:37 +11:00
|
|
|
sess.dcx().emit_fatal(errors::AssertLoaded);
|
2021-10-31 17:05:48 -05:00
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-07 16:05:29 +01:00
|
|
|
match self {
|
2022-09-26 15:42:12 +01:00
|
|
|
LoadResult::LoadDepGraph(path, err) => {
|
2024-01-04 11:28:14 +11:00
|
|
|
sess.dcx().emit_warn(errors::LoadDepGraph { path, err });
|
2022-09-26 15:42:12 +01:00
|
|
|
Default::default()
|
|
|
|
|
}
|
2017-12-07 16:05:29 +01:00
|
|
|
LoadResult::DataOutOfDate => {
|
|
|
|
|
if let Err(err) = delete_all_session_dir_contents(sess) {
|
2023-12-18 22:21:37 +11:00
|
|
|
sess.dcx()
|
|
|
|
|
.emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err });
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
2018-10-16 16:57:53 +02:00
|
|
|
Default::default()
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
|
|
|
|
LoadResult::Ok { data } => data,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 04:12:38 -05:00
|
|
|
fn load_data(path: &Path, sess: &Session) -> LoadResult<(Mmap, usize)> {
|
2023-09-20 15:38:18 +02:00
|
|
|
match file_format::read_file(
|
2023-05-08 04:12:38 -05:00
|
|
|
path,
|
|
|
|
|
sess.opts.unstable_opts.incremental_info,
|
|
|
|
|
sess.is_nightly_build(),
|
|
|
|
|
sess.cfg_version,
|
2023-09-20 15:38:18 +02:00
|
|
|
) {
|
2017-12-07 16:05:29 +01:00
|
|
|
Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
|
2016-09-26 16:05:01 -04:00
|
|
|
Ok(None) => {
|
|
|
|
|
// The file either didn't exist or was produced by an incompatible
|
|
|
|
|
// compiler version. Neither is an error.
|
2017-12-07 16:05:29 +01:00
|
|
|
LoadResult::DataOutOfDate
|
2016-07-21 12:44:59 -04:00
|
|
|
}
|
2022-09-26 15:42:12 +01:00
|
|
|
Err(err) => LoadResult::LoadDepGraph(path.to_path_buf(), err),
|
2016-03-28 17:42:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-07 22:30:44 -04:00
|
|
|
fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) {
|
2016-07-21 12:44:59 -04:00
|
|
|
debug!("delete_dirty_work_product({:?})", swp);
|
2018-05-07 22:30:44 -04:00
|
|
|
work_product::delete_workproduct_files(sess, &swp.work_product);
|
2016-07-21 12:44:59 -04:00
|
|
|
}
|
2016-08-30 16:49:54 -04:00
|
|
|
|
2023-09-24 01:34:45 +02:00
|
|
|
fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkProductMap)> {
|
2019-10-08 14:05:41 +02:00
|
|
|
let prof = sess.prof.clone();
|
2017-12-03 14:21:23 +01:00
|
|
|
|
2017-12-07 16:05:29 +01:00
|
|
|
if sess.opts.incremental.is_none() {
|
|
|
|
|
// No incremental compilation.
|
2023-09-20 15:38:18 +02:00
|
|
|
return LoadResult::Ok { data: Default::default() };
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
2017-11-13 15:13:44 +01:00
|
|
|
|
2020-01-09 03:48:00 +01:00
|
|
|
let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph");
|
|
|
|
|
|
2017-12-07 16:05:29 +01:00
|
|
|
// Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`.
|
|
|
|
|
// Fortunately, we just checked that this isn't the case.
|
2023-11-21 20:07:32 +01:00
|
|
|
let path = dep_graph_path(sess);
|
2021-04-15 19:25:01 -04:00
|
|
|
let expected_hash = sess.opts.dep_tracking_hash(false);
|
2017-12-07 16:05:29 +01:00
|
|
|
|
2023-06-05 00:16:20 -04:00
|
|
|
let mut prev_work_products = UnordMap::default();
|
2018-05-07 22:30:44 -04:00
|
|
|
|
|
|
|
|
// If we are only building with -Zquery-dep-graph but without an actual
|
2018-05-08 09:13:18 -04:00
|
|
|
// incr. comp. session directory, we skip this. Otherwise we'd fail
|
2018-05-07 22:30:44 -04:00
|
|
|
// when trying to load work products.
|
|
|
|
|
if sess.incr_comp_session_dir_opt().is_some() {
|
|
|
|
|
let work_products_path = work_products_path(sess);
|
2023-05-08 04:12:38 -05:00
|
|
|
let load_result = load_data(&work_products_path, sess);
|
2018-05-07 22:30:44 -04:00
|
|
|
|
|
|
|
|
if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
|
|
|
|
|
// Decode the list of work_products
|
2024-05-24 15:08:18 +10:00
|
|
|
let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos)
|
2024-05-03 21:17:57 -04:00
|
|
|
else {
|
|
|
|
|
sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
|
|
|
|
|
return LoadResult::DataOutOfDate;
|
|
|
|
|
};
|
2018-05-07 22:30:44 -04:00
|
|
|
let work_products: Vec<SerializedWorkProduct> =
|
Make `Decodable` and `Decoder` infallible.
`Decoder` has two impls:
- opaque: this impl is already partly infallible, i.e. in some places it
currently panics on failure (e.g. if the input is too short, or on a
bad `Result` discriminant), and in some places it returns an error
(e.g. on a bad `Option` discriminant). The number of places where
either happens is surprisingly small, just because the binary
representation has very little redundancy and a lot of input reading
can occur even on malformed data.
- json: this impl is fully fallible, but it's only used (a) for the
`.rlink` file production, and there's a `FIXME` comment suggesting it
should change to a binary format, and (b) in a few tests in
non-fundamental ways. Indeed #85993 is open to remove it entirely.
And the top-level places in the compiler that call into decoding just
abort on error anyway. So the fallibility is providing little value, and
getting rid of it leads to some non-trivial performance improvements.
Much of this commit is pretty boring and mechanical. Some notes about
a few interesting parts:
- The commit removes `Decoder::{Error,error}`.
- `InternIteratorElement::intern_with`: the impl for `T` now has the same
optimization for small counts that the impl for `Result<T, E>` has,
because it's now much hotter.
- Decodable impls for SmallVec, LinkedList, VecDeque now all use
`collect`, which is nice; the one for `Vec` uses unsafe code, because
that gave better perf on some benchmarks.
2022-01-18 13:22:50 +11:00
|
|
|
Decodable::decode(&mut work_product_decoder);
|
2018-05-07 22:30:44 -04:00
|
|
|
|
|
|
|
|
for swp in work_products {
|
2023-05-07 21:08:47 -04:00
|
|
|
let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| {
|
2022-07-04 14:38:42 +01:00
|
|
|
let exists = in_incr_comp_dir_sess(sess, path).exists();
|
2022-07-06 07:44:47 -05:00
|
|
|
if !exists && sess.opts.unstable_opts.incremental_info {
|
2022-07-04 14:38:42 +01:00
|
|
|
eprintln!("incremental: could not find file for work product: {path}",);
|
2018-05-07 22:30:44 -04:00
|
|
|
}
|
2022-07-04 14:38:42 +01:00
|
|
|
exists
|
|
|
|
|
});
|
2018-05-07 22:30:44 -04:00
|
|
|
|
|
|
|
|
if all_files_exist {
|
|
|
|
|
debug!("reconcile_work_products: all files for {:?} exist", swp);
|
|
|
|
|
prev_work_products.insert(swp.id, swp.work_product);
|
|
|
|
|
} else {
|
|
|
|
|
debug!("reconcile_work_products: some file for {:?} does not exist", swp);
|
|
|
|
|
delete_dirty_work_product(sess, swp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 15:38:18 +02:00
|
|
|
let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
|
2020-01-01 02:24:05 +01:00
|
|
|
|
2023-09-20 15:38:18 +02:00
|
|
|
match load_data(&path, sess) {
|
|
|
|
|
LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
|
|
|
|
|
LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
|
|
|
|
|
LoadResult::Ok { data: (bytes, start_pos) } => {
|
2024-05-21 20:12:07 -04:00
|
|
|
let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
|
2024-05-03 21:17:57 -04:00
|
|
|
sess.dcx().emit_warn(errors::CorruptFile { path: &path });
|
|
|
|
|
return LoadResult::DataOutOfDate;
|
|
|
|
|
};
|
2025-02-21 21:36:58 -05:00
|
|
|
let prev_commandline_args_hash = Hash64::decode(&mut decoder);
|
2020-01-01 02:24:05 +01:00
|
|
|
|
2023-09-20 15:38:18 +02:00
|
|
|
if prev_commandline_args_hash != expected_hash {
|
|
|
|
|
if sess.opts.unstable_opts.incremental_info {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"[incremental] completely ignoring cache because of \
|
2017-12-07 16:05:29 +01:00
|
|
|
differing commandline arguments"
|
2023-09-20 15:38:18 +02:00
|
|
|
);
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
2023-09-20 15:38:18 +02:00
|
|
|
// We can't reuse the cache, purge it.
|
|
|
|
|
debug!("load_dep_graph_new: differing commandline arg hashes");
|
2020-01-01 02:24:05 +01:00
|
|
|
|
2023-09-20 15:38:18 +02:00
|
|
|
// No need to do any further work
|
|
|
|
|
return LoadResult::DataOutOfDate;
|
2017-12-07 16:05:29 +01:00
|
|
|
}
|
2023-09-20 15:38:18 +02:00
|
|
|
|
|
|
|
|
let dep_graph = SerializedDepGraph::decode::<DepsType>(&mut decoder);
|
|
|
|
|
|
|
|
|
|
LoadResult::Ok { data: (dep_graph, prev_work_products) }
|
2020-01-01 02:24:05 +01:00
|
|
|
}
|
2023-09-20 15:38:18 +02:00
|
|
|
}
|
2017-09-22 13:00:42 +02:00
|
|
|
}
|
2017-10-19 14:32:39 +02:00
|
|
|
|
2020-11-19 15:49:45 -05:00
|
|
|
/// Attempts to load the query result cache from disk
|
|
|
|
|
///
|
|
|
|
|
/// If we are not in incremental compilation mode, returns `None`.
|
|
|
|
|
/// Otherwise, tries to load the query result cache from disk,
|
|
|
|
|
/// creating an empty cache if it could not be loaded.
|
2024-11-03 20:50:46 +00:00
|
|
|
pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
|
2020-03-23 11:41:35 +11:00
|
|
|
if sess.opts.incremental.is_none() {
|
2020-11-19 15:49:45 -05:00
|
|
|
return None;
|
2017-10-19 14:32:39 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-08 14:05:41 +02:00
|
|
|
let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
|
|
|
|
|
|
2024-05-03 21:17:57 -04:00
|
|
|
let path = query_cache_path(sess);
|
|
|
|
|
match load_data(&path, sess) {
|
2023-03-25 09:46:19 +01:00
|
|
|
LoadResult::Ok { data: (bytes, start_pos) } => {
|
2024-05-21 20:12:07 -04:00
|
|
|
let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
|
2024-05-03 21:17:57 -04:00
|
|
|
sess.dcx().emit_warn(errors::CorruptFile { path: &path });
|
2024-11-03 20:50:46 +00:00
|
|
|
OnDiskCache::new_empty()
|
2024-05-03 21:17:57 -04:00
|
|
|
});
|
|
|
|
|
Some(cache)
|
2023-03-25 09:46:19 +01:00
|
|
|
}
|
2024-11-03 20:50:46 +00:00
|
|
|
_ => Some(OnDiskCache::new_empty()),
|
2017-10-19 14:32:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-20 15:38:18 +02:00
|
|
|
|
|
|
|
|
/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
|
|
|
|
|
/// new graph to an incremental session directory.
|
2024-07-10 17:38:52 +02:00
|
|
|
pub fn setup_dep_graph(sess: &Session, crate_name: Symbol) -> DepGraph {
|
2023-09-20 15:38:18 +02:00
|
|
|
// `load_dep_graph` can only be called after `prepare_session_directory`.
|
2024-07-10 17:38:52 +02:00
|
|
|
prepare_session_directory(sess, crate_name);
|
2023-09-20 15:38:18 +02:00
|
|
|
|
|
|
|
|
let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
|
|
|
|
|
|
|
|
|
|
if sess.opts.incremental.is_some() {
|
|
|
|
|
sess.time("incr_comp_garbage_collect_session_directories", || {
|
|
|
|
|
if let Err(e) = garbage_collect_session_directories(sess) {
|
|
|
|
|
warn!(
|
|
|
|
|
"Error while trying to garbage collect incremental \
|
|
|
|
|
compilation cache directory: {}",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-03 16:45:22 +00:00
|
|
|
res.and_then(|result| {
|
|
|
|
|
let (prev_graph, prev_work_products) = result.open(sess);
|
|
|
|
|
build_dep_graph(sess, prev_graph, prev_work_products)
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(DepGraph::new_disabled)
|
2023-09-20 15:38:18 +02:00
|
|
|
}
|