Skip to content

Commit 150cb38

Browse files
committed
Auto merge of rust-lang#108794 - nnethercote:avoid-unnecessary-hashing, r=cjgillot
Avoid unnecessary hashing I noticed some stable hashing being done in a non-incremental build. It turns out that some of this is necessary to compute the crate hash, but some of it is not. Removing the unnecessary hashing is a perf win. r? `@cjgillot`
2 parents 501ad02 + 9570023 commit 150cb38

File tree

10 files changed

+100
-82
lines changed

10 files changed

+100
-82
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+24-31
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,10 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
464464
rustc_span::hygiene::clear_syntax_context_map();
465465
}
466466

467-
let hir_hash = compute_hir_hash(tcx, &owners);
468-
hir::Crate { owners, hir_hash }
467+
// Don't hash unless necessary, because it's expensive.
468+
let opt_hir_hash =
469+
if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
470+
hir::Crate { owners, opt_hir_hash }
469471
}
470472

471473
#[derive(Copy, Clone, PartialEq, Debug)]
@@ -658,42 +660,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
658660

659661
bodies.sort_by_key(|(k, _)| *k);
660662
let bodies = SortedMap::from_presorted_elements(bodies);
661-
let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies);
662-
let (nodes, parenting) =
663-
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
664-
let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies };
665-
let attrs = {
666-
let hash = self.tcx.with_stable_hashing_context(|mut hcx| {
663+
664+
// Don't hash unless necessary, because it's expensive.
665+
let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() {
666+
self.tcx.with_stable_hashing_context(|mut hcx| {
667+
let mut stable_hasher = StableHasher::new();
668+
hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| {
669+
node.hash_stable(hcx, &mut stable_hasher)
670+
});
671+
let h1 = stable_hasher.finish();
672+
667673
let mut stable_hasher = StableHasher::new();
668674
attrs.hash_stable(&mut hcx, &mut stable_hasher);
669-
stable_hasher.finish()
670-
});
671-
hir::AttributeMap { map: attrs, hash }
675+
let h2 = stable_hasher.finish();
676+
677+
(Some(h1), Some(h2))
678+
})
679+
} else {
680+
(None, None)
672681
};
682+
let (nodes, parenting) =
683+
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
684+
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
685+
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
673686

674687
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
675688
}
676689

677-
/// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
678-
/// queries which depend on the full HIR tree and those which only depend on the item signature.
679-
fn hash_owner(
680-
&mut self,
681-
node: hir::OwnerNode<'hir>,
682-
bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>,
683-
) -> (Fingerprint, Fingerprint) {
684-
self.tcx.with_stable_hashing_context(|mut hcx| {
685-
let mut stable_hasher = StableHasher::new();
686-
hcx.with_hir_bodies(node.def_id(), bodies, |hcx| {
687-
node.hash_stable(hcx, &mut stable_hasher)
688-
});
689-
let hash_including_bodies = stable_hasher.finish();
690-
let mut stable_hasher = StableHasher::new();
691-
hcx.without_hir_bodies(|hcx| node.hash_stable(hcx, &mut stable_hasher));
692-
let hash_without_bodies = stable_hasher.finish();
693-
(hash_including_bodies, hash_without_bodies)
694-
})
695-
}
696-
697690
/// This method allocates a new `HirId` for the given `NodeId` and stores it in
698691
/// the `LoweringContext`'s `NodeId => HirId` map.
699692
/// Take care not to call this method if the resulting `HirId` is then not

compiler/rustc_hir/src/hir.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -815,12 +815,13 @@ pub struct ParentedNode<'tcx> {
815815
#[derive(Debug)]
816816
pub struct AttributeMap<'tcx> {
817817
pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>,
818-
pub hash: Fingerprint,
818+
// Only present when the crate hash is needed.
819+
pub opt_hash: Option<Fingerprint>,
819820
}
820821

821822
impl<'tcx> AttributeMap<'tcx> {
822823
pub const EMPTY: &'static AttributeMap<'static> =
823-
&AttributeMap { map: SortedMap::new(), hash: Fingerprint::ZERO };
824+
&AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) };
824825

