| use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; |
| use rustc_data_structures::unord::UnordMap; |
| use rustc_middle::bug; |
| #[expect(unused_imports, reason = "used by doc comments")] |
| use rustc_middle::dep_graph::DepKindVTable; |
| use rustc_middle::dep_graph::{ |
| DepGraphData, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex, |
| }; |
| use rustc_middle::query::erase::{Erasable, Erased}; |
| use rustc_middle::query::on_disk_cache::{CacheDecoder, CacheEncoder}; |
| use rustc_middle::query::{QueryCache, QueryState, QueryVTable, erase}; |
| use rustc_middle::ty::TyCtxt; |
| use rustc_middle::verify_ich::incremental_verify_ich; |
| use rustc_serialize::{Decodable, Encodable}; |
| |
| use crate::query_vtables::for_each_query_vtable; |
| |
| fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool { |
| state.active.lock_shards().all(|shard| shard.is_empty()) |
| } |
| |
| pub(crate) fn encode_query_values<'tcx>(tcx: TyCtxt<'tcx>, encoder: &mut CacheEncoder<'_, 'tcx>) { |
| for_each_query_vtable!(CACHE_ON_DISK, tcx, |query| { |
| encode_query_values_inner(tcx, query, encoder) |
| }); |
| } |
| |
| fn encode_query_values_inner<'a, 'tcx, C, V>( |
| tcx: TyCtxt<'tcx>, |
| query: &'tcx QueryVTable<'tcx, C>, |
| encoder: &mut CacheEncoder<'a, 'tcx>, |
| ) where |
| C: QueryCache<Value = Erased<V>>, |
| V: Erasable + Encodable<CacheEncoder<'a, 'tcx>>, |
| { |
| let _timer = tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name); |
| |
| assert!(all_inactive(&query.state)); |
| query.cache.for_each(&mut |key, value, dep_node| { |
| if query.will_cache_on_disk_for_key(*key) { |
| encoder.encode_query_value::<V>(dep_node, &erase::restore_val::<V>(*value)); |
| } |
| }); |
| } |
| |
| pub(crate) fn verify_query_key_hashes<'tcx>(tcx: TyCtxt<'tcx>) { |
| if tcx.sess.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) { |
| tcx.sess.time("verify_query_key_hashes", || { |
| for_each_query_vtable!(ALL, tcx, |query| { |
| verify_query_key_hashes_inner(query, tcx); |
| }); |
| }); |
| } |
| } |
| |
| fn verify_query_key_hashes_inner<'tcx, C: QueryCache>( |
| query: &'tcx QueryVTable<'tcx, C>, |
| tcx: TyCtxt<'tcx>, |
| ) { |
| let _timer = tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name); |
| |
| let cache = &query.cache; |
| let mut map = UnordMap::with_capacity(cache.len()); |
| cache.for_each(&mut |key, _, _| { |
| let node = DepNode::construct(tcx, query.dep_kind, key); |
| if let Some(other_key) = map.insert(node, *key) { |
| bug!( |
| "query key:\n\ |
| `{:?}`\n\ |
| and key:\n\ |
| `{:?}`\n\ |
| mapped to the same dep node:\n\ |
| {:?}", |
| key, |
| other_key, |
| node |
| ); |
| } |
| }); |
| } |
| |
| /// Whether a value loaded from the on-disk cache should have its fingerprint |
| /// verified with `incremental_verify_ich`. If `-Zincremental-verify-ich` is |
| /// specified, re-hash results from the cache and make sure that they have the |
| /// expected fingerprint. |
| /// |
| /// If not, we still verify a subset: re-hashing is too expensive to do for |
| /// every value. The subset rotates with the session count, covering the whole |
| /// cache every 32 sessions, and is deterministic so that a verification |
| /// failure reproduces on retry. |
| /// |
| /// `to_smaller_hash` mixes both fingerprint halves because neither half is |
| /// evenly distributed on its own (`DefPathHash` keys share the |
| /// `StableCrateId`, `HirId` keys contain a sequential id). |
| pub(crate) fn should_verify_loaded_value( |
| tcx: TyCtxt<'_>, |
| dep_graph_data: &DepGraphData, |
| key_fingerprint: PackedFingerprint, |
| ) -> bool { |
| let hash = Fingerprint::from(key_fingerprint).to_smaller_hash().as_u64(); |
| hash % 32 == dep_graph_data.session_count() % 32 |
| || tcx.sess.opts.unstable_opts.incremental_verify_ich |
| } |
| |
| /// Inner implementation of [`DepKindVTable::promote_from_disk_fn`] for queries. |
| pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( |
| tcx: TyCtxt<'tcx>, |
| query: &'tcx QueryVTable<'tcx, C>, |
| dep_node: DepNode, |
| prev_index: SerializedDepNodeIndex, |
| dep_node_index: DepNodeIndex, |
| ) { |
| debug_assert!(tcx.dep_graph.is_green(&dep_node)); |
| |
| let key = C::Key::try_recover_key(tcx, &dep_node).unwrap_or_else(|| { |
| panic!( |
| "Failed to recover key for {dep_node:?} with key fingerprint {}", |
| dep_node.key_fingerprint |
| ) |
| }); |
| |
| // If the recovered key isn't eligible for cache-on-disk, then there's no |
| // value on disk to promote. |
| if !query.will_cache_on_disk_for_key(key) { |
| return; |
| } |
| |
| // If the value is already in memory, then promotion isn't needed. |
| if query.cache.lookup(&key).is_some() { |
| return; |
| } |
| |
| // Load the disk-cached value into memory. |
| let dep_graph_data = |
| tcx.dep_graph.data().expect("should always be present in incremental mode"); |
| |
| let prof_timer = tcx.prof.incr_cache_loading(); |
| let value = (query.try_load_from_disk_fn)(tcx, prev_index); |
| prof_timer.finish_with_query_invocation_id(dep_node_index.into()); |
| |
| let Some(value) = value else { |
| // The key is cache-on-disk and its node is green, so a value must be on disk. |
| bug!("failed to load disk-cached value for green node {dep_node:?}"); |
| }; |
| |
| // Verify the fingerprints of the same subset of loaded values as |
| // `load_from_disk_or_invoke_provider_green` does. |
| if should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint) { |
| incremental_verify_ich( |
| tcx, |
| dep_graph_data, |
| &value, |
| prev_index, |
| query.hash_value_fn, |
| query.format_value, |
| ); |
| } |
| |
| query.cache.complete(key, value, dep_node_index); |
| } |
| |
| pub(crate) fn try_load_from_disk<'tcx, V>( |
| tcx: TyCtxt<'tcx>, |
| prev_index: SerializedDepNodeIndex, |
| ) -> Option<V> |
| where |
| V: for<'a> Decodable<CacheDecoder<'a, 'tcx>>, |
| { |
| let on_disk_cache = tcx.query_system.on_disk_cache.as_ref()?; |
| |
| // The call to `with_query_deserialization` enforces that no new `DepNodes` |
| // are created during deserialization. See the docs of that method for more |
| // details. |
| tcx.dep_graph.with_query_deserialization(|| on_disk_cache.try_load_query_value(tcx, prev_index)) |
| } |