1
Fork 0

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.
This commit is contained in:
Nicholas Nethercote 2023-03-03 17:02:11 +11:00
parent 816f958ac3
commit 9570023ce1
10 changed files with 100 additions and 82 deletions

View file

@ -463,8 +463,10 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
rustc_span::hygiene::clear_syntax_context_map(); rustc_span::hygiene::clear_syntax_context_map();
} }
let hir_hash = compute_hir_hash(tcx, &owners); // Don't hash unless necessary, because it's expensive.
hir::Crate { owners, hir_hash } let opt_hir_hash =
if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None };
hir::Crate { owners, opt_hir_hash }
} }
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
@ -657,40 +659,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
bodies.sort_by_key(|(k, _)| *k); bodies.sort_by_key(|(k, _)| *k);
let bodies = SortedMap::from_presorted_elements(bodies); let bodies = SortedMap::from_presorted_elements(bodies);
let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies);
let (nodes, parenting) =
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies };
let attrs = {
let hash = self.tcx.with_stable_hashing_context(|mut hcx| {
let mut stable_hasher = StableHasher::new();
attrs.hash_stable(&mut hcx, &mut stable_hasher);
stable_hasher.finish()
});
hir::AttributeMap { map: attrs, hash }
};
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) // Don't hash unless necessary, because it's expensive.
} let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() {
/// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
/// queries which depend on the full HIR tree and those which only depend on the item signature.
fn hash_owner(
&mut self,
node: hir::OwnerNode<'hir>,
bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>,
) -> (Fingerprint, Fingerprint) {
self.tcx.with_stable_hashing_context(|mut hcx| { self.tcx.with_stable_hashing_context(|mut hcx| {
let mut stable_hasher = StableHasher::new(); let mut stable_hasher = StableHasher::new();
hcx.with_hir_bodies(node.def_id(), bodies, |hcx| { hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| {
node.hash_stable(hcx, &mut stable_hasher) node.hash_stable(hcx, &mut stable_hasher)
}); });
let hash_including_bodies = stable_hasher.finish(); let h1 = stable_hasher.finish();
let mut stable_hasher = StableHasher::new(); let mut stable_hasher = StableHasher::new();
hcx.without_hir_bodies(|hcx| node.hash_stable(hcx, &mut stable_hasher)); attrs.hash_stable(&mut hcx, &mut stable_hasher);
let hash_without_bodies = stable_hasher.finish(); let h2 = stable_hasher.finish();
(hash_including_bodies, hash_without_bodies)
(Some(h1), Some(h2))
}) })
} else {
(None, None)
};
let (nodes, parenting) =
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
} }
/// This method allocates a new `HirId` for the given `NodeId` and stores it in /// This method allocates a new `HirId` for the given `NodeId` and stores it in

View file

@ -815,12 +815,13 @@ pub struct ParentedNode<'tcx> {
#[derive(Debug)] #[derive(Debug)]
pub struct AttributeMap<'tcx> { pub struct AttributeMap<'tcx> {
pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>, pub map: SortedMap<ItemLocalId, &'tcx [Attribute]>,
pub hash: Fingerprint, // Only present when the crate hash is needed.
pub opt_hash: Option<Fingerprint>,
} }
impl<'tcx> AttributeMap<'tcx> { impl<'tcx> AttributeMap<'tcx> {
pub const EMPTY: &'static AttributeMap<'static> = pub const EMPTY: &'static AttributeMap<'static> =
&AttributeMap { map: SortedMap::new(), hash: Fingerprint::ZERO }; &AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) };
#[inline] #[inline]
pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] {
@ -832,10 +833,9 @@ impl<'tcx> AttributeMap<'tcx> {
/// These nodes are mapped by `ItemLocalId` alongside the index of their parent node. /// These nodes are mapped by `ItemLocalId` alongside the index of their parent node.
/// The HIR tree, including bodies, is pre-hashed. /// The HIR tree, including bodies, is pre-hashed.
pub struct OwnerNodes<'tcx> { pub struct OwnerNodes<'tcx> {
/// Pre-computed hash of the full HIR. /// Pre-computed hash of the full HIR. Used in the crate hash. Only present
pub hash_including_bodies: Fingerprint, /// when incr. comp. is enabled.
/// Pre-computed hash of the item signature, without recursing into the body. pub opt_hash_including_bodies: Option<Fingerprint>,
pub hash_without_bodies: Fingerprint,
/// Full HIR for the current owner. /// Full HIR for the current owner.
// The zeroth node's parent should never be accessed: the owner's parent is computed by the // The zeroth node's parent should never be accessed: the owner's parent is computed by the
// hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally // 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<'_> {
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) )
.field("bodies", &self.bodies) .field("bodies", &self.bodies)
.field("hash_without_bodies", &self.hash_without_bodies) .field("opt_hash_including_bodies", &self.opt_hash_including_bodies)
.field("hash_including_bodies", &self.hash_including_bodies)
.finish() .finish()
} }
} }
@ -940,7 +939,8 @@ impl<T> MaybeOwner<T> {
#[derive(Debug)] #[derive(Debug)]
pub struct Crate<'hir> { pub struct Crate<'hir> {
pub owners: IndexVec<LocalDefId, MaybeOwner<&'hir OwnerInfo<'hir>>>, pub owners: IndexVec<LocalDefId, MaybeOwner<&'hir OwnerInfo<'hir>>>,
pub hir_hash: Fingerprint, // Only present when incr. comp. is enabled.
pub opt_hir_hash: Option<Fingerprint>,
} }
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]

