Remove deterministic picking from query cycle handling

This commit is contained in:
John Kåre Alsaker
2026-02-06 14:58:01 +01:00
parent c78a29473a
commit 9f7d502d64
5 changed files with 31 additions and 61 deletions
+1 -7
View File
@@ -4,7 +4,6 @@
use std::sync::Arc;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_hashes::Hash64;
use rustc_hir::def::DefKind;
use rustc_span::Span;
use rustc_span::def_id::DefId;
@@ -23,9 +22,6 @@ pub struct QueryStackFrame<I> {
pub info: I,
pub dep_kind: DepKind,
/// This hash is used to deterministically pick
/// a query to remove cycles in the parallel compiler.
pub hash: Hash64,
pub def_id: Option<DefId>,
/// A def-id that is extracted from a `Ty` in a query key
pub def_id_for_ty_in_cycle: Option<DefId>,
@@ -36,18 +32,16 @@ impl<'tcx> QueryStackFrame<QueryStackDeferred<'tcx>> {
pub fn new(
info: QueryStackDeferred<'tcx>,
dep_kind: DepKind,
hash: Hash64,
def_id: Option<DefId>,
def_id_for_ty_in_cycle: Option<DefId>,
) -> Self {
Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle }
Self { info, def_id, dep_kind, def_id_for_ty_in_cycle }
}
pub fn lift(&self) -> QueryStackFrame<QueryStackFrameExtra> {
QueryStackFrame {
info: self.info.extract(),
dep_kind: self.dep_kind,
hash: self.hash,
def_id: self.def_id,
def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle,
}
-1
View File
@@ -9,7 +9,6 @@ measureme = "12.0.1"
rustc_abi = { path = "../rustc_abi" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_hashes = { path = "../rustc_hashes" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
+29 -42
View File
@@ -209,27 +209,6 @@ fn connected_to_root<'tcx>(
visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited))
}
// Deterministically pick an query from a list
fn pick_query<'a, 'tcx, T, F>(job_map: &QueryJobMap<'tcx>, queries: &'a [T], f: F) -> &'a T
where
F: Fn(&T) -> (Span, QueryJobId),
{
// Deterministically pick an entry point
// FIXME: Sort this instead
queries
.iter()
.min_by_key(|v| {
let (span, query) = f(v);
let hash = job_map.frame_of(query).hash;
// Prefer entry points which have valid spans for nicer error messages
// We add an integer to the tuple ensuring that entry points
// with valid spans are picked first
let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
(span_cmp, hash)
})
.unwrap()
}
/// Looks for query cycles starting from the last query in `jobs`.
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
/// the function return true.
@@ -263,48 +242,56 @@ fn remove_cycle<'tcx>(
}
}
struct EntryPoint {
query_in_cycle: QueryJobId,
waiter: Option<(Span, QueryJobId)>,
}
// Find the queries in the cycle which are
// connected to queries outside the cycle
let entry_points = stack
.iter()
.filter_map(|&(span, query)| {
if job_map.parent_of(query).is_none() {
.filter_map(|&(_, query_in_cycle)| {
if job_map.parent_of(query_in_cycle).is_none() {
// This query is connected to the root (it has no query parent)
Some((span, query, None))
Some(EntryPoint { query_in_cycle, waiter: None })
} else {
let mut waiters = Vec::new();
// Find all the direct waiters who lead to the root
let _ = visit_waiters(job_map, query, |span, waiter| {
let mut waiter_on_cycle = None;
// Find a direct waiter who leads to the root
let _ = visit_waiters(job_map, query_in_cycle, |span, waiter| {
// Mark all the other queries in the cycle as already visited
let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
if connected_to_root(job_map, waiter, &mut visited).is_break() {
waiters.push((span, waiter));
waiter_on_cycle = Some((span, waiter));
ControlFlow::Break(None)
} else {
ControlFlow::Continue(())
}
ControlFlow::Continue(())
});
if waiters.is_empty() {
None
} else {
// Deterministically pick one of the waiters to show to the user
let waiter = *pick_query(job_map, &waiters, |s| *s);
Some((span, query, Some(waiter)))
}
waiter_on_cycle.map(|waiter_on_cycle| EntryPoint {
query_in_cycle,
waiter: Some(waiter_on_cycle),
})
}
})
.collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
.collect::<Vec<EntryPoint>>();
// Deterministically pick an entry point
let (_, entry_point, usage) = pick_query(job_map, &entry_points, |e| (e.0, e.1));
// Pick an entry point, preferring ones with waiters
let entry_point = entry_points
.iter()
.find(|entry_point| entry_point.waiter.is_some())
.unwrap_or(&entry_points[0]);
// Shift the stack so that our entry point is first
let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
let entry_point_pos =
stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle);
if let Some(pos) = entry_point_pos {
stack.rotate_left(pos);
}
let usage = usage.map(|(span, job)| (span, job_map.frame_of(job).clone()));
let usage = entry_point.waiter.map(|(span, job)| (span, job_map.frame_of(job).clone()));
// Create the cycle error
let error = CycleError {
+1 -10
View File
@@ -4,10 +4,8 @@
use std::num::NonZero;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_data_structures::unord::UnordMap;
use rustc_hashes::Hash64;
use rustc_hir::def_id::DefId;
use rustc_hir::limit::Limit;
use rustc_index::Idx;
@@ -319,18 +317,11 @@ pub(crate) fn create_deferred_query_stack_frame<'tcx, Cache>(
{
let kind = vtable.dep_kind;
let hash = tcx.with_stable_hashing_context(|mut hcx| {
let mut hasher = StableHasher::new();
kind.as_usize().hash_stable(&mut hcx, &mut hasher);
key.hash_stable(&mut hcx, &mut hasher);
hasher.finish::<Hash64>()
});
let def_id: Option<DefId> = key.key_as_def_id();
let def_id_for_ty_in_cycle: Option<DefId> = key.def_id_for_ty_in_cycle();
let info = QueryStackDeferred::new((tcx, vtable, key), mk_query_stack_frame_extra);
QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle)
QueryStackFrame::new(info, kind, def_id, def_id_for_ty_in_cycle)
}
pub(crate) fn encode_query_results<'a, 'tcx, Q, C: QueryCache, const FLAGS: QueryFlags>(