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,14 +8,15 @@ use std::sync::atomic::{AtomicU32, Ordering};
7
8
8
9
use rustc_data_structures:: fingerprint:: Fingerprint ;
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_index:: IndexVec ;
16
17
use rustc_macros:: { Decodable , Encodable } ;
17
18
use rustc_serialize:: opaque:: { FileEncodeResult , FileEncoder } ;
19
+ use rustc_session:: Session ;
18
20
use tracing:: { debug, instrument} ;
19
21
#[ cfg( debug_assertions) ]
20
22
use { super :: debug:: EdgeFilter , std:: env} ;
@@ -118,7 +120,7 @@ where
118
120
119
121
impl < D : Deps > DepGraph < D > {
120
122
pub fn new (
121
- profiler : & SelfProfilerRef ,
123
+ session : & Session ,
122
124
prev_graph : Arc < SerializedDepGraph > ,
123
125
prev_work_products : WorkProductMap ,
124
126
encoder : FileEncoder ,
@@ -128,7 +130,7 @@ impl<D: Deps> DepGraph<D> {
128
130
let prev_graph_node_count = prev_graph. node_count ( ) ;
129
131
130
132
let current = CurrentDepGraph :: new (
131
- profiler ,
133
+ session ,
132
134
prev_graph_node_count,
133
135
encoder,
134
136
record_graph,
@@ -353,12 +355,13 @@ impl<D: Deps> DepGraphData<D> {
353
355
// in `DepGraph::try_mark_green()`.
354
356
// 2. Two distinct query keys get mapped to the same `DepNode`
355
357
// (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 \
359
361
- query-key: {arg:?}\n \
360
362
- dep-node: {key:?}"
361
- ) ;
363
+ )
364
+ } ) ;
362
365
363
366
let with_deps = |task_deps| D :: with_deps ( task_deps, || task ( cx, arg) ) ;
364
367
let ( result, edges) = if cx. dep_context ( ) . is_eval_always ( key. kind ) {
@@ -438,7 +441,31 @@ impl<D: Deps> DepGraphData<D> {
438
441
hash : self . current . anon_id_seed . combine ( hasher. finish ( ) ) . into ( ) ,
439
442
} ;
440
443
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
+ match self
452
+ . current
453
+ . anon_node_to_index
454
+ . get_shard_by_value ( & target_dep_node)
455
+ . lock ( )
456
+ . entry ( target_dep_node)
457
+ {
458
+ Entry :: Occupied ( entry) => * entry. get ( ) ,
459
+ Entry :: Vacant ( entry) => {
460
+ let dep_node_index = self . current . intern_new_node (
461
+ target_dep_node,
462
+ task_deps,
463
+ Fingerprint :: ZERO ,
464
+ ) ;
465
+ entry. insert ( dep_node_index) ;
466
+ dep_node_index
467
+ }
468
+ }
442
469
}
443
470
} ;
444
471
@@ -613,20 +640,22 @@ impl<D: Deps> DepGraph<D> {
613
640
}
614
641
615
642
impl < D : Deps > DepGraphData < D > {
616
- #[ inline]
617
- fn dep_node_index_of_opt ( & self , dep_node : & DepNode ) -> Option < DepNodeIndex > {
643
+ fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
644
+ & self ,
645
+ dep_node : & DepNode ,
646
+ msg : impl FnOnce ( ) -> S ,
647
+ ) {
618
648
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)
649
+ let current = self . current . prev_index_to_index . lock ( ) [ prev_index] ;
650
+ assert ! ( current. is_none( ) , "{}" , msg( ) )
651
+ } else if let Some ( nodes_newly_allocated_in_current_session) =
652
+ & self . current . nodes_newly_allocated_in_current_session
653
+ {
654
+ let seen = nodes_newly_allocated_in_current_session. lock ( ) . contains ( dep_node) ;
655
+ assert ! ( !seen, "{}" , msg( ) ) ;
622
656
}
623
657
}
624
658
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
-
630
659
fn node_color ( & self , dep_node : & DepNode ) -> Option < DepNodeColor > {
631
660
if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
632
661
self . colors . get ( prev_index)
@@ -659,11 +688,6 @@ impl<D: Deps> DepGraphData<D> {
659
688
}
660
689
661
690
impl < 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
-
667
691
/// Checks whether a previous work product exists for `v` and, if
668
692
/// so, return the path that leads to it. Used to skip doing work.
669
693
pub fn previous_work_product ( & self , v : & WorkProductId ) -> Option < WorkProduct > {
@@ -926,6 +950,16 @@ impl<D: Deps> DepGraph<D> {
926
950
self . node_color ( dep_node) . is_some_and ( |c| c. is_green ( ) )
927
951
}
928
952
953
+ pub fn assert_dep_node_not_yet_allocated_in_current_session < S : std:: fmt:: Display > (
954
+ & self ,
955
+ dep_node : & DepNode ,
956
+ msg : impl FnOnce ( ) -> S ,
957
+ ) {
958
+ if let Some ( data) = & self . data {
959
+ data. assert_dep_node_not_yet_allocated_in_current_session ( dep_node, msg)
960
+ }
961
+ }
962
+
929
963
/// This method loads all on-disk cacheable query results into memory, so
930
964
/// they can be written out to the new cache file again. Most query results
931
965
/// will already be in memory but in the case where we marked something as
@@ -1031,24 +1065,24 @@ rustc_index::newtype_index! {
1031
1065
/// largest in the compiler.
1032
1066
///
1033
1067
/// 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
1068
+ /// keys. The `anon_node_to_index ` map only contains nodes of anonymous queries not in the previous
1035
1069
/// graph, and we map nodes in the previous graph to indices via a two-step
1036
1070
/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
1037
1071
/// and the `prev_index_to_index` vector (which is more compact and faster than
1038
1072
/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
1039
1073
///
1040
- /// This struct uses three locks internally. The `data`, `new_node_to_index `,
1074
+ /// This struct uses three locks internally. The `data`, `anon_node_to_index `,
1041
1075
/// and `prev_index_to_index` fields are locked separately. Operations that take
1042
1076
/// a `DepNodeIndex` typically just access the `data` field.
1043
1077
///
1044
1078
/// 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`
1079
+ /// `anon_node_to_index ` and `data`, or `prev_index_to_index` and `data`. When
1080
+ /// manipulating both, we acquire `anon_node_to_index ` or `prev_index_to_index`
1047
1081
/// first, and `data` second.
1048
1082
pub ( super ) struct CurrentDepGraph < D : Deps > {
1049
1083
encoder : GraphEncoder < D > ,
1050
- new_node_to_index : ShardedHashMap < DepNode , DepNodeIndex > ,
1051
1084
prev_index_to_index : Lock < IndexVec < SerializedDepNodeIndex , Option < DepNodeIndex > > > ,
1085
+ anon_node_to_index : Sharded < FxHashMap < DepNode , DepNodeIndex > > ,
1052
1086
1053
1087
/// This is used to verify that fingerprints do not change between the creation of a node
1054
1088
/// and its recomputation.
@@ -1060,6 +1094,13 @@ pub(super) struct CurrentDepGraph<D: Deps> {
1060
1094
#[ cfg( debug_assertions) ]
1061
1095
forbidden_edge : Option < EdgeFilter > ,
1062
1096
1097
+ /// Used to verify the absence of hash collisions among DepNodes.
1098
+ /// This field is only `Some` if the `-Z incremental_verify_ich` option is present.
1099
+ ///
1100
+ /// The map contains all DepNodes that have been allocated in the current session so far and
1101
+ /// for which there is no equivalent in the previous session.
1102
+ nodes_newly_allocated_in_current_session : Option < Lock < FxHashSet < DepNode > > > ,
1103
+
1063
1104
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
1064
1105
/// their edges. This has the beneficial side-effect that multiple anonymous
1065
1106
/// nodes can be coalesced into one without changing the semantics of the
@@ -1081,7 +1122,7 @@ pub(super) struct CurrentDepGraph<D: Deps> {
1081
1122
1082
1123
impl < D : Deps > CurrentDepGraph < D > {
1083
1124
fn new (
1084
- profiler : & SelfProfilerRef ,
1125
+ session : & Session ,
1085
1126
prev_graph_node_count : usize ,
1086
1127
encoder : FileEncoder ,
1087
1128
record_graph : bool ,
@@ -1113,18 +1154,31 @@ impl<D: Deps> CurrentDepGraph<D> {
1113
1154
prev_graph_node_count,
1114
1155
record_graph,
1115
1156
record_stats,
1116
- profiler ,
1157
+ & session . prof ,
1117
1158
previous,
1118
1159
) ,
1119
- new_node_to_index : ShardedHashMap :: with_capacity (
1120
- new_node_count_estimate / sharded:: shards ( ) ,
1121
- ) ,
1160
+ anon_node_to_index : Sharded :: new ( || {
1161
+ FxHashMap :: with_capacity_and_hasher (
1162
+ new_node_count_estimate / sharded:: shards ( ) ,
1163
+ Default :: default ( ) ,
1164
+ )
1165
+ } ) ,
1122
1166
prev_index_to_index : Lock :: new ( IndexVec :: from_elem_n ( None , prev_graph_node_count) ) ,
1123
1167
anon_id_seed,
1124
1168
#[ cfg( debug_assertions) ]
1125
1169
forbidden_edge,
1126
1170
#[ cfg( debug_assertions) ]
1127
1171
fingerprints : Lock :: new ( IndexVec :: from_elem_n ( None , new_node_count_estimate) ) ,
1172
+ nodes_newly_allocated_in_current_session : session
1173
+ . opts
1174
+ . unstable_opts
1175
+ . incremental_verify_ich
1176
+ . then ( || {
1177
+ Lock :: new ( FxHashSet :: with_capacity_and_hasher (
1178
+ new_node_count_estimate,
1179
+ Default :: default ( ) ,
1180
+ ) )
1181
+ } ) ,
1128
1182
total_read_count : AtomicU64 :: new ( 0 ) ,
1129
1183
total_duplicate_read_count : AtomicU64 :: new ( 0 ) ,
1130
1184
}
@@ -1148,13 +1202,19 @@ impl<D: Deps> CurrentDepGraph<D> {
1148
1202
edges : EdgesVec ,
1149
1203
current_fingerprint : Fingerprint ,
1150
1204
) -> 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) ) ;
1205
+ let dep_node_index = self . encoder . send ( key, current_fingerprint, edges) ;
1154
1206
1155
1207
#[ cfg( debug_assertions) ]
1156
1208
self . record_edge ( dep_node_index, key, current_fingerprint) ;
1157
1209
1210
+ if let Some ( ref nodes_newly_allocated_in_current_session) =
1211
+ self . nodes_newly_allocated_in_current_session
1212
+ {
1213
+ if !nodes_newly_allocated_in_current_session. lock ( ) . insert ( key) {
1214
+ panic ! ( "Found duplicate dep-node {key:?}" ) ;
1215
+ }
1216
+ }
1217
+
1158
1218
dep_node_index
1159
1219
}
1160
1220
@@ -1248,7 +1308,10 @@ impl<D: Deps> CurrentDepGraph<D> {
1248
1308
) {
1249
1309
let node = & prev_graph. index_to_node ( prev_index) ;
1250
1310
debug_assert ! (
1251
- !self . new_node_to_index. get( node) . is_some( ) ,
1311
+ !self
1312
+ . nodes_newly_allocated_in_current_session
1313
+ . as_ref( )
1314
+ . map_or( false , |set| set. lock( ) . contains( node) ) ,
1252
1315
"node from previous graph present in new node collection"
1253
1316
) ;
1254
1317
}
@@ -1370,16 +1433,6 @@ fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepN
1370
1433
}
1371
1434
}
1372
1435
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
- }
1380
- }
1381
- }
1382
-
1383
1436
let dep_node = dep_node. map_or_else (
1384
1437
|| format ! ( "with index {:?}" , dep_node_index) ,
1385
1438
|dep_node| format ! ( "`{:?}`" , dep_node) ,
0 commit comments