Skip to content

Commit 8b4fa75

Browse files
committed
Only use the new node hashmap for anonymous nodes.
1 parent fe03b46 commit 8b4fa75

File tree

5 files changed

+110
-92
lines changed

5 files changed

+110
-92
lines changed

compiler/rustc_codegen_cranelift/src/driver/aot.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -522,11 +522,12 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR
522522
// know that later). If we are not doing LTO, there is only one optimized
523523
// version of each module, so we re-use that.
524524
let dep_node = cgu.codegen_dep_node(tcx);
525-
assert!(
526-
!tcx.dep_graph.dep_node_exists(&dep_node),
527-
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
528-
cgu.name()
529-
);
525+
tcx.dep_graph.assert_nonexistent_node(&dep_node, || {
526+
format!(
527+
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
528+
cgu.name()
529+
)
530+
});
530531

531532
if tcx.try_mark_green(&dep_node) { CguReuse::PostLto } else { CguReuse::No }
532533
}

compiler/rustc_codegen_ssa/src/base.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1031,11 +1031,12 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR
10311031
// know that later). If we are not doing LTO, there is only one optimized
10321032
// version of each module, so we re-use that.
10331033
let dep_node = cgu.codegen_dep_node(tcx);
1034-
assert!(
1035-
!tcx.dep_graph.dep_node_exists(&dep_node),
1036-
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
1037-
cgu.name()
1038-
);
1034+
tcx.dep_graph.assert_nonexistent_node(&dep_node, || {
1035+
format!(
1036+
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
1037+
cgu.name()
1038+
)
1039+
});
10391040

