@@ -9,6 +9,7 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
9
9
use rustc_data_structures:: unord:: UnordMap ;
10
10
use rustc_index:: IndexVec ;
11
11
use rustc_serialize:: opaque:: { FileEncodeResult , FileEncoder } ;
12
+ use rustc_session:: Session ;
12
13
use smallvec:: { smallvec, SmallVec } ;
13
14
use std:: assert_matches:: assert_matches;
14
15
use std:: collections:: hash_map:: Entry ;
@@ -115,7 +116,7 @@ where
115
116
116
117
impl < K : DepKind > DepGraph < K > {
117
118
pub fn new (
118
- profiler : & SelfProfilerRef ,
119
+ session : & Session ,
119
120
prev_graph : SerializedDepGraph < K > ,
120
121
prev_work_products : FxIndexMap < WorkProductId , WorkProduct > ,
121
122
encoder : FileEncoder ,
@@ -125,7 +126,7 @@ impl<K: DepKind> DepGraph<K> {
125
126
let prev_graph_node_count = prev_graph. node_count ( ) ;
126
127
127
128
let current = CurrentDepGraph :: new (
128
- profiler ,
129
+ session ,
129
130
prev_graph_node_count,
130
131
encoder,
131
132
record_graph,
@@ -136,7 +137,7 @@ impl<K: DepKind> DepGraph<K> {
136
137
137
138
// Instantiate a dependy-less node only once for anonymous queries.
138
139
let _green_node_index = current. intern_new_node (
139
- profiler ,
140
+ & session . prof ,
140
141
DepNode { kind : DepKind :: NULL , hash : current. anon_id_seed . into ( ) } ,
141
142
smallvec ! [ ] ,
142
143
Fingerprint :: ZERO ,
@@ -145,7 +146,7 @@ impl<K: DepKind> DepGraph<K> {
145
146
146
147
// Instantiate a dependy-less red node only once for anonymous queries.
147
148
let ( red_node_index, red_node_prev_index_and_color) = current. intern_node (
148
- profiler ,
149
+ & session . prof ,
149
150
& prev_graph,
150
151
DepNode { kind : DepKind :: RED , hash : Fingerprint :: ZERO . into ( ) } ,
151
152
smallvec ! [ ] ,
@@ -343,17 +344,13 @@ impl<K: DepKind> DepGraphData<K> {
343
344
task : fn ( Ctxt , A ) -> R ,
344
345
hash_result : Option < fn ( & mut StableHashingContext < ' _ > , & R ) -> Fingerprint > ,
345
346
) -> ( 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
+ } ) ;
357
354
358
355
let with_deps = |task_deps| K :: with_deps ( task_deps, || task ( cx, arg) ) ;
359
356
let ( result, edges) = if cx. dep_context ( ) . is_eval_always ( key. kind ) {
@@ -449,12 +446,32 @@ impl<K: DepKind> DepGraphData<K> {
449
446
hash : self . current . anon_id_seed . combine ( hasher. finish ( ) ) . into ( ) ,
450
447
} ;
451
448
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
+ }
458
475
}
459
476
} ;
460
477
@@ -625,25 +642,18 @@ impl<K: DepKind> DepGraph<K> {
625
642
}
626
643
627
644
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( ) ) ;
639
654
}
640
655
}
641
656
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
-
647
657
fn node_color ( & self , dep_node : & DepNode < K > ) -> Option < DepNodeColor > {
648
658
if let Some ( prev_index) = self . previous . node_to_index_opt ( dep_node) {
649
659
self . colors . get ( prev_index)
@@ -676,11 +686,6 @@ impl<K: DepKind> DepGraphData<K> {
676
686
}
677
687
678
688
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
-
684
689
/// Checks whether a previous work product exists for `v` and, if
685
690
/// so, return the path that leads to it. Used to skip doing work.
686
691
pub fn previous_work_product ( & self , v : & WorkProductId ) -> Option < WorkProduct > {
@@ -762,7 +767,7 @@ impl<K: DepKind> DepGraphData<K> {
762
767
}
763
768
}
764
769
765
- #[ instrument( skip( self , qcx, parent_dep_node_index , frame) , level = "debug" ) ]
770
+ #[ instrument( skip( self , qcx, frame) , level = "debug" ) ]
766
771
fn try_mark_parent_green < Qcx : QueryContext < DepKind = K > > (
767
772
& self ,
768
773
qcx : Qcx ,
@@ -861,10 +866,11 @@ impl<K: DepKind> DepGraphData<K> {
861
866
let frame = MarkFrame { index : prev_dep_node_index, parent : frame } ;
862
867
863
868
#[ 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( ) ) ;
868
874
869
875
// We never try to mark eval_always nodes as green
870
876
debug_assert ! ( !qcx. dep_context( ) . is_eval_always( dep_node. kind) ) ;
@@ -959,6 +965,18 @@ impl<K: DepKind> DepGraph<K> {
959
965
self . node_color ( dep_node) . is_some_and ( |c| c. is_green ( ) )
960
966
}
961
967
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
+
962
980
/// This method loads all on-disk cacheable query results into memory, so
963
981
/// they can be written out to the new cache file again. Most query results
964
982
/// will already be in memory but in the case where we marked something as
@@ -1066,24 +1084,24 @@ rustc_index::newtype_index! {
1066
1084
/// largest in the compiler.
1067
1085
///
1068
1086
/// 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
1070
1088
/// graph, and we map nodes in the previous graph to indices via a two-step
1071
1089
/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
1072
1090
/// and the `prev_index_to_index` vector (which is more compact and faster than
1073
1091
/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
1074
1092
///
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 `,
1076
1094
/// and `prev_index_to_index` fields are locked separately. Operations that take
1077
1095
/// a `DepNodeIndex` typically just access the `data` field.
1078
1096
///
1079
1097
/// 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`
1082
1100
/// first, and `data` second.
1083
1101
pub ( super ) struct CurrentDepGraph < K : DepKind > {
1084
1102
encoder : Steal < GraphEncoder < K > > ,
1085
- new_node_to_index : Sharded < FxHashMap < DepNode < K > , DepNodeIndex > > ,
1086
1103
prev_index_to_index : Lock < IndexVec < SerializedDepNodeIndex , Option < DepNodeIndex > > > ,
1104
+ anon_node_to_index : Sharded < FxHashMap < DepNode < K > , DepNodeIndex > > ,
1087
1105
1088
1106
/// This is used to verify that fingerprints do not change between the creation of a node
1089
1107
/// and its recomputation.
@@ -1095,6 +1113,11 @@ pub(super) struct CurrentDepGraph<K: DepKind> {
1095
1113
#[ cfg( debug_assertions) ]
1096
1114
forbidden_edge : Option < EdgeFilter < K > > ,
1097
1115
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
+
1098
1121
/// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
1099
1122
/// their edges. This has the beneficial side-effect that multiple anonymous
1100
1123
/// nodes can be coalesced into one without changing the semantics of the
@@ -1122,7 +1145,7 @@ pub(super) struct CurrentDepGraph<K: DepKind> {
1122
1145
1123
1146
impl < K : DepKind > CurrentDepGraph < K > {
1124
1147
fn new (
1125
- profiler : & SelfProfilerRef ,
1148
+ session : & Session ,
1126
1149
prev_graph_node_count : usize ,
1127
1150
encoder : FileEncoder ,
1128
1151
record_graph : bool ,
@@ -1152,7 +1175,8 @@ impl<K: DepKind> CurrentDepGraph<K> {
1152
1175
1153
1176
let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200 ;
1154
1177
1155
- let node_intern_event_id = profiler
1178
+ let node_intern_event_id = session
1179
+ . prof
1156
1180
. get_or_alloc_cached_string ( "incr_comp_intern_dep_graph_node" )
1157
1181
. map ( EventId :: from_label) ;
1158
1182
@@ -1163,7 +1187,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
1163
1187
record_graph,
1164
1188
record_stats,
1165
1189
) ) ,
1166
- new_node_to_index : Sharded :: new ( || {
1190
+ anon_node_to_index : Sharded :: new ( || {
1167
1191
FxHashMap :: with_capacity_and_hasher (
1168
1192
new_node_count_estimate / sharded:: SHARDS ,
1169
1193
Default :: default ( ) ,
@@ -1175,6 +1199,13 @@ impl<K: DepKind> CurrentDepGraph<K> {
1175
1199
forbidden_edge,
1176
1200
#[ cfg( debug_assertions) ]
1177
1201
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
+ } ) ,
1178
1209
total_read_count : AtomicU64 :: new ( 0 ) ,
1179
1210
total_duplicate_read_count : AtomicU64 :: new ( 0 ) ,
1180
1211
node_intern_event_id,
@@ -1200,20 +1231,18 @@ impl<K: DepKind> CurrentDepGraph<K> {
1200
1231
edges : EdgesVec ,
1201
1232
current_fingerprint : Fingerprint ,
1202
1233
) -> 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) ;
1213
1235
1214
1236
#[ cfg( debug_assertions) ]
1215
1237
self . record_edge ( dep_node_index, key, current_fingerprint) ;
1216
1238
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
+
1217
1246
dep_node_index
1218
1247
}
1219
1248
@@ -1297,8 +1326,6 @@ impl<K: DepKind> CurrentDepGraph<K> {
1297
1326
prev_graph : & SerializedDepGraph < K > ,
1298
1327
prev_index : SerializedDepNodeIndex ,
1299
1328
) -> DepNodeIndex {
1300
- self . debug_assert_not_in_new_nodes ( prev_graph, prev_index) ;
1301
-
1302
1329
let mut prev_index_to_index = self . prev_index_to_index . lock ( ) ;
1303
1330
1304
1331
match prev_index_to_index[ prev_index] {
@@ -1319,19 +1346,6 @@ impl<K: DepKind> CurrentDepGraph<K> {
1319
1346
}
1320
1347
}
1321
1348
}
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
- }
1335
1349
}
1336
1350
1337
1351
/// The capacity of the `reads` field `SmallVec`
0 commit comments