View file

@ -100,24 +100,23 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'
// `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
// the body satisfies the condition of two nodes being different have different // the body satisfies the condition of two nodes being different have different
// `hash_stable` results. // `hash_stable` results.
let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } = let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self;
*self; opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher);
hash_including_bodies.hash_stable(hcx, hasher);
} }
} }
impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> { impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for AttributeMap<'tcx> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
// We ignore the `map` since it refers to information included in `hash` which is hashed in // We ignore the `map` since it refers to information included in `opt_hash` which is
// the collector and used for the crate hash. // hashed in the collector and used for the crate hash.
let AttributeMap { hash, map: _ } = *self; let AttributeMap { opt_hash, map: _ } = *self;
hash.hash_stable(hcx, hasher); opt_hash.unwrap().hash_stable(hcx, hasher);
} }
} }
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> { impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for Crate<'_> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
let Crate { owners: _, hir_hash } = self; let Crate { owners: _, opt_hir_hash } = self;
hir_hash.hash_stable(hcx, hasher) opt_hir_hash.unwrap().hash_stable(hcx, hasher)
} }
} }

View file

@ -297,10 +297,12 @@ pub fn prepare_session_directory(
/// renaming it to `s-{timestamp}-{svh}` and releasing the file lock. /// renaming it to `s-{timestamp}-{svh}` and releasing the file lock.
/// If there have been compilation errors, however, this function will just /// If there have been compilation errors, however, this function will just
/// delete the presumably invalid session directory. /// delete the presumably invalid session directory.
pub fn finalize_session_directory(sess: &Session, svh: Svh) { pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
if sess.opts.incremental.is_none() { if sess.opts.incremental.is_none() {
return; return;
} }
// The svh is always produced when incr. comp. is enabled.
let svh = svh.unwrap();
let _timer = sess.timer("incr_comp_finalize_session_directory"); let _timer = sess.timer("incr_comp_finalize_session_directory");

View file

@ -284,7 +284,11 @@ impl<'tcx> Queries<'tcx> {
let codegen_backend = self.codegen_backend().clone(); let codegen_backend = self.codegen_backend().clone();
let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| { let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| {
(tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone(), tcx.dep_graph.clone()) (
if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None },
tcx.output_filenames(()).clone(),
tcx.dep_graph.clone(),
)
}); });
let ongoing_codegen = self.ongoing_codegen()?.steal(); let ongoing_codegen = self.ongoing_codegen()?.steal();
@ -308,7 +312,8 @@ pub struct Linker {
// compilation outputs // compilation outputs
dep_graph: DepGraph, dep_graph: DepGraph,
prepare_outputs: Arc<OutputFilenames>, prepare_outputs: Arc<OutputFilenames>,
crate_hash: Svh, // Only present when incr. comp. is enabled.
crate_hash: Option<Svh>,
ongoing_codegen: Box<dyn Any>, ongoing_codegen: Box<dyn Any>,
} }

View file