825826
#[inline]
826827
pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] {
@@ -832,10 +833,9 @@ impl<'tcx> AttributeMap<'tcx> {
832833
/// These nodes are mapped by `ItemLocalId` alongside the index of their parent node.
833834
/// The HIR tree, including bodies, is pre-hashed.
834835
pub struct OwnerNodes<'tcx> {
835-
/// Pre-computed hash of the full HIR.
836-
pub hash_including_bodies: Fingerprint,
837-
/// Pre-computed hash of the item signature, without recursing into the body.
838-
pub hash_without_bodies: Fingerprint,
836+
/// Pre-computed hash of the full HIR. Used in the crate hash. Only present
837+
/// when incr. comp. is enabled.
838+
pub opt_hash_including_bodies: Option<Fingerprint>,
839839
/// Full HIR for the current owner.
840840
// The zeroth node's parent should never be accessed: the owner's parent is computed by the
841841
// hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
@@ -872,8 +872,7 @@ impl fmt::Debug for OwnerNodes<'_> {
872872
.collect::<Vec<_>>(),
873873
)
874874
.field("bodies", &self.bodies)
875-
.field("hash_without_bodies", &self.hash_without_bodies)
876-
.field("hash_including_bodies", &self.hash_including_bodies)
875+
.field("opt_hash_including_bodies", &self.opt_hash_including_bodies)
877876
.finish()
878877
}
879878
}
@@ -940,7 +939,8 @@ impl<T> MaybeOwner<T> {
940939
#[derive(Debug)]
941940
pub struct Crate<'hir> {
942941
pub owners: IndexVec<LocalDefId, MaybeOwner<&'hir OwnerInfo<'hir>>>,
943-
pub hir_hash: Fingerprint,
942+
// Only present when incr. comp. is enabled.
943+
pub opt_hir_hash: Option<Fingerprint>,
944944
}
945945

946946
#[derive(Debug, HashStable_Generic)]

compiler/rustc_hir/src/stable_hash_impls.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -100,24 +100,23 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'
100100
// `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
101101
// the body satisfies the condition of two nodes being different have different
102102
// `hash_stable` results.
103-
let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } =
104-
*self;
105-
hash_including_bodies.hash_stable(hcx, hasher);
103+
let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self;
104+
opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher);
106105
}
107106
}
108107

109108
impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
110109
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
111-
// We ignore the `map` since it refers to information included in `hash` which is hashed in
112-
// the collector and used for the crate hash.
113-
let AttributeMap { hash, map: _ } = *self;
114-
hash.hash_stable(hcx, hasher);
110+
// We ignore the `map` since it refers to information included in `opt_hash` which is
111+
// hashed in the collector and used for the crate hash.
112+
let AttributeMap { opt_hash, map: _ } = *self;
113+
opt_hash.unwrap().hash_stable(hcx, hasher);
115114
}
116115
}
117116

118117
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
119118
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
120-
let Crate { owners: _, hir_hash } = self;
121-
hir_hash.hash_stable(hcx, hasher)
119+
let Crate { owners: _, opt_hir_hash } = self;
120+
opt_hir_hash.unwrap().hash_stable(hcx, hasher)
122121
}
123122
}

