diff --git a/Cargo.lock b/Cargo.lock index 5e57835c9cfef..44c2db1e941a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4415,6 +4415,7 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_fs_util", + "rustc_hashes", "rustc_hir", "rustc_hir_pretty", "rustc_index", diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 50f7a15b19adf..426a4e0109153 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -8,6 +8,7 @@ use std::fmt::{self, Write}; use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::StableHasher; use rustc_hashes::Hash64; use rustc_index::IndexVec; @@ -46,13 +47,55 @@ impl LocalDefIdMap { } } +#[derive(Debug, Default)] +pub struct DefPathToIndexMap { + /// Stores mapping from local hash to def indices which are allocated when the order + /// of their allocation is deterministic (see where `commit_end_of_determinism` is called, + /// at the moment of writing it is after prefetch of `hir_crate_items` in `run_required_analysis`). + pub before_parallel_alloc: DefPathHashMap, + + /// Stores the same mapping as previous map but is used after the allocation order of def indices + /// is not deterministic, so when serializing metadata we have a ready to use sorted by stable local + /// hash (which does not change between compiler invocations) mapping. We use it for relatively small number + /// of definitions, so the majority of them would be stored in `DefPathHashMap`, which makes insertion + /// and serialization costs of `SortedMap` acceptable. + pub after_parallel_alloc: Option>, +} + +impl DefPathToIndexMap { + #[inline] + pub fn get(&self, hash: Hash64) -> Option { + self.before_parallel_alloc + .get(&hash) + .or_else(|| self.after_parallel_alloc.as_ref().and_then(|map| map.get(&hash).copied())) + } + + /// This insert function does not behave like regular `insert` of a `HashMap`, + /// as the return value is used only for printing information about existing + /// def index for local hash before panicking. So we can do not actually insert + /// def index into `det_part` when we are in non-deterministic mode. + #[inline] + pub fn insert(&mut self, hash: Hash64, index: DefIndex) -> Option { + match self.after_parallel_alloc.as_mut() { + None => self.before_parallel_alloc.insert(&hash, &index), + Some(map) => { + if let Some(existing) = self.before_parallel_alloc.get(&hash) { + return Some(existing); + } + + map.insert(hash, index) + } + } + } +} + #[derive(Debug)] pub struct Definitions { stable_crate_id: StableCrateId, def_id_to_key: IndexVec, // We do only store the local hash, as all the definitions are from the current crate. def_path_hashes: IndexVec, - def_path_hash_to_index: DefPathHashMap, + def_path_hash_to_index: DefPathToIndexMap, } /// A unique identifier that we can use to lookup a definition @@ -249,6 +292,15 @@ pub enum DefPathData { } impl Definitions { + /// This function indicates that the order of def id allocations + /// may be non-deterministic after it was called. + pub fn commit_end_of_determinism(&mut self) { + assert!( + self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()).is_none(), + "this function should be called only once" + ) + } + #[inline(always)] pub fn def_key(&self, id: LocalDefId) -> DefKey { self.def_id_to_key[id] @@ -323,7 +375,7 @@ impl Definitions { // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = - self.def_path_hash_to_index.insert(&local_hash, &def_id.local_def_index) + self.def_path_hash_to_index.insert(local_hash, def_id.local_def_index) { let def_path1 = self.def_path(LocalDefId { local_def_index: existing }); let def_path2 = self.def_path(def_id); @@ -411,11 +463,11 @@ impl Definitions { pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> Option { debug_assert!(hash.stable_crate_id() == self.stable_crate_id); self.def_path_hash_to_index - .get(&hash.local_hash()) + .get(hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) } - pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { + pub fn def_path_hash_to_def_index_map(&self) -> &DefPathToIndexMap { &self.def_path_hash_to_index } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2316e80b6e945..8a3a149066e6a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1099,6 +1099,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // to use `hir_crate_items`. tcx.ensure_done().hir_crate_items(()); + tcx.untracked().definitions.write().commit_end_of_determinism(); + rustc_passes::delegation::check_glob_and_list_delegations_target_expr(tcx); let sess = tcx.sess; diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index a2ef8454ed6a6..cbcbe4a2ac551 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fs_util = { path = "../rustc_fs_util" } +rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 949d7630f6736..61ec7ad4cf7b3 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -1,5 +1,8 @@ use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_hir::def_path_hash_map::{Config as HashMapConfig, DefPathHashMap}; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_hashes::Hash64; +use rustc_hir::def_path_hash_map::Config as HashMapConfig; +use rustc_hir::definitions::DefPathToIndexMap; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::def_id::{DefIndex, DefPathHash}; @@ -7,8 +10,8 @@ use crate::rmeta::EncodeContext; use crate::rmeta::decoder::BlobDecodeContext; pub(crate) enum DefPathHashMapRef<'tcx> { - OwnedFromMetadata(odht::HashTable), - BorrowedFromTcx(&'tcx DefPathHashMap), + OwnedFromMetadata(odht::HashTable, SortedMap), + BorrowedFromTcx(&'tcx DefPathToIndexMap), } impl DefPathHashMapRef<'_> { @@ -17,8 +20,11 @@ impl DefPathHashMapRef<'_> { &self, def_path_hash: &DefPathHash, ) -> Option { - match *self { - DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()), + match self { + DefPathHashMapRef::OwnedFromMetadata(det_map, non_det_map) => { + let hash = &def_path_hash.local_hash(); + det_map.get(hash).or_else(|| non_det_map.get(hash).copied()) + } DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") } @@ -29,12 +35,18 @@ impl DefPathHashMapRef<'_> { impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { match *self { - DefPathHashMapRef::BorrowedFromTcx(def_path_hash_map) => { - let bytes = def_path_hash_map.raw_bytes(); + DefPathHashMapRef::BorrowedFromTcx(map) => { + let bytes = map.before_parallel_alloc.raw_bytes(); e.emit_usize(bytes.len()); e.emit_raw_bytes(bytes); + + map.after_parallel_alloc + .as_ref() + .expect("must be set before metadata encoding") + .range(..) + .encode(e); } - DefPathHashMapRef::OwnedFromMetadata(_) => { + DefPathHashMapRef::OwnedFromMetadata(..) => { panic!("DefPathHashMap::OwnedFromMetadata variant only exists for deserialization") } } @@ -55,6 +67,10 @@ impl<'a> Decodable> for DefPathHashMapRef<'static> { let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { panic!("decode error: {e}"); }); - DefPathHashMapRef::OwnedFromMetadata(inner) + + let elements = Vec::<(Hash64, DefIndex)>::decode(d); + let non_det_map = SortedMap::from_presorted_elements(elements); + + DefPathHashMapRef::OwnedFromMetadata(inner, non_det_map) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9d0b334b63fbd..d5cfaf570bafe 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1377,9 +1377,7 @@ impl<'tcx> TyCtxt<'tcx> { self.untracked.definitions.freeze() } - pub fn def_path_hash_to_def_index_map( - self, - ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { + pub fn def_path_hash_to_def_index_map(self) -> &'tcx rustc_hir::definitions::DefPathToIndexMap { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure_ok().hir_crate_items(());