@ -6,9 +6,9 @@ use crate::{encode_metadata, EncodedMetadata};
use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{CrateType, OutputType}; use rustc_session::config::OutputType;
use rustc_session::output::filename_for_metadata; use rustc_session::output::filename_for_metadata;
use rustc_session::Session; use rustc_session::{MetadataKind, Session};
use tempfile::Builder as TempFileBuilder; use tempfile::Builder as TempFileBuilder;
use std::fs; use std::fs;
@ -39,27 +39,6 @@ pub fn emit_wrapper_file(
} }
pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum MetadataKind {
None,
Uncompressed,
Compressed,
}
let metadata_kind = tcx
.sess
.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
CrateType::Rlib => MetadataKind::Uncompressed,
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
.max()
.unwrap_or(MetadataKind::None);
let crate_name = tcx.crate_name(LOCAL_CRATE); let crate_name = tcx.crate_name(LOCAL_CRATE);
let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(())); let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(()));
// To avoid races with another rustc process scanning the output directory, // 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) {
// Always create a file at `metadata_filename`, even if we have nothing to write to it. // Always create a file at `metadata_filename`, even if we have nothing to write to it.
// This simplifies the creation of the output `out_filename` when requested. // This simplifies the creation of the output `out_filename` when requested.
let metadata_kind = tcx.sess.metadata_kind();
match metadata_kind { match metadata_kind {
MetadataKind::None => { MetadataKind::None => {
std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { std::fs::File::create(&metadata_filename).unwrap_or_else(|err| {

View file

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

View file

@ -8,7 +8,6 @@ pub mod place;
use crate::ty::query::Providers; use crate::ty::query::Providers;
use crate::ty::{ImplSubject, TyCtxt}; use crate::ty::{ImplSubject, TyCtxt};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
@ -24,14 +23,15 @@ use rustc_span::{ExpnId, DUMMY_SP};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Owner<'tcx> { pub struct Owner<'tcx> {
node: OwnerNode<'tcx>, node: OwnerNode<'tcx>,
hash_without_bodies: Fingerprint,
} }
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> { impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Owner<'tcx> {
#[inline] #[inline]
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
let Owner { node: _, hash_without_bodies } = self; // Perform a shallow hash instead using the deep hash saved in `OwnerNodes`. This lets us
hash_without_bodies.hash_stable(hcx, hasher) // differentiate queries that depend on the full HIR tree from those that only depend on
// the item signature.
hcx.without_hir_bodies(|hcx| self.node.hash_stable(hcx, hasher));
} }
} }
@ -123,7 +123,7 @@ pub fn provide(providers: &mut Providers) {
providers.hir_owner = |tcx, id| { providers.hir_owner = |tcx, id| {
let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?; let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?;
let node = owner.node(); let node = owner.node();
Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies }) Some(Owner { node })
}; };
providers.opt_local_def_id_to_hir_id = |tcx, id| { providers.opt_local_def_id_to_hir_id = |tcx, id| {
let owner = tcx.hir_crate(()).owners[id].map(|_| ()); let owner = tcx.hir_crate(()).owners[id].map(|_| ());

View file

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

View file

@ -224,6 +224,13 @@ pub struct PerfStats {
pub normalize_projection_ty: AtomicUsize, pub normalize_projection_ty: AtomicUsize,
} }
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum MetadataKind {
None,
Uncompressed,
Compressed,
}
impl Session { impl Session {
pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) { pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
self.miri_unleashed_features.lock().push((span, feature_gate)); self.miri_unleashed_features.lock().push((span, feature_gate));
@ -287,6 +294,38 @@ impl Session {
self.crate_types.get().unwrap().as_slice() self.crate_types.get().unwrap().as_slice()
} }
pub fn needs_crate_hash(&self) -> bool {
// Why is the crate hash needed for these configurations?
// - debug_assertions: for the "fingerprint the result" check in
// `rustc_query_system::query::plumbing::execute_job`.
// - incremental: for query lookups.
// - needs_metadata: for putting into crate metadata.
// - instrument_coverage: for putting into coverage data (see
// `hash_mir_source`).
cfg!(debug_assertions)
|| self.opts.incremental.is_some()
|| self.needs_metadata()
|| self.instrument_coverage()
}
pub fn metadata_kind(&self) -> MetadataKind {
self.crate_types()
.iter()
.map(|ty| match *ty {
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => {
MetadataKind::None
}
CrateType::Rlib => MetadataKind::Uncompressed,
CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
})
.max()
.unwrap_or(MetadataKind::None)
}
pub fn needs_metadata(&self) -> bool {
self.metadata_kind() != MetadataKind::None
}
pub fn init_crate_types(&self, crate_types: Vec<CrateType>) { pub fn init_crate_types(&self, crate_types: Vec<CrateType>) {
self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
} }