compiler/rustc_incremental/src/persist/fs.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,12 @@ pub fn prepare_session_directory(
297297
/// renaming it to `s-{timestamp}-{svh}` and releasing the file lock.
298298
/// If there have been compilation errors, however, this function will just
299299
/// delete the presumably invalid session directory.
300-
pub fn finalize_session_directory(sess: &Session, svh: Svh) {
300+
pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
301301
if sess.opts.incremental.is_none() {
302302
return;
303303
}
304+
// The svh is always produced when incr. comp. is enabled.
305+
let svh = svh.unwrap();
304306

305307
let _timer = sess.timer("incr_comp_finalize_session_directory");
306308

compiler/rustc_interface/src/queries.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,11 @@ impl<'tcx> Queries<'tcx> {
284284
let codegen_backend = self.codegen_backend().clone();
285285

286286
let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| {
287-
(tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone(), tcx.dep_graph.clone())
287+
(
288+
if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
289+
tcx.output_filenames(()).clone(),
290+
tcx.dep_graph.clone(),
291+
)
288292
});
289293
let ongoing_codegen = self.ongoing_codegen()?.steal();
290294

@@ -308,7 +312,8 @@ pub struct Linker {
308312
// compilation outputs
309313
dep_graph: DepGraph,
310314
prepare_outputs: Arc<OutputFilenames>,
311-
crate_hash: Svh,
315+
// Only present when incr. comp. is enabled.
316+
crate_hash: Option<Svh>,
312317
ongoing_codegen: Box<dyn Any>,
313318
}
314319

compiler/rustc_metadata/src/fs.rs

+3-23
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use crate::{encode_metadata, EncodedMetadata};
66
use rustc_data_structures::temp_dir::MaybeTempDir;
77
use rustc_hir::def_id::LOCAL_CRATE;
88
use rustc_middle::ty::TyCtxt;
9-
use rustc_session::config::{CrateType, OutputType};
9+
use rustc_session::config::OutputType;
1010
use rustc_session::output::filename_for_metadata;
11-
use rustc_session::Session;
11+
use rustc_session::{MetadataKind, Session};
1212
use tempfile::Builder as TempFileBuilder;
1313

1414
use std::fs;
@@ -39,27 +39,6 @@ pub fn emit_wrapper_file(
3939
}
4040

4141
pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
42-
#[derive(PartialEq, Eq, PartialOrd, Ord)]
43-
enum MetadataKind {
44-
None,
45-
Uncompressed,
46-
Compressed,
47-
}
48-
49-
let metadata_kind = tcx
50-
.sess
51-
.crate_types()
52-
.iter()
53-
.map(|ty| match *ty {
54-
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
55-
56-
CrateType::Rlib => MetadataKind::Uncompressed,
57-
58-
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
59-
})
60-
.max()
61-
.unwrap_or(MetadataKind::None);
62-
6342
let crate_name = tcx.crate_name(LOCAL_CRATE);
6443
let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(()));
6544
// To avoid races with another rustc process scanning the output directory,
@@ -76,6 +55,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
7655

7756
// Always create a file at `metadata_filename`, even if we have nothing to write to it.
7857
// This simplifies the creation of the output `out_filename` when requested.
58+
let metadata_kind = tcx.sess.metadata_kind();
7959
match metadata_kind {
8060
MetadataKind::None => {
8161
std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {

compiler/rustc_middle/src/hir/map/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,7 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> {
11341134
pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh {
11351135
debug_assert_eq!(crate_num, LOCAL_CRATE);
11361136
let krate = tcx.hir_crate(());
1137-
let hir_body_hash = krate.hir_hash;
1137+
let hir_body_hash = krate.opt_hir_hash.expect("HIR hash missing while computing crate hash");
11381138

11391139
let upstream_crates = upstream_crates(tcx);
11401140

compiler/rustc_middle/src/hir/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub mod place;
88

99
use crate::ty::query::Providers;
1010
use crate::ty::{ImplSubject, TyCtxt};
11-
use rustc_data_structures::fingerprint::Fingerprint;
1211
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
1312
use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
1413
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -24,14 +23,15 @@ use rustc_span::{ExpnId, DUMMY_SP};
2423
#[derive(Copy, Clone, Debug)]
2524
pub struct Owner<'tcx> {
2625
node: OwnerNode<'tcx>,
27-
hash_without_bodies: Fingerprint,
2826
}
2927

3028
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> {
3129
#[inline]
3230
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
33-
let Owner { node: _, hash_without_bodies } = self;
34-
hash_without_bodies.hash_stable(hcx, hasher)
31+
// Perform a shallow hash instead using the deep hash saved in `OwnerNodes`. This lets us
32+
// differentiate queries that depend on the full HIR tree from those that only depend on
33+
// the item signature.
34+
hcx.without_hir_bodies(|hcx| self.node.hash_stable(hcx, hasher));
3535
}
3636
}
3737

@@ -123,7 +123,7 @@ pub fn provide(providers: &mut Providers) {
123123
providers.hir_owner = |tcx, id| {
124124
let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?;
125125
let node = owner.node();
126-
Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies })
126+
Some(Owner { node })
127127
};
128128
providers.opt_local_def_id_to_hir_id = |tcx, id| {
129129
let owner = tcx.hir_crate(()).owners[id].map(|_| ());

compiler/rustc_mir_transform/src/coverage/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -577,5 +577,5 @@ fn get_body_span<'tcx>(
577577
fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
578578
// FIXME(cjgillot) Stop hashing HIR manually here.
579579
let owner = hir_body.id().hir_id.owner;
580-
tcx.hir_owner_nodes(owner).unwrap().hash_including_bodies.to_smaller_hash()
580+
tcx.hir_owner_nodes(owner).unwrap().opt_hash_including_bodies.unwrap().to_smaller_hash()
581581
}

compiler/rustc_session/src/session.rs

+39
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,13 @@ pub struct PerfStats {
224224
pub normalize_projection_ty: AtomicUsize,
225225
}
226226

227+
#[derive(PartialEq, Eq, PartialOrd, Ord)]
228+
pub enum MetadataKind {
229+
None,
230+
Uncompressed,
231+
Compressed,
232+
}
233+
227234
impl Session {
228235
pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
229236
self.miri_unleashed_features.lock().push((span, feature_gate));
@@ -287,6 +294,38 @@ impl Session {
287294
self.crate_types.get().unwrap().as_slice()
288295
}
289296

297+
pub fn needs_crate_hash(&self) -> bool {
298+
// Why is the crate hash needed for these configurations?
299+
// - debug_assertions: for the "fingerprint the result" check in
300+
// `rustc_query_system::query::plumbing::execute_job`.
301+
// - incremental: for query lookups.
302+
// - needs_metadata: for putting into crate metadata.
303+
// - instrument_coverage: for putting into coverage data (see
304+
// `hash_mir_source`).
305+
cfg!(debug_assertions)
306+
|| self.opts.incremental.is_some()
307+
|| self.needs_metadata()
308+
|| self.instrument_coverage()
309+
}
310+
311+
pub fn metadata_kind(&self) -> MetadataKind {
312+
self.crate_types()
313+
.iter()
314+
.map(|ty| match *ty {
315+
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
316+
MetadataKind::None
317+
}
318+
CrateType::Rlib => MetadataKind::Uncompressed,
319+
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
320+
})
321+
.max()
322+
.unwrap_or(MetadataKind::None)
323+
}
324+
325+
pub fn needs_metadata(&self) -> bool {
326+
self.metadata_kind() != MetadataKind::None
327+
}
328+
290329
pub fn init_crate_types(&self, crate_types: Vec<CrateType>) {
291330
self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
292331
}

0 commit comments

Comments
 (0)