10401041
if tcx.try_mark_green(&dep_node) {
10411042
// We can re-use either the pre- or the post-thinlto state. If no LTO is

compiler/rustc_incremental/src/persist/save.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub fn build_dep_graph(
170170
sess.opts.dep_tracking_hash(false).encode(&mut encoder);
171171

172172
Some(DepGraph::new(
173-
&sess.prof,
173+
sess,
174174
prev_graph,
175175
prev_work_products,
176176
encoder,

compiler/rustc_query_system/src/dep_graph/graph.rs

+94-80
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
99
use rustc_data_structures::unord::UnordMap;
1010
use rustc_index::IndexVec;
1111
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
12+
use rustc_session::Session;
1213
use smallvec::{smallvec, SmallVec};
1314
use std::assert_matches::assert_matches;
1415
use std::collections::hash_map::Entry;
@@ -115,7 +116,7 @@ where
115116

116117
impl<K: DepKind> DepGraph<K> {
117118
pub fn new(
118-
profiler: &SelfProfilerRef,
119+
session: &Session,
119120
prev_graph: SerializedDepGraph<K>,
120121
prev_work_products: FxIndexMap<WorkProductId, WorkProduct>,
121122
encoder: FileEncoder,
@@ -125,7 +126,7 @@ impl<K: DepKind> DepGraph<K> {
125126
let prev_graph_node_count = prev_graph.node_count();
126127

127128
let current = CurrentDepGraph::new(
128-
profiler,
129+
session,
129130
prev_graph_node_count,
130131
encoder,
131132
record_graph,
@@ -136,7 +137,7 @@ impl<K: DepKind> DepGraph<K> {
136137

137138
// Instantiate a dependy-less node only once for anonymous queries.
138139
let _green_node_index = current.intern_new_node(
139-
profiler,
140+
&session.prof,
140141
DepNode { kind: DepKind::NULL, hash: current.anon_id_seed.into() },
141142
smallvec![],
142143
Fingerprint::ZERO,
@@ -145,7 +146,7 @@ impl<K: DepKind> DepGraph<K> {
145146

146147
// Instantiate a dependy-less red node only once for anonymous queries.
147148
let (red_node_index, red_node_prev_index_and_color) = current.intern_node(
148-
profiler,
149+
&session.prof,
149150
&prev_graph,
150151
DepNode { kind: DepKind::RED, hash: Fingerprint::ZERO.into() },
151152
smallvec![],
@@ -343,17 +344,13 @@ impl<K: DepKind> DepGraphData<K> {
343344
task: fn(Ctxt, A) -> R,
344345
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
345346
) -> (R, DepNodeIndex) {
346-
// If the following assertion triggers, it can have two reasons:
347-
// 1. Something is wrong with DepNode creation, either here or
348-
// in `DepGraph::try_mark_green()`.
349-
// 2. Two distinct query keys get mapped to the same `DepNode`
350-
// (see for example #48923).
351-
assert!(
352-
!self.dep_node_exists(&key),
353-
"forcing query with already existing `DepNode`\n\
354-
- query-key: {arg:?}\n\
355-
- dep-node: {key:?}"
356-
);
347+
self.assert_nonexistent_node(&key, || {
348+
format!(
349+
"forcing query with already existing `DepNode`\n\
350+
- query-key: {arg:?}\n\
351+
- dep-node: {key:?}"
352+
)
353+
});
357354

358355
let with_deps = |task_deps| K::with_deps(task_deps, || task(cx, arg));
359356
let (result, edges) = if cx.dep_context().is_eval_always(key.kind) {
@@ -449,12 +446,32 @@ impl<K: DepKind> DepGraphData<K> {
449446
hash: self.current.anon_id_seed.combine(hasher.finish()).into(),
450447
};
451448

452-
self.current.intern_new_node(
453-
cx.profiler(),
454-
target_dep_node,
455-
task_deps,
456-
Fingerprint::ZERO,
457-
)
449+
// The DepNodes generated by the process above are not unique. 2 queries could
450+
// have exactly the same dependencies. However, deserialization does not handle
451+
// duplicated nodes, so we do the deduplication here directly.
452+
//
453+
// As anonymous nodes are a small quantity compared to the full dep-graph, the
454+
// memory impact of this `anon_node_to_index` map remains tolerable, and helps
455+
// us avoid useless growth of the graph with almost-equivalent nodes.
456+
match self
457+
.current
458+
.anon_node_to_index
459+
.get_shard_by_value(&target_dep_node)
460+
.lock()
461+
.entry(target_dep_node)
462+
{
463+
Entry::Occupied(entry) => *entry.get(),
464+
Entry::Vacant(entry) => {
465+
let dep_node_index = self.current.intern_new_node(
466+
cx.profiler(),
467+
target_dep_node,
468+
task_deps,
469+
Fingerprint::ZERO,
470+
);
471+
entry.insert(dep_node_index);
472+
dep_node_index
473+
}
474+
}
458475
}
459476
};
460477

@@ -625,25 +642,18 @@ impl<K: DepKind> DepGraph<K> {
625642
}
626643

627644
impl<K: DepKind> DepGraphData<K> {
628-
#[inline]
629-
pub fn dep_node_index_of_opt(&self, dep_node: &DepNode<K>) -> Option<DepNodeIndex> {
630-
if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
631-
self.current.prev_index_to_index.lock()[prev_index]
632-
} else {
633-
self.current
634-
.new_node_to_index
635-
.get_shard_by_value(dep_node)
636-
.lock()
637-
.get(dep_node)
638-
.copied()
645+
fn assert_nonexistent_node<S: std::fmt::Display>(
646+
&self,
647+
_dep_node: &DepNode<K>,
648+
_msg: impl FnOnce() -> S,
649+
) {
650+
#[cfg(debug_assertions)]
651+
if let Some(seen_dep_nodes) = &self.current.seen_dep_nodes {
652+
let seen = seen_dep_nodes.lock().contains(_dep_node);
653+
assert!(!seen, "{}", _msg());
639654
}
640655
}
641656

642-
#[inline]
643-
pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool {
644-
self.dep_node_index_of_opt(dep_node).is_some()
645-
}
646-
647657
fn node_color(&self, dep_node: &DepNode<K>) -> Option<DepNodeColor> {
648658
if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
649659
self.colors.get(prev_index)
@@ -676,11 +686,6 @@ impl<K: DepKind> DepGraphData<K> {
676686
}
677687

678688
impl<K: DepKind> DepGraph<K> {
679-
#[inline]
680-
pub fn dep_node_exists(&self, dep_node: &DepNode<K>) -> bool {
681-
self.data.as_ref().is_some_and(|data| data.dep_node_exists(dep_node))
682-
}
683-
684689
/// Checks whether a previous work product exists for `v` and, if
685690
/// so, return the path that leads to it. Used to skip doing work.
686691
pub fn previous_work_product(&self, v: &WorkProductId) -> Option<WorkProduct> {
@@ -762,7 +767,7 @@ impl<K: DepKind> DepGraphData<K> {
762767
}
763768
}
764769

765-
#[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")]
770+
#[instrument(skip(self, qcx, frame), level = "debug")]
766771
fn try_mark_parent_green<Qcx: QueryContext<DepKind = K>>(
767772
&self,
768773
qcx: Qcx,
@@ -861,10 +866,11 @@ impl<K: DepKind> DepGraphData<K> {
861866
let frame = MarkFrame { index: prev_dep_node_index, parent: frame };
862867

863868
#[cfg(not(parallel_compiler))]
864-
{
865-
debug_assert!(!self.dep_node_exists(dep_node));
866-
debug_assert!(self.colors.get(prev_dep_node_index).is_none());
867-
}
869+
self.assert_nonexistent_node(dep_node, || {
870+
format!("trying to mark existing {dep_node:?} as green")
871+
});
872+
#[cfg(not(parallel_compiler))]
873+
debug_assert!(self.colors.get(prev_dep_node_index).is_none());
868874

869875
// We never try to mark eval_always nodes as green
870876
debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind));
@@ -959,6 +965,18 @@ impl<K: DepKind> DepGraph<K> {
959965
self.node_color(dep_node).is_some_and(|c| c.is_green())
960966
}
961967

968+
pub fn assert_nonexistent_node<S: std::fmt::Display>(
969+
&self,
970+
dep_node: &DepNode<K>,
971+
msg: impl FnOnce() -> S,
972+
) {
973+
if cfg!(debug_assertions)
974+
&& let Some(data) = &self.data
975+
{
976+
data.assert_nonexistent_node(dep_node, msg)
977+
}
978+
}
979+
962980
/// This method loads all on-disk cacheable query results into memory, so
963981
/// they can be written out to the new cache file again. Most query results
964982
/// will already be in memory but in the case where we marked something as
@@ -1066,24 +1084,24 @@ rustc_index::newtype_index! {
10661084
/// largest in the compiler.
10671085
///
10681086
/// For this reason, we avoid storing `DepNode`s more than once as map
1069-
/// keys. The `new_node_to_index` map only contains nodes not in the previous
1087+
/// keys. The `anon_node_to_index` map only contains nodes of anonymous queries not in the previous
10701088
/// graph, and we map nodes in the previous graph to indices via a two-step
10711089
/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
10721090
/// and the `prev_index_to_index` vector (which is more compact and faster than
10731091
/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
10741092
///
1075-
/// This struct uses three locks internally. The `data`, `new_node_to_index`,
1093+
/// This struct uses three locks internally. The `data`, `anon_node_to_index`,
10761094
/// and `prev_index_to_index` fields are locked separately. Operations that take
10771095
/// a `DepNodeIndex` typically just access the `data` field.
10781096
///
10791097
/// We only need to manipulate at most two locks simultaneously:
1080-
/// `new_node_to_index` and `data`, or `prev_index_to_index` and `data`. When
1081-
/// manipulating both, we acquire `new_node_to_index` or `prev_index_to_index`
1098+
/// `anon_node_to_index` and `data`, or `prev_index_to_index` and `data`. When
1099+
/// manipulating both, we acquire `anon_node_to_index` or `prev_index_to_index`
10821100
/// first, and `data` second.
10831101
pub(super) struct CurrentDepGraph<K: DepKind> {
10841102
encoder: Steal<GraphEncoder<K>>,
1085-
new_node_to_index: Sharded<FxHashMap<DepNode<K>, DepNodeIndex>>,
10861103
prev_index_to_index: Lock<IndexVec<SerializedDepNodeIndex, Option<DepNodeIndex>>>,
1104+
anon_node_to_index: Sharded<FxHashMap<DepNode<K>, DepNodeIndex>>,
10871105

10881106
/// This is used to verify that fingerprints do not change between the creation of a node
10891107
/// and its recomputation.
@@ -1095,6 +1113,11 @@ pub(super) struct CurrentDepGraph<K: DepKind> {
10951113
#[cfg(debug_assertions)]
10961114
forbidden_edge: Option<EdgeFilter<K>>,
10971115

1116+
/// Used to verify the absence of hash collisions among DepNodes.
1117+
/// This field is only `Some` if the `-Z incremental_verify_ich` option is present.
1118+
#[cfg(debug_assertions)]
1119+
seen_dep_nodes: Option<Lock<FxHashSet<DepNode<K>>>>,
1120+
10981121
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
10991122
/// their edges. This has the beneficial side-effect that multiple anonymous
11001123
/// nodes can be coalesced into one without changing the semantics of the
@@ -1122,7 +1145,7 @@ pub(super) struct CurrentDepGraph<K: DepKind> {
11221145

11231146
impl<K: DepKind> CurrentDepGraph<K> {
11241147
fn new(
1125-
profiler: &SelfProfilerRef,
1148+
session: &Session,
11261149
prev_graph_node_count: usize,
11271150
encoder: FileEncoder,
11281151
record_graph: bool,
@@ -1152,7 +1175,8 @@ impl<K: DepKind> CurrentDepGraph<K> {
11521175

11531176
let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200;
11541177

1155-
let node_intern_event_id = profiler
1178+
let node_intern_event_id = session
1179+
.prof
11561180
.get_or_alloc_cached_string("incr_comp_intern_dep_graph_node")
11571181
.map(EventId::from_label);
11581182

@@ -1163,7 +1187,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
11631187
record_graph,
11641188
record_stats,
11651189
)),
1166-
new_node_to_index: Sharded::new(|| {
1190+
anon_node_to_index: Sharded::new(|| {
11671191
FxHashMap::with_capacity_and_hasher(
11681192
new_node_count_estimate / sharded::SHARDS,
11691193
Default::default(),
@@ -1175,6 +1199,13 @@ impl<K: DepKind> CurrentDepGraph<K> {
11751199
forbidden_edge,
11761200
#[cfg(debug_assertions)]
11771201
fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)),
1202+
#[cfg(debug_assertions)]
1203+
seen_dep_nodes: session.opts.unstable_opts.incremental_verify_ich.then(|| {
1204+
Lock::new(FxHashSet::with_capacity_and_hasher(
1205+
new_node_count_estimate,
1206+
Default::default(),
1207+
))
1208+
}),
11781209
total_read_count: AtomicU64::new(0),
11791210
total_duplicate_read_count: AtomicU64::new(0),
11801211
node_intern_event_id,
@@ -1200,20 +1231,18 @@ impl<K: DepKind> CurrentDepGraph<K> {
12001231
edges: EdgesVec,
12011232
current_fingerprint: Fingerprint,
12021233
) -> DepNodeIndex {
1203-
let dep_node_index = match self.new_node_to_index.get_shard_by_value(&key).lock().entry(key)
1204-
{
1205-
Entry::Occupied(entry) => *entry.get(),
1206-
Entry::Vacant(entry) => {
1207-
let dep_node_index =
1208-
self.encoder.borrow().send(profiler, key, current_fingerprint, edges);
1209-
entry.insert(dep_node_index);
1210-
dep_node_index
1211-
}
1212-
};
1234+
let dep_node_index = self.encoder.borrow().send(profiler, key, current_fingerprint, edges);
12131235

12141236
#[cfg(debug_assertions)]
12151237
self.record_edge(dep_node_index, key, current_fingerprint);
12161238

1239+
#[cfg(debug_assertions)]
1240+
if let Some(ref seen_dep_nodes) = self.seen_dep_nodes {
1241+
if !seen_dep_nodes.lock().insert(key) {
1242+
panic!("Found duplicate dep-node {key:?}");
1243+
}
1244+
}
1245+
12171246
dep_node_index
12181247
}
12191248

@@ -1297,8 +1326,6 @@ impl<K: DepKind> CurrentDepGraph<K> {
12971326
prev_graph: &SerializedDepGraph<K>,
12981327
prev_index: SerializedDepNodeIndex,
12991328
) -> DepNodeIndex {
1300-
self.debug_assert_not_in_new_nodes(prev_graph, prev_index);
1301-
13021329
let mut prev_index_to_index = self.prev_index_to_index.lock();
13031330

13041331
match prev_index_to_index[prev_index] {
@@ -1319,19 +1346,6 @@ impl<K: DepKind> CurrentDepGraph<K> {
13191346
}
13201347
}
13211348
}
1322-
1323-
#[inline]
1324-
fn debug_assert_not_in_new_nodes(
1325-
&self,
1326-
prev_graph: &SerializedDepGraph<K>,
1327-
prev_index: SerializedDepNodeIndex,
1328-
) {
1329-
let node = &prev_graph.index_to_node(prev_index);
1330-
debug_assert!(
1331-
!self.new_node_to_index.get_shard_by_value(node).lock().contains_key(node),
1332-
"node from previous graph present in new node collection"
1333-
);
1334-
}
13351349
}
13361350

13371351
/// The capacity of the `reads` field `SmallVec`

compiler/rustc_session/src/options.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,9 @@ options! {
15311531
incremental_relative_spans: bool = (false, parse_bool, [TRACKED],
15321532
"hash spans relative to their parent item for incr. comp. (default: no)"),
15331533
incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED],
1534-
"verify incr. comp. hashes of green query instances (default: no)"),
1534+
"verify extended properties for incr. comp. (default: no):
1535+
- hashes of green query instances;
1536+
- hash collisions when creating dep-nodes."),
15351537
inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
15361538
"control whether `#[inline]` functions are in all CGUs"),
15371539
inline_llvm: bool = (true, parse_bool, [TRACKED],

0 commit comments

Comments
 (0)