1
1
use std:: assert_matches:: assert_matches;
2
+ use std:: collections:: hash_map:: Entry ;
2
3
use std:: fmt:: Debug ;
3
4
use std:: hash:: Hash ;
4
5
use std:: marker:: PhantomData ;
@@ -7,15 +8,16 @@ use std::sync::atomic::{AtomicU32, Ordering};
7
8
8
9
use rustc_data_structures:: fingerprint:: { Fingerprint , PackedFingerprint } ;
9
10
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
10
- use rustc_data_structures:: profiling:: { QueryInvocationId , SelfProfilerRef } ;
11
- use rustc_data_structures:: sharded:: { self , ShardedHashMap } ;
11
+ use rustc_data_structures:: profiling:: QueryInvocationId ;
12
+ use rustc_data_structures:: sharded:: { self , Sharded } ;
12
13
use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
13
14
use rustc_data_structures:: sync:: { AtomicU64 , Lock } ;
14
15
use rustc_data_structures:: unord:: UnordMap ;
15
16
use rustc_errors:: DiagInner ;
16
17
use rustc_index:: IndexVec ;
17
18
use rustc_macros:: { Decodable , Encodable } ;
18
19
use rustc_serialize:: opaque:: { FileEncodeResult , FileEncoder } ;
20
+ use rustc_session:: Session ;
19
21
use tracing:: { debug, instrument} ;
20
22
#[ cfg( debug_assertions) ]
21
23
use { super :: debug:: EdgeFilter , std:: env} ;
@@ -117,7 +119,7 @@ where
117
119
118
120
impl < D : Deps > DepGraph < D > {
119
121
pub fn new (
120
- profiler : & SelfProfilerRef ,
122
+ session : & Session ,
121
123
prev_graph : Arc < SerializedDepGraph > ,
122
124
prev_work_products : WorkProductMap ,
123
125
encoder : FileEncoder ,
@@ -127,7 +129,7 @@ impl<D: Deps> DepGraph<D> {
127
129
let prev_graph_node_count = prev_graph. node_count ( ) ;
128
130
129
131
let current = CurrentDepGraph :: new (
130
- profiler ,
132
+ session ,
131
133
prev_graph_node_count,
132
134
encoder,
133
135
record_graph,
@@ -351,12 +353,13 @@ impl<D: Deps> DepGraphData<D> {
351
353
// in `DepGraph::try_mark_green()`.
352
354
// 2. Two distinct query keys get mapped to the same `DepNode`
353
355
// (see for example #48923).
354
- assert ! (
355
- ! self . dep_node_exists ( & key ) ,
356
- "forcing query with already existing `DepNode`\n \
356
+ self . assert_dep_node_not_yet_allocated_in_current_session ( & key , || {
357
+ format ! (
358
+ "forcing query with already existing `DepNode`\n \
357
359
- query-key: {arg:?}\n \
358
360
- dep-node: {key:?}"
359
- ) ;
361
+ )
362
+ } ) ;
360
363
361
364
let with_deps = |task_deps| D :: with_deps ( task_deps, || task ( cx, arg) ) ;
362
365
let ( result, edges) = if cx. dep_context ( ) . is_eval_always ( key. kind ) {
@@ -436,7 +439,31 @@ impl<D: Deps> DepGraphData<D> {
436
439
hash : self . current . anon_id_seed . combine ( hasher. finish ( ) ) . into ( ) ,
437
440
} ;
438
441
439
- self . current . intern_new_node ( target_dep_node, task_deps, Fingerprint :: ZERO )
442
+ // The DepNodes generated by the process above are not unique. 2 queries could
443
+ // have exactly the same dependencies. However, deserialization does not handle
444
+ // duplicated nodes, so we do the deduplication here directly.
445
+ //
446
+ // As anonymous nodes are a small quantity compared to the full dep-graph, the
447
+ // memory impact of this `anon_node_to_index` map remains tolerable, and helps
448
+ // us avoid useless growth of the graph with almost-equivalent nodes.
449
+ match self
450
+ . current
451
+ . anon_node_to_index
452
+ . get_shard_by_value ( & target_dep_node)
453
+ . lock ( )
454
+ . entry ( target_dep_node)
455
+ {
456
+ Entry :: Occupied ( entry) => * entry. get ( ) ,
457
+ Entry :: Vacant ( entry) => {
458
+ let dep_node_index = self . current . intern_new_node (
459
+ target_dep_node,
460
+ task_deps,
461
+ Fingerprint :: ZERO ,
462
+ ) ;
463
+ entry. insert ( dep_node_index) ;
464
+ dep_node_index
465
+ }
466
+ }
440
467
}
441
468
} ;
442
469
@@ -637,20 +664,22 @@ impl<D: Deps> DepGraph<D> {
637
664
}
638
665
639
666
impl < D : Deps > DepGraphData < D > {
640
- #[ inline]
641
- fn dep_node_index_of_opt ( & self , dep_node : & DepNode ) -> Option < DepNodeIndex > {
667
+ fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
668
+ & self ,
669
+ dep_node : & DepNode ,
670
+ msg : impl FnOnce ( ) -> S ,
671
+ ) {
642
672
if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
643
- self . current . prev_index_to_index . lock ( ) [ prev_index]
644
- } else {
645
- self . current . new_node_to_index . get ( dep_node)
673
+ let current = self . current . prev_index_to_index . lock ( ) [ prev_index] ;
674
+ assert ! ( current. is_none( ) , "{}" , msg( ) )
675
+ } else if let Some ( nodes_newly_allocated_in_current_session) =
676
+ & self . current . nodes_newly_allocated_in_current_session
677
+ {
678
+ let seen = nodes_newly_allocated_in_current_session. lock ( ) . contains ( dep_node) ;
679
+ assert ! ( !seen, "{}" , msg( ) ) ;
646
680
}
647
681
}
648
682
649
- #[ inline]
650
- fn dep_node_exists ( & self , dep_node : & DepNode ) -> bool {
651
- self . dep_node_index_of_opt ( dep_node) . is_some ( )
652
- }
653
-
654
683
fn node_color ( & self , dep_node : & DepNode ) -> Option < DepNodeColor > {
655
684
if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
656
685
self . colors . get ( prev_index)
@@ -734,11 +763,6 @@ impl<D: Deps> DepGraphData<D> {
734
763
}
735
764
736
765
impl < D : Deps > DepGraph < D > {
737
- #[ inline]
738
- pub fn dep_node_exists ( & self , dep_node : & DepNode ) -> bool {
739
- self . data . as_ref ( ) . is_some_and ( |data| data. dep_node_exists ( dep_node) )
740
- }
741
-
742
766
/// Checks whether a previous work product exists for `v` and, if
743
767
/// so, return the path that leads to it. Used to skip doing work.
744
768
pub fn previous_work_product ( & self , v : & WorkProductId ) -> Option < WorkProduct > {
@@ -964,6 +988,16 @@ impl<D: Deps> DepGraph<D> {
964
988
self . node_color ( dep_node) . is_some_and ( |c| c. is_green ( ) )
965
989
}
966
990
991
+ pub fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
992
+ & self ,
993
+ dep_node : & DepNode ,
994
+ msg : impl FnOnce ( ) -> S ,
995
+ ) {
996
+ if let Some ( data) = & self . data {
997
+ data. assert_dep_node_not_yet_allocated_in_current_session ( dep_node, msg)
998
+ }
999
+ }
1000
+
967
1001
/// This method loads all on-disk cacheable query results into memory, so
968
1002
/// they can be written out to the new cache file again. Most query results
969
1003
/// will already be in memory but in the case where we marked something as
@@ -1069,24 +1103,24 @@ rustc_index::newtype_index! {
1069
1103
/// largest in the compiler.
1070
1104
///
1071
1105
/// For this reason, we avoid storing `DepNode`s more than once as map
1072
- /// keys. The `new_node_to_index ` map only contains nodes not in the previous
1106
+ /// keys. The `anon_node_to_index ` map only contains nodes of anonymous queries not in the previous
1073
1107
/// graph, and we map nodes in the previous graph to indices via a two-step
1074
1108
/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
1075
1109
/// and the `prev_index_to_index` vector (which is more compact and faster than
1076
1110
/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
1077
1111
///
1078
- /// This struct uses three locks internally. The `data`, `new_node_to_index `,
1112
+ /// This struct uses three locks internally. The `data`, `anon_node_to_index `,
1079
1113
/// and `prev_index_to_index` fields are locked separately. Operations that take
1080
1114
/// a `DepNodeIndex` typically just access the `data` field.
1081
1115
///
1082
1116
/// We only need to manipulate at most two locks simultaneously:
1083
- /// `new_node_to_index ` and `data`, or `prev_index_to_index` and `data`. When
1084
- /// manipulating both, we acquire `new_node_to_index ` or `prev_index_to_index`
1117
+ /// `anon_node_to_index ` and `data`, or `prev_index_to_index` and `data`. When
1118
+ /// manipulating both, we acquire `anon_node_to_index ` or `prev_index_to_index`
1085
1119
/// first, and `data` second.
1086
1120
pub ( super ) struct CurrentDepGraph < D : Deps > {
1087
1121
encoder : GraphEncoder < D > ,
1088
- new_node_to_index : ShardedHashMap < DepNode , DepNodeIndex > ,
1089
1122
prev_index_to_index : Lock < IndexVec < SerializedDepNodeIndex , Option < DepNodeIndex > > > ,
1123
+ anon_node_to_index : Sharded < FxHashMap < DepNode , DepNodeIndex > > ,
1090
1124
1091
1125
/// This is used to verify that fingerprints do not change between the creation of a node
1092
1126
/// and its recomputation.
@@ -1098,6 +1132,13 @@ pub(super) struct CurrentDepGraph<D: Deps> {
1098
1132
#[ cfg( debug_assertions) ]
1099
1133
forbidden_edge : Option < EdgeFilter > ,
1100
1134
1135
+ /// Used to verify the absence of hash collisions among DepNodes.
1136
+ /// This field is only `Some` if the `-Z incremental_verify_ich` option is present.
1137
+ ///
1138
+ /// The map contains all DepNodes that have been allocated in the current session so far and
1139
+ /// for which there is no equivalent in the previous session.
1140
+ nodes_newly_allocated_in_current_session : Option < Lock < FxHashSet < DepNode > > > ,
1141
+
1101
1142
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
1102
1143
/// their edges. This has the beneficial side-effect that multiple anonymous
1103
1144
/// nodes can be coalesced into one without changing the semantics of the
@@ -1119,7 +1160,7 @@ pub(super) struct CurrentDepGraph<D: Deps> {
1119
1160
1120
1161
impl < D : Deps > CurrentDepGraph < D > {
1121
1162
fn new (
1122
- profiler : & SelfProfilerRef ,
1163
+ session : & Session ,
1123
1164
prev_graph_node_count : usize ,
1124
1165
encoder : FileEncoder ,
1125
1166
record_graph : bool ,
@@ -1151,18 +1192,31 @@ impl<D: Deps> CurrentDepGraph<D> {
1151
1192
prev_graph_node_count,
1152
1193
record_graph,
1153
1194
record_stats,
1154
- profiler ,
1195
+ & session . prof ,
1155
1196
previous,
1156
1197
) ,
1157
- new_node_to_index : ShardedHashMap :: with_capacity (
1158
- new_node_count_estimate / sharded:: shards ( ) ,
1159
- ) ,
1198
+ anon_node_to_index : Sharded :: new ( || {
1199
+ FxHashMap :: with_capacity_and_hasher (
1200
+ new_node_count_estimate / sharded:: shards ( ) ,
1201
+ Default :: default ( ) ,
1202
+ )
1203
+ } ) ,
1160
1204
prev_index_to_index : Lock :: new ( IndexVec :: from_elem_n ( None , prev_graph_node_count) ) ,
1161
1205
anon_id_seed,
1162
1206
#[ cfg( debug_assertions) ]
1163
1207
forbidden_edge,
1164
1208
#[ cfg( debug_assertions) ]
1165
1209
fingerprints : Lock :: new ( IndexVec :: from_elem_n ( None , new_node_count_estimate) ) ,
1210
+ nodes_newly_allocated_in_current_session : session
1211
+ . opts
1212
+ . unstable_opts
1213
+ . incremental_verify_ich
1214
+ . then ( || {
1215
+ Lock :: new ( FxHashSet :: with_capacity_and_hasher (
1216
+ new_node_count_estimate,
1217
+ Default :: default ( ) ,
1218
+ ) )
1219
+ } ) ,
1166
1220
total_read_count : AtomicU64 :: new ( 0 ) ,
1167
1221
total_duplicate_read_count : AtomicU64 :: new ( 0 ) ,
1168
1222
}
@@ -1186,13 +1240,19 @@ impl<D: Deps> CurrentDepGraph<D> {
1186
1240
edges : EdgesVec ,
1187
1241
current_fingerprint : Fingerprint ,
1188
1242
) -> DepNodeIndex {
1189
- let dep_node_index = self
1190
- . new_node_to_index
1191
- . get_or_insert_with ( key, || self . encoder . send ( key, current_fingerprint, edges) ) ;
1243
+ let dep_node_index = self . encoder . send ( key, current_fingerprint, edges) ;
1192
1244
1193
1245
#[ cfg( debug_assertions) ]
1194
1246
self . record_edge ( dep_node_index, key, current_fingerprint) ;
1195
1247
1248
+ if let Some ( ref nodes_newly_allocated_in_current_session) =
1249
+ self . nodes_newly_allocated_in_current_session
1250
+ {
1251
+ if !nodes_newly_allocated_in_current_session. lock ( ) . insert ( key) {
1252
+ panic ! ( "Found duplicate dep-node {key:?}" ) ;
1253
+ }
1254
+ }
1255
+
1196
1256
dep_node_index
1197
1257
}
1198
1258
@@ -1286,7 +1346,10 @@ impl<D: Deps> CurrentDepGraph<D> {
1286
1346
) {
1287
1347
let node = & prev_graph. index_to_node ( prev_index) ;
1288
1348
debug_assert ! (
1289
- !self . new_node_to_index. get( node) . is_some( ) ,
1349
+ !self
1350
+ . nodes_newly_allocated_in_current_session
1351
+ . as_ref( )
1352
+ . map_or( false , |set| set. lock( ) . contains( node) ) ,
1290
1353
"node from previous graph present in new node collection"
1291
1354
) ;
1292
1355
}
@@ -1408,16 +1471,6 @@ fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepN
1408
1471
}
1409
1472
}
1410
1473
1411
- if dep_node. is_none ( ) {
1412
- // Try to find it among the new nodes
1413
- for shard in data. current . new_node_to_index . lock_shards ( ) {
1414
- if let Some ( ( node, _) ) = shard. iter ( ) . find ( |( _, index) | * index == dep_node_index) {
1415
- dep_node = Some ( * node) ;
1416
- break ;
1417
- }
1418
- }
1419
- }
1420
-
1421
1474
let dep_node = dep_node. map_or_else (
1422
1475
|| format ! ( "with index {:?}" , dep_node_index) ,
1423
1476
|dep_node| format ! ( "`{:?}`" , dep_node) ,
0 commit comments