From 469ca379d6dc0ade5ee08f14892c1549bb383e77 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 1 Sep 2020 18:27:02 -0700 Subject: [PATCH] Avoid rehashing Fingerprint as a map key This introduces a no-op `Unhasher` for map keys that are already hash- like, for example `Fingerprint` and its wrapper `DefPathHash`. For these we can directly produce the `u64` hash for maps. The first use of this is `def_path_hash_to_def_id: Option>`. --- .../rustc_data_structures/src/fingerprint.rs | 30 ++++++++++++++++++- compiler/rustc_data_structures/src/lib.rs | 1 + compiler/rustc_data_structures/src/unhash.rs | 29 ++++++++++++++++++ compiler/rustc_middle/src/ty/context.rs | 5 ++-- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 compiler/rustc_data_structures/src/unhash.rs diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index f8d631ce01e..aba0bbbac80 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -3,9 +3,10 @@ use rustc_serialize::{ opaque::{self, EncodeResult}, Decodable, Encodable, }; +use std::hash::{Hash, Hasher}; use std::mem; -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] pub struct Fingerprint(u64, u64); impl Fingerprint { @@ -76,6 +77,33 @@ impl ::std::fmt::Display for Fingerprint { } } +impl Hash for Fingerprint { + #[inline] + fn hash(&self, state: &mut H) { + state.write_fingerprint(self); + } +} + +trait FingerprintHasher { + fn write_fingerprint(&mut self, fingerprint: &Fingerprint); +} + +impl FingerprintHasher for H { + #[inline] + default fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + self.write_u64(fingerprint.0); + self.write_u64(fingerprint.1); + } +} + +impl FingerprintHasher for crate::unhash::Unhasher { + #[inline] + fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { + // `Unhasher` only wants a single `u64` + self.write_u64(fingerprint.0); + } +} + impl stable_hasher::StableHasherResult for Fingerprint { #[inline] fn finish(hasher: stable_hasher::StableHasher) -> Self { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 78b7e08ceed..de4e7a13424 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -103,6 +103,7 @@ pub use atomic_ref::AtomicRef; pub mod frozen; pub mod tagged_ptr; pub mod temp_dir; +pub mod unhash; pub struct OnDrop(pub F); diff --git a/compiler/rustc_data_structures/src/unhash.rs b/compiler/rustc_data_structures/src/unhash.rs new file mode 100644 index 00000000000..48e21a9dab1 --- /dev/null +++ b/compiler/rustc_data_structures/src/unhash.rs @@ -0,0 +1,29 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasherDefault, Hasher}; + +pub type UnhashMap = HashMap>; +pub type UnhashSet = HashSet>; + +/// This no-op hasher expects only a single `write_u64` call. It's intended for +/// map keys that already have hash-like quality, like `Fingerprint`. +#[derive(Default)] +pub struct Unhasher { + value: u64, +} + +impl Hasher for Unhasher { + #[inline] + fn finish(&self) -> u64 { + self.value + } + + fn write(&mut self, _bytes: &[u8]) { + unimplemented!("use write_u64"); + } + + #[inline] + fn write_u64(&mut self, value: u64) { + debug_assert_eq!(0, self.value, "Unhasher doesn't mix values!"); + self.value = value; + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 18ae744cb1e..5819c774bc2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -34,6 +34,7 @@ use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -935,7 +936,7 @@ pub struct GlobalCtxt<'tcx> { /// A map from `DefPathHash` -> `DefId`. Includes `DefId`s from the local crate /// as well as all upstream crates. Only populated in incremental mode. - pub def_path_hash_to_def_id: Option>, + pub def_path_hash_to_def_id: Option>, pub queries: query::Queries<'tcx>, @@ -1104,7 +1105,7 @@ impl<'tcx> TyCtxt<'tcx> { let def_path_hash_to_def_id = if s.opts.build_dep_graph() { let capacity = definitions.def_path_table().num_def_ids() + crates.iter().map(|cnum| cstore.num_def_ids(*cnum)).sum::(); - let mut map = FxHashMap::with_capacity_and_hasher(capacity, Default::default()); + let mut map = UnhashMap::with_capacity_and_hasher(capacity, Default::default()); map.extend(definitions.def_path_table().all_def_path_hashes_and_def_ids(LOCAL_CRATE)); for cnum in &crates {