Skip to content

Commit e380efa

Browse files
committed
Auto merge of #67471 - nnethercote:revert-66405, r=nikomatsakis
Revert parts of #66405. Because PR #66405 caused major performance regressions in some cases. That PR had five commits, two of which affected performance, and three of which were refactorings. This change undoes the performance-affecting changes, while keeping the refactorings in place. Fixes #67454. r? @nikomatsakis
2 parents 9e6fb53 + 18a3669 commit e380efa

File tree

1 file changed

+80
-111
lines changed
  • src/librustc_data_structures/obligation_forest

1 file changed

+80
-111
lines changed

src/librustc_data_structures/obligation_forest/mod.rs

+80-111
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,14 @@ type ObligationTreeIdGenerator =
129129

130130
pub struct ObligationForest<O: ForestObligation> {
131131
/// The list of obligations. In between calls to `process_obligations`,
132-
/// this list only contains nodes in the `Pending` or `Success` state.
132+
/// this list only contains nodes in the `Pending` or `Waiting` state.
133133
///
134134
/// `usize` indices are used here and throughout this module, rather than
135135
/// `rustc_index::newtype_index!` indices, because this code is hot enough
136136
/// that the `u32`-to-`usize` conversions that would be required are
137137
/// significant, and space considerations are not important.
138138
nodes: Vec<Node<O>>,
139139

140-
/// The process generation is 1 on the first call to `process_obligations`,
141-
/// 2 on the second call, etc.
142-
gen: u32,
143-
144140
/// A cache of predicates that have been successfully completed.
145141
done_cache: FxHashSet<O::Predicate>,
146142

@@ -196,9 +192,9 @@ impl<O> Node<O> {
196192
}
197193
}
198194

