@@ -7,14 +7,16 @@ use std::sync::atomic::{AtomicU32, Ordering};
77
88use rustc_data_structures:: fingerprint:: Fingerprint ;
99use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
10- use rustc_data_structures:: profiling:: { QueryInvocationId , SelfProfilerRef } ;
10+ use rustc_data_structures:: outline;
11+ use rustc_data_structures:: profiling:: QueryInvocationId ;
1112use rustc_data_structures:: sharded:: { self , ShardedHashMap } ;
1213use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
1314use rustc_data_structures:: sync:: { AtomicU64 , Lock } ;
1415use rustc_data_structures:: unord:: UnordMap ;
1516use rustc_index:: IndexVec ;
1617use rustc_macros:: { Decodable , Encodable } ;
1718use rustc_serialize:: opaque:: { FileEncodeResult , FileEncoder } ;
19+ use rustc_session:: Session ;
1820use tracing:: { debug, instrument} ;
1921#[ cfg( debug_assertions) ]
2022use { super :: debug:: EdgeFilter , std:: env} ;
@@ -118,7 +120,7 @@ where
118120
119121impl < D : Deps > DepGraph < D > {
120122 pub fn new (
121- profiler : & SelfProfilerRef ,
123+ session : & Session ,
122124 prev_graph : Arc < SerializedDepGraph > ,
123125 prev_work_products : WorkProductMap ,
124126 encoder : FileEncoder ,
@@ -128,7 +130,7 @@ impl<D: Deps> DepGraph<D> {
128130 let prev_graph_node_count = prev_graph. node_count ( ) ;
129131
130132 let current = CurrentDepGraph :: new (
131- profiler ,
133+ session ,
132134 prev_graph_node_count,
133135 encoder,
134136 record_graph,
@@ -139,7 +141,7 @@ impl<D: Deps> DepGraph<D> {
139141 let colors = DepNodeColorMap :: new ( prev_graph_node_count) ;
140142
141143 // Instantiate a dependy-less node only once for anonymous queries.
142- let _green_node_index = current. intern_new_node (
144+ let _green_node_index = current. alloc_new_node (
143145 DepNode { kind : D :: DEP_KIND_NULL , hash : current. anon_id_seed . into ( ) } ,
144146 EdgesVec :: new ( ) ,
145147 Fingerprint :: ZERO ,
@@ -353,12 +355,13 @@ impl<D: Deps> DepGraphData<D> {
353355 // in `DepGraph::try_mark_green()`.
354356 // 2. Two distinct query keys get mapped to the same `DepNode`
355357 // (see for example #48923).
356- assert ! (
357- ! self . dep_node_exists ( & key ) ,
358- "forcing query with already existing `DepNode`\n \
358+ self . assert_dep_node_not_yet_allocated_in_current_session ( & key , || {
359+ format ! (
360+ "forcing query with already existing `DepNode`\n \
359361 - query-key: {arg:?}\n \
360362 - dep-node: {key:?}"
361- ) ;
363+ )
364+ } ) ;
362365
363366 let with_deps = |task_deps| D :: with_deps ( task_deps, || task ( cx, arg) ) ;
364367 let ( result, edges) = if cx. dep_context ( ) . is_eval_always ( key. kind ) {
@@ -438,7 +441,16 @@ impl<D: Deps> DepGraphData<D> {
438441 hash : self . current . anon_id_seed . combine ( hasher. finish ( ) ) . into ( ) ,
439442 } ;
440443
441- self . current . intern_new_node ( target_dep_node, task_deps, Fingerprint :: ZERO )
444+ // The DepNodes generated by the process above are not unique. 2 queries could
445+ // have exactly the same dependencies. However, deserialization does not handle
446+ // duplicated nodes, so we do the deduplication here directly.
447+ //
448+ // As anonymous nodes are a small quantity compared to the full dep-graph, the
449+ // memory impact of this `anon_node_to_index` map remains tolerable, and helps
450+ // us avoid useless growth of the graph with almost-equivalent nodes.
451+ self . current . anon_node_to_index . get_or_insert_with ( target_dep_node, || {
452+ self . current . alloc_new_node ( target_dep_node, task_deps, Fingerprint :: ZERO )
453+ } )
442454 }
443455 } ;
444456
@@ -613,20 +625,24 @@ impl<D: Deps> DepGraph<D> {
613625}
614626
615627impl < D : Deps > DepGraphData < D > {
616- #[ inline]
617- fn dep_node_index_of_opt ( & self , dep_node : & DepNode ) -> Option < DepNodeIndex > {
628+ fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
629+ & self ,
630+ dep_node : & DepNode ,
631+ msg : impl FnOnce ( ) -> S ,
632+ ) {
618633 if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
619- self . current . prev_index_to_index . lock ( ) [ prev_index]
620- } else {
621- self . current . new_node_to_index . get ( dep_node)
634+ let current = self . current . prev_index_to_index . lock ( ) [ prev_index] ;
635+ assert ! ( current. is_none( ) , "{}" , msg( ) )
636+ } else if let Some ( nodes_newly_allocated_in_current_session) =
637+ & self . current . nodes_newly_allocated_in_current_session
638+ {
639+ outline ( || {
640+ let seen = nodes_newly_allocated_in_current_session. lock ( ) . contains_key ( dep_node) ;
641+ assert ! ( !seen, "{}" , msg( ) ) ;
642+ } ) ;
622643 }
623644 }
624645
625- #[ inline]
626- fn dep_node_exists ( & self , dep_node : & DepNode ) -> bool {
627- self . dep_node_index_of_opt ( dep_node) . is_some ( )
628- }
629-
630646 fn node_color ( & self , dep_node : & DepNode ) -> Option < DepNodeColor > {
631647 if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
632648 self . colors . get ( prev_index)
@@ -659,11 +675,6 @@ impl<D: Deps> DepGraphData<D> {
659675}
660676
661677impl < D : Deps > DepGraph < D > {
662- #[ inline]
663- pub fn dep_node_exists ( & self , dep_node : & DepNode ) -> bool {
664- self . data . as_ref ( ) . is_some_and ( |data| data. dep_node_exists ( dep_node) )
665- }
666-
667678 /// Checks whether a previous work product exists for `v` and, if
668679 /// so, return the path that leads to it. Used to skip doing work.
669680 pub fn previous_work_product ( & self , v : & WorkProductId ) -> Option < WorkProduct > {
@@ -926,6 +937,16 @@ impl<D: Deps> DepGraph<D> {
926937 self . node_color ( dep_node) . is_some_and ( |c| c. is_green ( ) )
927938 }
928939
940+ pub fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
941+ & self ,
942+ dep_node : & DepNode ,
943+ msg : impl FnOnce ( ) -> S ,
944+ ) {
945+ if let Some ( data) = & self . data {
946+ data. assert_dep_node_not_yet_allocated_in_current_session ( dep_node, msg)
947+ }
948+ }
949+
929950 /// This method loads all on-disk cacheable query results into memory, so
930951 /// they can be written out to the new cache file again. Most query results
931952 /// will already be in memory but in the case where we marked something as
@@ -1031,24 +1052,24 @@ rustc_index::newtype_index! {
10311052/// largest in the compiler.
10321053///
10331054/// For this reason, we avoid storing `DepNode`s more than once as map
1034- /// keys. The `new_node_to_index ` map only contains nodes not in the previous
1055+ /// keys. The `anon_node_to_index ` map only contains nodes of anonymous queries not in the previous
10351056/// graph, and we map nodes in the previous graph to indices via a two-step
10361057/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
10371058/// and the `prev_index_to_index` vector (which is more compact and faster than
10381059/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
10391060///
1040- /// This struct uses three locks internally. The `data`, `new_node_to_index `,
1061+ /// This struct uses three locks internally. The `data`, `anon_node_to_index `,
10411062/// and `prev_index_to_index` fields are locked separately. Operations that take
10421063/// a `DepNodeIndex` typically just access the `data` field.
10431064///
10441065/// We only need to manipulate at most two locks simultaneously:
1045- /// `new_node_to_index ` and `data`, or `prev_index_to_index` and `data`. When
1046- /// manipulating both, we acquire `new_node_to_index ` or `prev_index_to_index`
1066+ /// `anon_node_to_index ` and `data`, or `prev_index_to_index` and `data`. When
1067+ /// manipulating both, we acquire `anon_node_to_index ` or `prev_index_to_index`
10471068/// first, and `data` second.
10481069pub ( super ) struct CurrentDepGraph < D : Deps > {
10491070 encoder : GraphEncoder < D > ,
1050- new_node_to_index : ShardedHashMap < DepNode , DepNodeIndex > ,
10511071 prev_index_to_index : Lock < IndexVec < SerializedDepNodeIndex , Option < DepNodeIndex > > > ,
1072+ anon_node_to_index : ShardedHashMap < DepNode , DepNodeIndex > ,
10521073
10531074 /// This is used to verify that fingerprints do not change between the creation of a node
10541075 /// and its recomputation.
@@ -1060,6 +1081,14 @@ pub(super) struct CurrentDepGraph<D: Deps> {
10601081 #[ cfg( debug_assertions) ]
10611082 forbidden_edge : Option < EdgeFilter > ,
10621083
1084+ /// Used to verify the absence of hash collisions among DepNodes.
1085+ /// This field is only `Some` if the `-Z incremental_verify_ich` option is present
1086+ /// or if `debug_assertions` are enabled.
1087+ ///
1088+ /// The map contains all DepNodes that have been allocated in the current session so far and
1089+ /// for which there is no equivalent in the previous session.
1090+ nodes_newly_allocated_in_current_session : Option < Lock < FxHashMap < DepNode , DepNodeIndex > > > ,
1091+
10631092 /// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
10641093 /// their edges. This has the beneficial side-effect that multiple anonymous
10651094 /// nodes can be coalesced into one without changing the semantics of the
@@ -1081,7 +1110,7 @@ pub(super) struct CurrentDepGraph<D: Deps> {
10811110
10821111impl < D : Deps > CurrentDepGraph < D > {
10831112 fn new (
1084- profiler : & SelfProfilerRef ,
1113+ session : & Session ,
10851114 prev_graph_node_count : usize ,
10861115 encoder : FileEncoder ,
10871116 record_graph : bool ,
@@ -1107,16 +1136,20 @@ impl<D: Deps> CurrentDepGraph<D> {
11071136
11081137 let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200 ;
11091138
1139+ let new_node_dbg =
1140+ session. opts . unstable_opts . incremental_verify_ich || cfg ! ( debug_assertions) ;
1141+
11101142 CurrentDepGraph {
11111143 encoder : GraphEncoder :: new (
11121144 encoder,
11131145 prev_graph_node_count,
11141146 record_graph,
11151147 record_stats,
1116- profiler ,
1148+ & session . prof ,
11171149 previous,
11181150 ) ,
1119- new_node_to_index : ShardedHashMap :: with_capacity (
1151+ anon_node_to_index : ShardedHashMap :: with_capacity (
1152+ // FIXME: The count estimate is off as anon nodes are only a portion of the nodes.
11201153 new_node_count_estimate / sharded:: shards ( ) ,
11211154 ) ,
11221155 prev_index_to_index : Lock :: new ( IndexVec :: from_elem_n ( None , prev_graph_node_count) ) ,
@@ -1125,6 +1158,12 @@ impl<D: Deps> CurrentDepGraph<D> {
11251158 forbidden_edge,
11261159 #[ cfg( debug_assertions) ]
11271160 fingerprints : Lock :: new ( IndexVec :: from_elem_n ( None , new_node_count_estimate) ) ,
1161+ nodes_newly_allocated_in_current_session : new_node_dbg. then ( || {
1162+ Lock :: new ( FxHashMap :: with_capacity_and_hasher (
1163+ new_node_count_estimate,
1164+ Default :: default ( ) ,
1165+ ) )
1166+ } ) ,
11281167 total_read_count : AtomicU64 :: new ( 0 ) ,
11291168 total_duplicate_read_count : AtomicU64 :: new ( 0 ) ,
11301169 }
@@ -1142,19 +1181,31 @@ impl<D: Deps> CurrentDepGraph<D> {
11421181 /// Writes the node to the current dep-graph and allocates a `DepNodeIndex` for it.
11431182 /// Assumes that this is a node that has no equivalent in the previous dep-graph.
11441183 #[ inline( always) ]
1145- fn intern_new_node (
1184+ fn alloc_new_node (
11461185 & self ,
11471186 key : DepNode ,
11481187 edges : EdgesVec ,
11491188 current_fingerprint : Fingerprint ,
11501189 ) -> DepNodeIndex {
1151- let dep_node_index = self
1152- . new_node_to_index
1153- . get_or_insert_with ( key, || self . encoder . send ( key, current_fingerprint, edges) ) ;
1190+ let dep_node_index = self . encoder . send ( key, current_fingerprint, edges) ;
11541191
11551192 #[ cfg( debug_assertions) ]
11561193 self . record_edge ( dep_node_index, key, current_fingerprint) ;
11571194
1195+ if let Some ( ref nodes_newly_allocated_in_current_session) =
1196+ self . nodes_newly_allocated_in_current_session
1197+ {
1198+ outline ( || {
1199+ if nodes_newly_allocated_in_current_session
1200+ . lock ( )
1201+ . insert ( key, dep_node_index)
1202+ . is_some ( )
1203+ {
1204+ panic ! ( "Found duplicate dep-node {key:?}" ) ;
1205+ }
1206+ } ) ;
1207+ }
1208+
11581209 dep_node_index
11591210 }
11601211
@@ -1209,7 +1260,7 @@ impl<D: Deps> CurrentDepGraph<D> {
12091260 let fingerprint = fingerprint. unwrap_or ( Fingerprint :: ZERO ) ;
12101261
12111262 // This is a new node: it didn't exist in the previous compilation session.
1212- let dep_node_index = self . intern_new_node ( key, edges, fingerprint) ;
1263+ let dep_node_index = self . alloc_new_node ( key, edges, fingerprint) ;
12131264
12141265 ( dep_node_index, None )
12151266 }
@@ -1248,7 +1299,10 @@ impl<D: Deps> CurrentDepGraph<D> {
12481299 ) {
12491300 let node = & prev_graph. index_to_node ( prev_index) ;
12501301 debug_assert ! (
1251- !self . new_node_to_index. get( node) . is_some( ) ,
1302+ !self
1303+ . nodes_newly_allocated_in_current_session
1304+ . as_ref( )
1305+ . map_or( false , |set| set. lock( ) . contains_key( node) ) ,
12521306 "node from previous graph present in new node collection"
12531307 ) ;
12541308 }
@@ -1370,13 +1424,12 @@ fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepN
13701424 }
13711425 }
13721426
1373- if dep_node. is_none ( ) {
1374- // Try to find it among the new nodes
1375- for shard in data. current . new_node_to_index . lock_shards ( ) {
1376- if let Some ( ( node, _) ) = shard. iter ( ) . find ( |( _, index) | * index == dep_node_index) {
1377- dep_node = Some ( * node) ;
1378- break ;
1379- }
1427+ if dep_node. is_none ( )
1428+ && let Some ( nodes) = & data. current . nodes_newly_allocated_in_current_session
1429+ {
1430+ // Try to find it among the nodes allocated so far in this session
1431+ if let Some ( ( node, _) ) = nodes. lock ( ) . iter ( ) . find ( |& ( _, index) | * index == dep_node_index) {
1432+ dep_node = Some ( * node) ;
13801433 }
13811434 }
13821435
0 commit comments