@@ -6,18 +6,18 @@ use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams
6
6
use crate :: dep_graph:: { DepGraphData , HasDepContext } ;
7
7
use crate :: ich:: StableHashingContext ;
8
8
use crate :: query:: caches:: QueryCache ;
9
+ #[ cfg( parallel_compiler) ]
10
+ use crate :: query:: job:: QueryLatch ;
9
11
use crate :: query:: job:: { report_cycle, QueryInfo , QueryJob , QueryJobId , QueryJobInfo } ;
10
12
use crate :: query:: { QueryContext , QueryMap , QuerySideEffects , QueryStackFrame } ;
11
13
use crate :: values:: Value ;
12
14
use crate :: HandleCycleError ;
13
15
use rustc_data_structures:: fingerprint:: Fingerprint ;
14
16
use rustc_data_structures:: fx:: FxHashMap ;
15
- #[ cfg( parallel_compiler) ]
16
- use rustc_data_structures:: profiling:: TimingGuard ;
17
- #[ cfg( parallel_compiler) ]
18
- use rustc_data_structures:: sharded:: Sharded ;
19
17
use rustc_data_structures:: stack:: ensure_sufficient_stack;
20
- use rustc_data_structures:: sync:: { Lock , LockGuard } ;
18
+ use rustc_data_structures:: sync:: Lock ;
19
+ #[ cfg( parallel_compiler) ]
20
+ use rustc_data_structures:: { cold_path, sharded:: Sharded } ;
21
21
use rustc_errors:: { DiagnosticBuilder , ErrorGuaranteed , FatalError } ;
22
22
use rustc_session:: Session ;
23
23
use rustc_span:: { Span , DUMMY_SP } ;
@@ -116,7 +116,6 @@ where
116
116
{
117
117
state : & ' tcx QueryState < K , D > ,
118
118
key : K ,
119
- id : QueryJobId ,
120
119
}
121
120
122
121
#[ cold]
@@ -166,81 +165,6 @@ impl<'tcx, K, D: DepKind> JobOwner<'tcx, K, D>
166
165
where
167
166
K : Eq + Hash + Copy ,
168
167
{
169
- /// Either gets a `JobOwner` corresponding the query, allowing us to
170
- /// start executing the query, or returns with the result of the query.
171
- /// This function assumes that `try_get_cached` is already called and returned `lookup`.
172
- /// If the query is executing elsewhere, this will wait for it and return the result.
173
- /// If the query panicked, this will silently panic.
174
- ///
175
- /// This function is inlined because that results in a noticeable speed-up
176
- /// for some compile-time benchmarks.
177
- #[ inline( always) ]
178
- fn try_start < ' b , Qcx > (
179
- qcx : & ' b Qcx ,
180
- state : & ' b QueryState < K , Qcx :: DepKind > ,
181
- mut state_lock : LockGuard < ' b , FxHashMap < K , QueryResult < Qcx :: DepKind > > > ,
182
- span : Span ,
183
- key : K ,
184
- ) -> TryGetJob < ' b , K , D >
185
- where
186
- Qcx : QueryContext + HasDepContext < DepKind = D > ,
187
- {
188
- let lock = & mut * state_lock;
189
- let current_job_id = qcx. current_query_job ( ) ;
190
-
191
- match lock. entry ( key) {
192
- Entry :: Vacant ( entry) => {
193
- let id = qcx. next_job_id ( ) ;
194
- let job = QueryJob :: new ( id, span, current_job_id) ;
195
-
196
- let key = * entry. key ( ) ;
197
- entry. insert ( QueryResult :: Started ( job) ) ;
198
-
199
- let owner = JobOwner { state, id, key } ;
200
- return TryGetJob :: NotYetStarted ( owner) ;
201
- }
202
- Entry :: Occupied ( mut entry) => {
203
- match entry. get_mut ( ) {
204
- #[ cfg( not( parallel_compiler) ) ]
205
- QueryResult :: Started ( job) => {
206
- let id = job. id ;
207
- drop ( state_lock) ;
208
-
209
- // If we are single-threaded we know that we have cycle error,
210
- // so we just return the error.
211
- return TryGetJob :: Cycle ( id. find_cycle_in_stack (
212
- qcx. try_collect_active_jobs ( ) . unwrap ( ) ,
213
- & current_job_id,
214
- span,
215
- ) ) ;
216
- }
217
- #[ cfg( parallel_compiler) ]
218
- QueryResult :: Started ( job) => {
219
- // For parallel queries, we'll block and wait until the query running
220
- // in another thread has completed. Record how long we wait in the
221
- // self-profiler.
222
- let query_blocked_prof_timer = qcx. dep_context ( ) . profiler ( ) . query_blocked ( ) ;
223
-
224
- // Get the latch out
225
- let latch = job. latch ( ) ;
226
-
227
- drop ( state_lock) ;
228
-
229
- // With parallel queries we might just have to wait on some other
230
- // thread.
231
- let result = latch. wait_on ( current_job_id, span) ;
232
-
233
- match result {
234
- Ok ( ( ) ) => TryGetJob :: JobCompleted ( query_blocked_prof_timer) ,
235
- Err ( cycle) => TryGetJob :: Cycle ( cycle) ,
236
- }
237
- }
238
- QueryResult :: Poisoned => FatalError . raise ( ) ,
239
- }
240
- }
241
- }
242
- }
243
-
244
168
/// Completes the query by updating the query cache with the `result`,
245
169
/// signals the waiter and forgets the JobOwner, so it won't poison the query
246
170
fn complete < C > ( self , cache : & C , result : C :: Value , dep_node_index : DepNodeIndex )
@@ -307,25 +231,6 @@ pub(crate) struct CycleError<D: DepKind> {
307
231
pub cycle : Vec < QueryInfo < D > > ,
308
232
}
309
233
310
- /// The result of `try_start`.
311
- enum TryGetJob < ' tcx , K , D >
312
- where
313
- K : Eq + Hash + Copy ,
314
- D : DepKind ,
315
- {
316
- /// The query is not yet started. Contains a guard to the cache eventually used to start it.
317
- NotYetStarted ( JobOwner < ' tcx , K , D > ) ,
318
-
319
- /// The query was already completed.
320
- /// Returns the result of the query and its dep-node index
321
- /// if it succeeded or a cycle error if it failed.
322
- #[ cfg( parallel_compiler) ]
323
- JobCompleted ( TimingGuard < ' tcx > ) ,
324
-
325
- /// Trying to execute the query resulted in a cycle.
326
- Cycle ( CycleError < D > ) ,
327
- }
328
-
329
234
/// Checks if the query is already computed and in the cache.
330
235
/// It returns the shard index and a lock guard to the shard,
331
236
/// which will be used if the query is not in the cache and we need
@@ -346,6 +251,65 @@ where
346
251
}
347
252
}
348
253
254
+ #[ cold]
255
+ #[ inline( never) ]
256
+ #[ cfg( not( parallel_compiler) ) ]
257
+ fn cycle_error < Q , Qcx > (
258
+ query : Q ,
259
+ qcx : Qcx ,
260
+ try_execute : QueryJobId ,
261
+ span : Span ,
262
+ ) -> ( Q :: Value , Option < DepNodeIndex > )
263
+ where
264
+ Q : QueryConfig < Qcx > ,
265
+ Qcx : QueryContext ,
266
+ {
267
+ let error = try_execute. find_cycle_in_stack (
268
+ qcx. try_collect_active_jobs ( ) . unwrap ( ) ,
269
+ & qcx. current_query_job ( ) ,
270
+ span,
271
+ ) ;
272
+ ( mk_cycle ( qcx, error, query. handle_cycle_error ( ) ) , None )
273
+ }
274
+
275
+ #[ inline( always) ]
276
+ #[ cfg( parallel_compiler) ]
277
+ fn wait_for_query < Q , Qcx > (
278
+ query : Q ,
279
+ qcx : Qcx ,
280
+ span : Span ,
281
+ key : Q :: Key ,
282
+ latch : QueryLatch < Qcx :: DepKind > ,
283
+ current : Option < QueryJobId > ,
284
+ ) -> ( Q :: Value , Option < DepNodeIndex > )
285
+ where
286
+ Q : QueryConfig < Qcx > ,
287
+ Qcx : QueryContext ,
288
+ {
289
+ // For parallel queries, we'll block and wait until the query running
290
+ // in another thread has completed. Record how long we wait in the
291
+ // self-profiler.
292
+ let query_blocked_prof_timer = qcx. dep_context ( ) . profiler ( ) . query_blocked ( ) ;
293
+
294
+ // With parallel queries we might just have to wait on some other
295
+ // thread.
296
+ let result = latch. wait_on ( current, span) ;
297
+
298
+ match result {
299
+ Ok ( ( ) ) => {
300
+ let Some ( ( v, index) ) = query. query_cache ( qcx) . lookup ( & key) else {
301
+ cold_path ( || panic ! ( "value must be in cache after waiting" ) )
302
+ } ;
303
+
304
+ qcx. dep_context ( ) . profiler ( ) . query_cache_hit ( index. into ( ) ) ;
305
+ query_blocked_prof_timer. finish_with_query_invocation_id ( index. into ( ) ) ;
306
+
307
+ ( v, Some ( index) )
308
+ }
309
+ Err ( cycle) => ( mk_cycle ( qcx, cycle, query. handle_cycle_error ( ) ) , None ) ,
310
+ }
311
+ }
312
+
349
313
#[ inline( never) ]
350
314
fn try_execute_query < Q , Qcx > (
351
315
query : Q ,
@@ -360,9 +324,9 @@ where
360
324
{
361
325
let state = query. query_state ( qcx) ;
362
326
#[ cfg( parallel_compiler) ]
363
- let state_lock = state. active . get_shard_by_value ( & key) . lock ( ) ;
327
+ let mut state_lock = state. active . get_shard_by_value ( & key) . lock ( ) ;
364
328
#[ cfg( not( parallel_compiler) ) ]
365
- let state_lock = state. active . lock ( ) ;
329
+ let mut state_lock = state. active . lock ( ) ;
366
330
367
331
// For the parallel compiler we need to check both the query cache and query state structures
368
332
// while holding the state lock to ensure that 1) the query has not yet completed and 2) the
@@ -377,44 +341,82 @@ where
377
341
}
378
342
}
379
343
380
- match JobOwner :: < ' _ , Q :: Key , Qcx :: DepKind > :: try_start ( & qcx, state, state_lock, span, key) {
381
- TryGetJob :: NotYetStarted ( job) => {
382
- let ( result, dep_node_index) = match qcx. dep_context ( ) . dep_graph ( ) . data ( ) {
383
- None => execute_job_non_incr ( query, qcx, key, job. id ) ,
384
- Some ( data) => execute_job_incr ( query, qcx, data, key, dep_node, job. id ) ,
385
- } ;
344
+ let current_job_id = qcx. current_query_job ( ) ;
345
+
346
+ match state_lock. entry ( key) {
347
+ Entry :: Vacant ( entry) => {
348
+ // Nothing has computed or is computing the query, so we start a new job and insert it in the
349
+ // state map.
350
+ let id = qcx. next_job_id ( ) ;
351
+ let job = QueryJob :: new ( id, span, current_job_id) ;
352
+ entry. insert ( QueryResult :: Started ( job) ) ;
353
+
354
+ // Drop the lock before we start executing the query
355
+ drop ( state_lock) ;
356
+
357
+ execute_job ( query, qcx, state, key, id, dep_node)
358
+ }
359
+ Entry :: Occupied ( mut entry) => {
360
+ match entry. get_mut ( ) {
361
+ #[ cfg( not( parallel_compiler) ) ]
362
+ QueryResult :: Started ( job) => {
363
+ let id = job. id ;
364
+ drop ( state_lock) ;
365
+
366
+ // If we are single-threaded we know that we have cycle error,
367
+ // so we just return the error.
368
+ cycle_error ( query, qcx, id, span)
369
+ }
370
+ #[ cfg( parallel_compiler) ]
371
+ QueryResult :: Started ( job) => {
372
+ // Get the latch out
373
+ let latch = job. latch ( ) ;
374
+ drop ( state_lock) ;
386
375
387
- let cache = query. query_cache ( qcx) ;
388
- if query. feedable ( ) {
389
- // We should not compute queries that also got a value via feeding.
390
- // This can't happen, as query feeding adds the very dependencies to the fed query
391
- // as its feeding query had. So if the fed query is red, so is its feeder, which will
392
- // get evaluated first, and re-feed the query.
393
- if let Some ( ( cached_result, _) ) = cache. lookup ( & key) {
394
- panic ! (
395
- "fed query later has its value computed. The already cached value: {cached_result:?}"
396
- ) ;
376
+ wait_for_query ( query, qcx, span, key, latch, current_job_id)
397
377
}
378
+ QueryResult :: Poisoned => FatalError . raise ( ) ,
398
379
}
399
- job. complete ( cache, result, dep_node_index) ;
400
- ( result, Some ( dep_node_index) )
401
380
}
402
- TryGetJob :: Cycle ( error) => {
403
- let result = mk_cycle ( qcx, error, query. handle_cycle_error ( ) ) ;
404
- ( result, None )
405
- }
406
- #[ cfg( parallel_compiler) ]
407
- TryGetJob :: JobCompleted ( query_blocked_prof_timer) => {
408
- let Some ( ( v, index) ) = query. query_cache ( qcx) . lookup ( & key) else {
409
- panic ! ( "value must be in cache after waiting" )
410
- } ;
381
+ }
382
+ }
411
383
412
- qcx. dep_context ( ) . profiler ( ) . query_cache_hit ( index. into ( ) ) ;
413
- query_blocked_prof_timer. finish_with_query_invocation_id ( index. into ( ) ) ;
384
+ #[ inline( always) ]
385
+ fn execute_job < Q , Qcx > (
386
+ query : Q ,
387
+ qcx : Qcx ,
388
+ state : & QueryState < Q :: Key , Qcx :: DepKind > ,
389
+ key : Q :: Key ,
390
+ id : QueryJobId ,
391
+ dep_node : Option < DepNode < Qcx :: DepKind > > ,
392
+ ) -> ( Q :: Value , Option < DepNodeIndex > )
393
+ where
394
+ Q : QueryConfig < Qcx > ,
395
+ Qcx : QueryContext ,
396
+ {
397
+ // Use `JobOwner` so the query will be poisoned if executing it panics.
398
+ let job_owner = JobOwner { state, key } ;
414
399
415
- ( v, Some ( index) )
400
+ let ( result, dep_node_index) = match qcx. dep_context ( ) . dep_graph ( ) . data ( ) {
401
+ None => execute_job_non_incr ( query, qcx, key, id) ,
402
+ Some ( data) => execute_job_incr ( query, qcx, data, key, dep_node, id) ,
403
+ } ;
404
+
405
+ let cache = query. query_cache ( qcx) ;
406
+ if query. feedable ( ) {
407
+ // We should not compute queries that also got a value via feeding.
408
+ // This can't happen, as query feeding adds the very dependencies to the fed query
409
+ // as its feeding query had. So if the fed query is red, so is its feeder, which will
410
+ // get evaluated first, and re-feed the query.
411
+ if let Some ( ( cached_result, _) ) = cache. lookup ( & key) {
412
+ panic ! (
413
+ "fed query later has its value computed. The already cached value: {cached_result:?}"
414
+ ) ;
416
415
}
417
416
}
417
+ job_owner. complete ( cache, result, dep_node_index) ;
418
+
419
+ ( result, Some ( dep_node_index) )
418
420
}
419
421
420
422
// Fast path for when incr. comp. is off.
0 commit comments