199-
/// The state of one node in some tree within the forest. This
200-
/// represents the current state of processing for the obligation (of
201-
/// type `O`) associated with this node.
195+
/// The state of one node in some tree within the forest. This represents the
196+
/// current state of processing for the obligation (of type `O`) associated
197+
/// with this node.
202198
///
203199
/// The non-`Error` state transitions are as follows.
204200
/// ```
@@ -210,51 +206,47 @@ impl<O> Node<O> {
210206
/// |
211207
/// | process_obligations()
212208
/// v
213-
/// Success(not_waiting())
214-
/// | |
215-
/// | | mark_still_waiting_nodes()
209+
/// Success
210+
/// | ^
211+
/// | | mark_successes()
216212
/// | v
217-
/// | Success(still_waiting())
218-
/// | |
219-
/// | | compress()
220-
/// v v
213+
/// | Waiting
214+
/// |
215+
/// | process_cycles()
216+
/// v
217+
/// Done
218+
/// |
219+
/// | compress()
220+
/// v
221221
/// (Removed)
222222
/// ```
223223
/// The `Error` state can be introduced in several places, via `error_at()`.
224224
///
225225
/// Outside of `ObligationForest` methods, nodes should be either `Pending` or
226-
/// `Success`.
226+
/// `Waiting`.
227227
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
228228
enum NodeState {
229229
/// This obligation has not yet been selected successfully. Cannot have
230230
/// subobligations.
231231
Pending,
232232

233-
/// This obligation was selected successfully, but it may be waiting on one
234-
/// or more pending subobligations, as indicated by the `WaitingState`.
235-
Success(WaitingState),
233+
/// This obligation was selected successfully, but may or may not have
234+
/// subobligations.
235+
Success,
236+
237+
/// This obligation was selected successfully, but it has a pending
238+
/// subobligation.
239+
Waiting,
240+
241+
/// This obligation, along with its subobligations, are complete, and will
242+
/// be removed in the next collection.
243+
Done,
236244

237245
/// This obligation was resolved to an error. It will be removed by the
238246
/// next compression step.
239247
Error,
240248
}
241249

242-
/// Indicates when a `Success` node was last (if ever) waiting on one or more
243-
/// `Pending` nodes. The notion of "when" comes from `ObligationForest::gen`.
244-
/// - 0: "Not waiting". This is a special value, set by `process_obligation`,
245-
/// and usable because generation counting starts at 1.
246-
/// - 1..ObligationForest::gen: "Was waiting" in a previous generation, but
247-
/// waiting no longer. In other words, finished.
248-
/// - ObligationForest::gen: "Still waiting" in this generation.
249-
///
250-
/// Things to note about this encoding:
251-
/// - Every time `ObligationForest::gen` is incremented, all the "still
252-
/// waiting" nodes automatically become "was waiting".
253-
/// - `ObligationForest::is_still_waiting` is very cheap.
254-
///
255-
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
256-
struct WaitingState(u32);
257-
258250
#[derive(Debug)]
259251
pub struct Outcome<O, E> {
260252
/// Obligations that were completely evaluated, including all
@@ -291,7 +283,6 @@ impl<O: ForestObligation> ObligationForest<O> {
291283
pub fn new() -> ObligationForest<O> {
292284
ObligationForest {
293285
nodes: vec![],
294-
gen: 0,
295286
done_cache: Default::default(),
296287
active_cache: Default::default(),
297288
node_rewrites: RefCell::new(vec![]),
@@ -392,18 +383,6 @@ impl<O: ForestObligation> ObligationForest<O> {
392383
.insert(node.obligation.as_predicate().clone());
393384
}
394385

395-
fn not_waiting() -> WaitingState {
396-
WaitingState(0)
397-
}
398-
399-
fn still_waiting(&self) -> WaitingState {
400-
WaitingState(self.gen)
401-
}
402-
403-
fn is_still_waiting(&self, waiting: WaitingState) -> bool {
404-
waiting.0 == self.gen
405-
}
406-
407386
/// Performs a pass through the obligation list. This must
408387
/// be called in a loop until `outcome.stalled` is false.
409388
///
@@ -416,8 +395,6 @@ impl<O: ForestObligation> ObligationForest<O> {
416395
where
417396
P: ObligationProcessor<Obligation = O>,
418397
{
419-
self.gen += 1;
420-
421398
let mut errors = vec![];
422399
let mut stalled = true;
423400

@@ -450,7 +427,7 @@ impl<O: ForestObligation> ObligationForest<O> {
450427
ProcessResult::Changed(children) => {
451428
// We are not (yet) stalled.
452429
stalled = false;
453-
node.state.set(NodeState::Success(Self::not_waiting()));
430+
node.state.set(NodeState::Success);
454431

455432
for child in children {
456433
let st = self.register_obligation_at(child, Some(index));
@@ -479,7 +456,7 @@ impl<O: ForestObligation> ObligationForest<O> {
479456
};
480457
}
481458

482-
self.mark_still_waiting_nodes();
459+
self.mark_successes();
483460
self.process_cycles(processor);
484461
let completed = self.compress(do_completed);
485462

@@ -519,41 +496,50 @@ impl<O: ForestObligation> ObligationForest<O> {
519496
trace
520497
}
521498

522-
/// Mark all `Success` nodes that depend on a pending node as still
523-
/// waiting. Upon completion, any `Success` nodes that aren't still waiting
524-
/// can be removed by `compress`.
525-
fn mark_still_waiting_nodes(&self) {
499+
/// Mark all `Waiting` nodes as `Success`, except those that depend on a
500+
/// pending node.
501+
fn mark_successes(&self) {
502+
// Convert all `Waiting` nodes to `Success`.
503+
for node in &self.nodes {
504+
if node.state.get() == NodeState::Waiting {
505+
node.state.set(NodeState::Success);
506+
}
507+
}
508+
509+
// Convert `Success` nodes that depend on a pending node back to
510+
// `Waiting`.
526511
for node in &self.nodes {
527512
if node.state.get() == NodeState::Pending {
528513
// This call site is hot.
529-
self.inlined_mark_dependents_as_still_waiting(node);
514+
self.inlined_mark_dependents_as_waiting(node);
530515
}
531516
}
532517
}
533518

534519
// This always-inlined function is for the hot call site.
535520
#[inline(always)]
536-
fn inlined_mark_dependents_as_still_waiting(&self, node: &Node<O>) {
521+
fn inlined_mark_dependents_as_waiting(&self, node: &Node<O>) {
537522
for &index in node.dependents.iter() {
538523
let node = &self.nodes[index];
539-
if let NodeState::Success(waiting) = node.state.get() {
540-
if !self.is_still_waiting(waiting) {
541-
node.state.set(NodeState::Success(self.still_waiting()));
542-
// This call site is cold.
543-
self.uninlined_mark_dependents_as_still_waiting(node);
544-
}
524+
let state = node.state.get();
525+
if state == NodeState::Success {
526+
node.state.set(NodeState::Waiting);
527+
// This call site is cold.
528+
self.uninlined_mark_dependents_as_waiting(node);
529+
} else {
530+
debug_assert!(state == NodeState::Waiting || state == NodeState::Error)
545531
}
546532
}
547533
}
548534

549535
// This never-inlined function is for the cold call site.
550536
#[inline(never)]
551-
fn uninlined_mark_dependents_as_still_waiting(&self, node: &Node<O>) {
552-
self.inlined_mark_dependents_as_still_waiting(node)
537+
fn uninlined_mark_dependents_as_waiting(&self, node: &Node<O>) {
538+
self.inlined_mark_dependents_as_waiting(node)
553539
}
554540

555-
/// Report cycles between all `Success` nodes that aren't still waiting.
556-
/// This must be called after `mark_still_waiting_nodes`.
541+
/// Report cycles between all `Success` nodes, and convert all `Success`
542+
/// nodes to `Done`. This must be called after `mark_successes`.
557543
fn process_cycles<P>(&self, processor: &mut P)
558544
where
559545
P: ObligationProcessor<Obligation = O>,
@@ -564,63 +550,51 @@ impl<O: ForestObligation> ObligationForest<O> {
564550
// For some benchmarks this state test is extremely hot. It's a win
565551
// to handle the no-op cases immediately to avoid the cost of the
566552
// function call.
567-
if let NodeState::Success(waiting) = node.state.get() {
568-
if !self.is_still_waiting(waiting) {
569-
self.find_cycles_from_node(&mut stack, processor, index, index);
570-
}
553+
if node.state.get() == NodeState::Success {
554+
self.find_cycles_from_node(&mut stack, processor, index);
571555
}
572556
}
573557

574558
debug_assert!(stack.is_empty());
575559
}
576560

577-
fn find_cycles_from_node<P>(
578-
&self,
579-
stack: &mut Vec<usize>,
580-
processor: &mut P,
581-
min_index: usize,
582-
index: usize,
583-
) where
561+
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
562+
where
584563
P: ObligationProcessor<Obligation = O>,
585564
{
586565
let node = &self.nodes[index];
587-
if let NodeState::Success(waiting) = node.state.get() {
588-
if !self.is_still_waiting(waiting) {
589-
match stack.iter().rposition(|&n| n == index) {
590-
None => {
591-
stack.push(index);
592-
for &dep_index in node.dependents.iter() {
593-
// The index check avoids re-considering a node.
594-
if dep_index >= min_index {
595-
self.find_cycles_from_node(stack, processor, min_index, dep_index);
596-
}
597-
}
598-
stack.pop();
599-
}
600-
Some(rpos) => {
601-
// Cycle detected.
602-
processor.process_backedge(
603-
stack[rpos..].iter().map(GetObligation(&self.nodes)),
604-
PhantomData,
605-
);
566+
if node.state.get() == NodeState::Success {
567+
match stack.iter().rposition(|&n| n == index) {
568+
None => {
569+
stack.push(index);
570+
for &dep_index in node.dependents.iter() {
571+
self.find_cycles_from_node(stack, processor, dep_index);
606572
}
573+
stack.pop();
574+
node.state.set(NodeState::Done);
575+
}
576+
Some(rpos) => {
577+
// Cycle detected.
578+
processor.process_backedge(
579+
stack[rpos..].iter().map(GetObligation(&self.nodes)),
580+
PhantomData,
581+
);
607582
}
608583
}
609584
}
610585
}
611586

612587
/// Compresses the vector, removing all popped nodes. This adjusts the
613588
/// indices and hence invalidates any outstanding indices. `process_cycles`
614-
/// must be run beforehand to remove any cycles on not-still-waiting
615-
/// `Success` nodes.
589+
/// must be run beforehand to remove any cycles on `Success` nodes.
616590
#[inline(never)]
617591
fn compress(&mut self, do_completed: DoCompleted) -> Option<Vec<O>> {
618592
let orig_nodes_len = self.nodes.len();
619593
let mut node_rewrites: Vec<_> = self.node_rewrites.replace(vec![]);
620594
debug_assert!(node_rewrites.is_empty());
621595
node_rewrites.extend(0..orig_nodes_len);
622596
let mut dead_nodes = 0;
623-
let mut removed_success_obligations: Vec<O> = vec![];
597+
let mut removed_done_obligations: Vec<O> = vec![];
624598

625599
// Move removable nodes to the end, preserving the order of the
626600
// remaining nodes.
@@ -632,19 +606,13 @@ impl<O: ForestObligation> ObligationForest<O> {
632606
for index in 0..orig_nodes_len {
633607
let node = &self.nodes[index];
634608
match node.state.get() {
635-
NodeState::Pending => {
636-
if dead_nodes > 0 {
637-
self.nodes.swap(index, index - dead_nodes);
638-
node_rewrites[index] -= dead_nodes;
639-
}
640-
}
641-
NodeState::Success(waiting) if self.is_still_waiting(waiting) => {
609+
NodeState::Pending | NodeState::Waiting => {
642610
if dead_nodes > 0 {
643611
self.nodes.swap(index, index - dead_nodes);
644612
node_rewrites[index] -= dead_nodes;
645613
}
646614
}
647-
NodeState::Success(_) => {
615+
NodeState::Done => {
648616
// This lookup can fail because the contents of
649617
// `self.active_cache` are not guaranteed to match those of
650618
// `self.nodes`. See the comment in `process_obligation`
@@ -658,7 +626,7 @@ impl<O: ForestObligation> ObligationForest<O> {
658626
}
659627
if do_completed == DoCompleted::Yes {
660628
// Extract the success stories.
661-
removed_success_obligations.push(node.obligation.clone());
629+
removed_done_obligations.push(node.obligation.clone());
662630
}
663631
node_rewrites[index] = orig_nodes_len;
664632
dead_nodes += 1;
@@ -672,6 +640,7 @@ impl<O: ForestObligation> ObligationForest<O> {
672640
node_rewrites[index] = orig_nodes_len;
673641
dead_nodes += 1;
674642
}
643+
NodeState::Success => unreachable!(),
675644
}
676645
}
677646

@@ -684,7 +653,7 @@ impl<O: ForestObligation> ObligationForest<O> {
684653
node_rewrites.truncate(0);
685654
self.node_rewrites.replace(node_rewrites);
686655

687-
if do_completed == DoCompleted::Yes { Some(removed_success_obligations) } else { None }
656+
if do_completed == DoCompleted::Yes { Some(removed_done_obligations) } else { None }
688657
}
689658

690659
fn apply_rewrites(&mut self, node_rewrites: &[usize]) {

0 commit comments

Comments
 (0)