Skip to content

Commit 9570023

Browse files
committed
Only compute the crate hash when necessary.
The crate hash is needed: - if debug assertions are enabled, or - if incr. comp. is enabled, or - if metadata is being generated, or - if `-C instrumentation-coverage` is enabled. This commit avoids computing the crate hash when these conditions are all false, such as when doing a release build of a binary crate. It uses `Option` to store the hashes when needed, rather than computing them on demand, because some of them are needed in multiple places and computing them on demand would make compilation slower. The commit also removes `Owner::hash_without_bodies`. There is no benefit to pre-computing that one, it can just be done in the normal fashion.
1 parent 816f958 commit 9570023

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
@@ -463,8 +463,10 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
463463
rustc_span::hygiene::clear_syntax_context_map();
464464
}
465465

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

470472
#[derive(Copy, Clone, PartialEq, Debug)]
@@ -657,42 +659,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
657659

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

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

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