From aa1267f630e01d19dcd9247ffb3a795aba0df461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 21 Jan 2023 00:00:00 +0000 Subject: [PATCH 1/9] Preprocess dominator tree to answer queries in O(1) --- .../src/transform/validate.rs | 4 +- .../src/graph/dominators/mod.rs | 107 ++++++++++++++++-- .../src/graph/dominators/tests.rs | 20 ++-- compiler/rustc_middle/src/mir/basic_blocks.rs | 7 +- .../rustc_mir_transform/src/coverage/graph.rs | 46 ++++---- .../rustc_mir_transform/src/coverage/spans.rs | 2 +- .../rustc_mir_transform/src/ctfe_limit.rs | 6 +- 7 files changed, 137 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 3c350e25ba6..2d8cbea8bac 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if self.unwind_edge_count <= 1 { return; } - let doms = self.body.basic_blocks.dominators(); + let dom_tree = self.body.basic_blocks.dominator_tree(); let mut post_contract_node = FxHashMap::default(); // Reusing the allocation across invocations of the closure let mut dom_path = vec![]; @@ -164,7 +164,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(root) = post_contract_node.get(&bb) { break *root; } - let parent = doms.immediate_dominator(bb).unwrap(); + let parent = dom_tree.immediate_dominator(bb).unwrap(); dom_path.push(bb); if !self.body.basic_blocks[parent].is_cleanup { break bb; diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index a7de709ba72..5d6a1de1d21 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -26,7 +26,7 @@ rustc_index::newtype_index! { struct PreorderIndex {} } -pub fn dominators(graph: G) -> Dominators { +pub fn dominator_tree(graph: G) -> DominatorTree { // compute the post order index (rank) for each node let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); @@ -244,7 +244,7 @@ pub fn dominators(graph: G) -> Dominators { let start_node = graph.start_node(); immediate_dominators[start_node] = None; - Dominators { start_node, post_order_rank, immediate_dominators } + DominatorTree { start_node, post_order_rank, immediate_dominators } } /// Evaluate the link-eval virtual forest, providing the currently minimum semi @@ -309,16 +309,18 @@ fn compress( /// Tracks the list of dominators for each node. #[derive(Clone, Debug)] -pub struct Dominators { +pub struct DominatorTree { start_node: N, post_order_rank: IndexVec, // Even though we track only the immediate dominator of each node, it's // possible to get its full list of dominators by looking up the dominator // of each dominator. (See the `impl Iterator for Iter` definition). + // + // Note: immediate_dominators[root] is Some(root)! immediate_dominators: IndexVec>, } -impl Dominators { +impl DominatorTree { /// Returns true if node is reachable from the start node. pub fn is_reachable(&self, node: Node) -> bool { node == self.start_node || self.immediate_dominators[node].is_some() @@ -333,12 +335,7 @@ impl Dominators { /// See the `impl Iterator for Iter` definition to understand how this works. pub fn dominators(&self, node: Node) -> Iter<'_, Node> { assert!(self.is_reachable(node), "node {node:?} is not reachable"); - Iter { dominators: self, node: Some(node) } - } - - pub fn dominates(&self, dom: Node, node: Node) -> bool { - // FIXME -- could be optimized by using post-order-rank - self.dominators(node).any(|n| n == dom) + Iter { dom_tree: self, node: Some(node) } } /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator @@ -351,7 +348,7 @@ impl Dominators { } pub struct Iter<'dom, Node: Idx> { - dominators: &'dom Dominators, + dom_tree: &'dom DominatorTree, node: Option, } @@ -360,10 +357,96 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> { fn next(&mut self) -> Option { if let Some(node) = self.node { - self.node = self.dominators.immediate_dominator(node); + self.node = self.dom_tree.immediate_dominator(node); Some(node) } else { None } } } + +#[derive(Clone, Debug)] +pub struct Dominators { + time: IndexVec, +} + +/// Describes the number of vertices discovered at the time when processing of a particular vertex +/// started and when it finished. Both values are zero for unreachable vertices. +#[derive(Copy, Clone, Default, Debug)] +struct Time { + start: u32, + finish: u32, +} + +impl Dominators { + pub fn dummy() -> Self { + Self { time: Default::default() } + } + + /// Returns true if `a` dominates `b`. + /// + /// # Panics + /// + /// Panics if `b` is unreachable. + pub fn dominates(&self, a: Node, b: Node) -> bool { + let a = self.time[a]; + let b = self.time[b]; + assert!(b.start != 0, "node {b:?} is not reachable"); + a.start <= b.start && b.finish <= a.finish + } +} + +pub fn dominators(tree: &DominatorTree) -> Dominators { + let DominatorTree { start_node, ref immediate_dominators, post_order_rank: _ } = *tree; + + // Transpose the dominator tree edges, so that child nodes of vertex v are stored in + // node[edges[v].start..edges[y].end]. + let mut edges: IndexVec> = + IndexVec::from_elem(0..0, immediate_dominators); + for &idom in immediate_dominators.iter() { + if let Some(idom) = idom { + edges[idom].end += 1; + } + } + let mut m = 0; + for e in edges.iter_mut() { + m += e.end; + e.start = m; + e.end = m; + } + let mut node = IndexVec::from_elem_n(Idx::new(0), m.try_into().unwrap()); + for (i, &idom) in immediate_dominators.iter_enumerated() { + if let Some(idom) = idom { + edges[idom].start -= 1; + node[edges[idom].start] = i; + } + } + + // Perform a depth-first search of the dominator tree. Record the number of vertices discovered + // when vertex v is discovered first as time[v].start, and when its processing is finished as + // time[v].finish. + let mut time: IndexVec = IndexVec::from_elem(Time::default(), immediate_dominators); + let mut stack = Vec::new(); + + let mut discovered = 1; + stack.push(start_node); + time[start_node].start = discovered; + + while let Some(&i) = stack.last() { + let e = &mut edges[i]; + if e.start == e.end { + // Finish processing vertex i. + time[i].finish = discovered; + stack.pop(); + } else { + let j = node[e.start]; + e.start += 1; + // Start processing vertex j. + discovered += 1; + time[j].start = discovered; + stack.push(j); + } + } + + Dominators { time } +} diff --git a/compiler/rustc_data_structures/src/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs index 8b124516623..61a21724dda 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/tests.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/tests.rs @@ -6,8 +6,8 @@ use super::super::tests::TestGraph; fn diamond() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); - let dominators = dominators(&graph); - let immediate_dominators = &dominators.immediate_dominators; + let tree = dominator_tree(&graph); + let immediate_dominators = &tree.immediate_dominators; assert_eq!(immediate_dominators[0], None); assert_eq!(immediate_dominators[1], Some(0)); assert_eq!(immediate_dominators[2], Some(0)); @@ -22,8 +22,8 @@ fn paper() { &[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)], ); - let dominators = dominators(&graph); - let immediate_dominators = &dominators.immediate_dominators; + let dom_tree = dominator_tree(&graph); + let immediate_dominators = &dom_tree.immediate_dominators; assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph assert_eq!(immediate_dominators[1], Some(6)); assert_eq!(immediate_dominators[2], Some(6)); @@ -41,15 +41,15 @@ fn paper_slt() { &[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)], ); - dominators(&graph); + dominator_tree(&graph); } #[test] fn immediate_dominator() { let graph = TestGraph::new(1, &[(1, 2), (2, 3)]); - let dominators = dominators(&graph); - assert_eq!(dominators.immediate_dominator(0), None); - assert_eq!(dominators.immediate_dominator(1), None); - assert_eq!(dominators.immediate_dominator(2), Some(1)); - assert_eq!(dominators.immediate_dominator(3), Some(2)); + let tree = dominator_tree(&graph); + assert_eq!(tree.immediate_dominator(0), None); + assert_eq!(tree.immediate_dominator(1), None); + assert_eq!(tree.immediate_dominator(2), Some(1)); + assert_eq!(tree.immediate_dominator(3), Some(2)); } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 1319ddbb877..f5157e18e6d 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -3,6 +3,7 @@ use crate::mir::{BasicBlock, BasicBlockData, Successors, Terminator, TerminatorK use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph; +use rustc_data_structures::graph::dominators::{dominator_tree, DominatorTree}; use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::OnceCell; @@ -41,8 +42,12 @@ impl<'tcx> BasicBlocks<'tcx> { *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self)) } + pub fn dominator_tree(&self) -> DominatorTree { + dominator_tree(&self) + } + pub fn dominators(&self) -> Dominators { - dominators(&self) + dominators(&self.dominator_tree()) } /// Returns predecessors for each basic block. diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 986d2fd190d..0126310e9ff 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -2,13 +2,14 @@ use super::Error; use itertools::Itertools; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::dominators::{self, Dominators}; +use rustc_data_structures::graph::dominators::{self, DominatorTree, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; +use std::cmp::Ordering; use std::ops::{Index, IndexMut}; const ID_SEPARATOR: &str = ","; @@ -24,6 +25,7 @@ pub(super) struct CoverageGraph { bb_to_bcb: IndexVec>, pub successors: IndexVec>, pub predecessors: IndexVec>, + dominator_tree: Option>, dominators: Option>, } @@ -66,9 +68,17 @@ impl CoverageGraph { } } - let mut basic_coverage_blocks = - Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; - let dominators = dominators::dominators(&basic_coverage_blocks); + let mut basic_coverage_blocks = Self { + bcbs, + bb_to_bcb, + successors, + predecessors, + dominator_tree: None, + dominators: None, + }; + let dominator_tree = dominators::dominator_tree(&basic_coverage_blocks); + let dominators = dominators::dominators(&dominator_tree); + basic_coverage_blocks.dominator_tree = Some(dominator_tree); basic_coverage_blocks.dominators = Some(dominators); basic_coverage_blocks } @@ -212,8 +222,12 @@ impl CoverageGraph { } #[inline(always)] - pub fn dominators(&self) -> &Dominators { - self.dominators.as_ref().unwrap() + pub fn rank_partial_cmp( + &self, + a: BasicCoverageBlock, + b: BasicCoverageBlock, + ) -> Option { + self.dominator_tree.as_ref().unwrap().rank_partial_cmp(a, b) } } @@ -650,26 +664,6 @@ pub(super) fn find_loop_backedges( let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); // Identify loops by their backedges. - // - // The computational complexity is bounded by: n(s) x d where `n` is the number of - // `BasicCoverageBlock` nodes (the simplified/reduced representation of the CFG derived from the - // MIR); `s` is the average number of successors per node (which is most likely less than 2, and - // independent of the size of the function, so it can be treated as a constant); - // and `d` is the average number of dominators per node. - // - // The average number of dominators depends on the size and complexity of the function, and - // nodes near the start of the function's control flow graph typically have less dominators - // than nodes near the end of the CFG. Without doing a detailed mathematical analysis, I - // think the resulting complexity has the characteristics of O(n log n). - // - // The overall complexity appears to be comparable to many other MIR transform algorithms, and I - // don't expect that this function is creating a performance hot spot, but if this becomes an - // issue, there may be ways to optimize the `dominates` algorithm (as indicated by an - // existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps - // by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short - // circuit downstream `dominates` checks. - // - // For now, that kind of optimization seems unnecessarily complicated. for (bcb, _) in basic_coverage_blocks.iter_enumerated() { for &successor in &basic_coverage_blocks.successors[bcb] { if basic_coverage_blocks.dominates(successor, bcb) { diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 287ae217087..97dd1dd09a2 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -345,7 +345,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { // before the dominated equal spans). When later comparing two spans in // order, the first will either dominate the second, or they will have no // dominator relationship. - self.basic_coverage_blocks.dominators().rank_partial_cmp(a.bcb, b.bcb) + self.basic_coverage_blocks.rank_partial_cmp(a.bcb, b.bcb) } } else { // Sort hi() in reverse order so shorter spans are attempted after longer spans. diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 1b3ac78fbc6..a5b3873533c 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -2,7 +2,7 @@ //! (thus indicating there is a loop in the CFG), or whose terminator is a function call. use crate::MirPass; -use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::graph::dominators::DominatorTree; use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, }; @@ -13,7 +13,7 @@ pub struct CtfeLimit; impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let doms = body.basic_blocks.dominators(); + let doms = body.basic_blocks.dominator_tree(); let indices: Vec = body .basic_blocks .iter_enumerated() @@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for CtfeLimit { } fn has_back_edge( - doms: &Dominators, + doms: &DominatorTree, node: BasicBlock, node_data: &BasicBlockData<'_>, ) -> bool { From 6f271dc49ce729a76eb702fcb6d15960aaf8c297 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 4 May 2023 16:26:09 +0000 Subject: [PATCH 2/9] Cache dominators. --- .../src/diagnostics/conflict_errors.rs | 4 ++-- compiler/rustc_borrowck/src/invalidation.rs | 2 +- compiler/rustc_borrowck/src/lib.rs | 13 ++++--------- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 2 +- compiler/rustc_middle/src/mir/basic_blocks.rs | 10 ++++++---- compiler/rustc_mir_transform/src/ssa.rs | 7 +++++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 04b8174079a..da9241253ee 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2382,7 +2382,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut back_edge_stack = Vec::new(); predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, self.dominators()) { + if location.dominates(predecessor, self.dominators) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); @@ -2494,7 +2494,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut has_predecessor = false; predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, self.dominators()) { + if location.dominates(predecessor, self.dominators) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 863c92acdf4..036391d074d 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -46,7 +46,7 @@ struct InvalidationGenerator<'cx, 'tcx> { all_facts: &'cx mut AllFacts, location_table: &'cx LocationTable, body: &'cx Body<'tcx>, - dominators: Dominators, + dominators: &'cx Dominators, borrow_set: &'cx BorrowSet<'tcx>, } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index eb25d454339..4412687b007 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -43,7 +43,6 @@ use rustc_target::abi::FieldIdx; use either::Either; use smallvec::SmallVec; -use std::cell::OnceCell; use std::cell::RefCell; use std::collections::BTreeMap; use std::ops::Deref; @@ -331,7 +330,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: Default::default(), + dominators: promoted_body.basic_blocks.dominators(), upvars: Vec::new(), local_names: IndexVec::from_elem(None, &promoted_body.local_decls), region_names: RefCell::default(), @@ -360,7 +359,7 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: Default::default(), + dominators: body.basic_blocks.dominators(), upvars, local_names, region_names: RefCell::default(), @@ -592,7 +591,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { borrow_set: Rc>, /// Dominators for MIR - dominators: OnceCell>, + dominators: &'cx Dominators, /// Information about upvars not necessarily preserved in types or MIR upvars: Vec>, @@ -1103,7 +1102,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. - if !is_active(this.dominators(), borrow, location) { + if !is_active(this.dominators, borrow, location) { assert!(allow_two_phase_borrow(borrow.kind)); return Control::Continue; } @@ -2267,10 +2266,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) } - - fn dominators(&self) -> &Dominators { - self.dominators.get_or_init(|| self.body.basic_blocks.dominators()) - } } mod error { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 569599faa36..835074806e9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -84,7 +84,7 @@ impl DefLocation { struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { fx: &'mir FunctionCx<'a, 'tcx, Bx>, - dominators: Dominators, + dominators: &'mir Dominators, locals: IndexVec, } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index f5157e18e6d..27a43c0a0e9 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -28,6 +28,8 @@ struct Cache { switch_sources: OnceCell, is_cyclic: OnceCell, postorder: OnceCell>, + dominator_tree: OnceCell>, + dominators: OnceCell>, } impl<'tcx> BasicBlocks<'tcx> { @@ -42,12 +44,12 @@ impl<'tcx> BasicBlocks<'tcx> { *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self)) } - pub fn dominator_tree(&self) -> DominatorTree { - dominator_tree(&self) + pub fn dominator_tree(&self) -> &DominatorTree { + self.cache.dominator_tree.get_or_init(|| dominator_tree(&self)) } - pub fn dominators(&self) -> Dominators { - dominators(&self.dominator_tree()) + pub fn dominators(&self) -> &Dominators { + self.cache.dominators.get_or_init(|| dominators(self.dominator_tree())) } /// Returns predecessors for each basic block. diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 2b404efccc7..c2b7e7c2d27 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -68,8 +68,11 @@ impl SsaLocals { let assignment_order = Vec::with_capacity(body.local_decls.len()); let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls); - let dominators = - if body.basic_blocks.len() > 2 { Some(body.basic_blocks.dominators()) } else { None }; + let dominators = if body.basic_blocks.len() > 2 { + Some(body.basic_blocks.dominators().clone()) + } else { + None + }; let dominators = SmallDominators { inner: dominators }; let direct_uses = IndexVec::from_elem(0, &body.local_decls); From ae318e34449c0b8d92f4067d68fda604bac5da82 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 10:26:04 +0000 Subject: [PATCH 3/9] Remove outdated comment. --- compiler/rustc_data_structures/src/graph/dominators/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 5d6a1de1d21..5c5c803cc06 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -315,8 +315,6 @@ pub struct DominatorTree { // Even though we track only the immediate dominator of each node, it's // possible to get its full list of dominators by looking up the dominator // of each dominator. (See the `impl Iterator for Iter` definition). - // - // Note: immediate_dominators[root] is Some(root)! immediate_dominators: IndexVec>, } From 7bd4fde684fb3ef282b83f90defbf2790034c69b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 10:28:33 +0000 Subject: [PATCH 4/9] Compute dominators on demand for borrowck. --- .../src/diagnostics/conflict_errors.rs | 4 ++-- compiler/rustc_borrowck/src/lib.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index da9241253ee..04b8174079a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2382,7 +2382,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut back_edge_stack = Vec::new(); predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, self.dominators) { + if location.dominates(predecessor, self.dominators()) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); @@ -2494,7 +2494,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut has_predecessor = false; predecessor_locations(self.body, location).for_each(|predecessor| { - if location.dominates(predecessor, self.dominators) { + if location.dominates(predecessor, self.dominators()) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4412687b007..43c4e1a9d6f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -330,7 +330,6 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: promoted_body.basic_blocks.dominators(), upvars: Vec::new(), local_names: IndexVec::from_elem(None, &promoted_body.local_decls), region_names: RefCell::default(), @@ -359,7 +358,6 @@ fn do_mir_borrowck<'tcx>( used_mut: Default::default(), used_mut_upvars: SmallVec::new(), borrow_set: Rc::clone(&borrow_set), - dominators: body.basic_blocks.dominators(), upvars, local_names, region_names: RefCell::default(), @@ -590,9 +588,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// The set of borrows extracted from the MIR borrow_set: Rc>, - /// Dominators for MIR - dominators: &'cx Dominators, - /// Information about upvars not necessarily preserved in types or MIR upvars: Vec>, @@ -1102,7 +1097,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. - if !is_active(this.dominators, borrow, location) { + if !is_active(this.dominators(), borrow, location) { assert!(allow_two_phase_borrow(borrow.kind)); return Control::Continue; } @@ -2266,6 +2261,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) } + + fn dominators(&self) -> &Dominators { + // `BasicBlocks` computes dominators on-demand and caches them. + self.body.basic_blocks.dominators() + } } mod error { From 4bbdb64016a0a7e1713d7c35b40ed931a47e4d8c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 10:29:12 +0000 Subject: [PATCH 5/9] Typo. --- compiler/rustc_data_structures/src/graph/dominators/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 5c5c803cc06..2112b7bcbe3 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -398,7 +398,7 @@ pub fn dominators(tree: &DominatorTree) -> Dominators { let DominatorTree { start_node, ref immediate_dominators, post_order_rank: _ } = *tree; // Transpose the dominator tree edges, so that child nodes of vertex v are stored in - // node[edges[v].start..edges[y].end]. + // node[edges[v].start..edges[v].end]. let mut edges: IndexVec> = IndexVec::from_elem(0..0, immediate_dominators); for &idom in immediate_dominators.iter() { From fa8598cb5066b3463d53682170cc695af2c1b380 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 10:37:29 +0000 Subject: [PATCH 6/9] Merge DominatorTree and Dominators. --- .../src/transform/validate.rs | 2 +- .../src/graph/dominators/mod.rs | 58 +++++++++---------- .../src/graph/dominators/tests.rs | 8 +-- compiler/rustc_middle/src/mir/basic_blocks.rs | 8 +-- .../rustc_mir_transform/src/coverage/graph.rs | 19 ++---- .../rustc_mir_transform/src/ctfe_limit.rs | 6 +- 6 files changed, 40 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 2d8cbea8bac..fae3589e6f1 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if self.unwind_edge_count <= 1 { return; } - let dom_tree = self.body.basic_blocks.dominator_tree(); + let dom_tree = self.body.basic_blocks.dominators(); let mut post_contract_node = FxHashMap::default(); // Reusing the allocation across invocations of the closure let mut dom_path = vec![]; diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 2112b7bcbe3..4868f49d8d5 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -26,7 +26,7 @@ rustc_index::newtype_index! { struct PreorderIndex {} } -pub fn dominator_tree(graph: G) -> DominatorTree { +pub fn dominators(graph: &G) -> Dominators { // compute the post order index (rank) for each node let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); @@ -244,7 +244,10 @@ pub fn dominator_tree(graph: G) -> DominatorTree { let start_node = graph.start_node(); immediate_dominators[start_node] = None; - DominatorTree { start_node, post_order_rank, immediate_dominators } + + let time = compute_access_time(start_node, &immediate_dominators); + + Dominators { start_node, post_order_rank, immediate_dominators, time } } /// Evaluate the link-eval virtual forest, providing the currently minimum semi @@ -309,16 +312,17 @@ fn compress( /// Tracks the list of dominators for each node. #[derive(Clone, Debug)] -pub struct DominatorTree { +pub struct Dominators { start_node: N, post_order_rank: IndexVec, // Even though we track only the immediate dominator of each node, it's // possible to get its full list of dominators by looking up the dominator // of each dominator. (See the `impl Iterator for Iter` definition). immediate_dominators: IndexVec>, + time: IndexVec, } -impl DominatorTree { +impl Dominators { /// Returns true if node is reachable from the start node. pub fn is_reachable(&self, node: Node) -> bool { node == self.start_node || self.immediate_dominators[node].is_some() @@ -343,10 +347,22 @@ impl DominatorTree { pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option { self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs]) } + + /// Returns true if `a` dominates `b`. + /// + /// # Panics + /// + /// Panics if `b` is unreachable. + pub fn dominates(&self, a: Node, b: Node) -> bool { + let a = self.time[a]; + let b = self.time[b]; + assert!(b.start != 0, "node {b:?} is not reachable"); + a.start <= b.start && b.finish <= a.finish + } } pub struct Iter<'dom, Node: Idx> { - dom_tree: &'dom DominatorTree, + dom_tree: &'dom Dominators, node: Option, } @@ -363,11 +379,6 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> { } } -#[derive(Clone, Debug)] -pub struct Dominators { - time: IndexVec, -} - /// Describes the number of vertices discovered at the time when processing of a particular vertex /// started and when it finished. Both values are zero for unreachable vertices. #[derive(Copy, Clone, Default, Debug)] @@ -376,27 +387,10 @@ struct Time { finish: u32, } -impl Dominators { - pub fn dummy() -> Self { - Self { time: Default::default() } - } - - /// Returns true if `a` dominates `b`. - /// - /// # Panics - /// - /// Panics if `b` is unreachable. - pub fn dominates(&self, a: Node, b: Node) -> bool { - let a = self.time[a]; - let b = self.time[b]; - assert!(b.start != 0, "node {b:?} is not reachable"); - a.start <= b.start && b.finish <= a.finish - } -} - -pub fn dominators(tree: &DominatorTree) -> Dominators { - let DominatorTree { start_node, ref immediate_dominators, post_order_rank: _ } = *tree; - +fn compute_access_time( + start_node: N, + immediate_dominators: &IndexSlice>, +) -> IndexVec { // Transpose the dominator tree edges, so that child nodes of vertex v are stored in // node[edges[v].start..edges[v].end]. let mut edges: IndexVec> = @@ -446,5 +440,5 @@ pub fn dominators(tree: &DominatorTree) -> Dominators { } } - Dominators { time } + time } diff --git a/compiler/rustc_data_structures/src/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs index 61a21724dda..31531fe3e2a 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/tests.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/tests.rs @@ -6,7 +6,7 @@ use super::super::tests::TestGraph; fn diamond() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); - let tree = dominator_tree(&graph); + let tree = dominators(&graph); let immediate_dominators = &tree.immediate_dominators; assert_eq!(immediate_dominators[0], None); assert_eq!(immediate_dominators[1], Some(0)); @@ -22,7 +22,7 @@ fn paper() { &[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)], ); - let dom_tree = dominator_tree(&graph); + let dom_tree = dominators(&graph); let immediate_dominators = &dom_tree.immediate_dominators; assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph assert_eq!(immediate_dominators[1], Some(6)); @@ -41,13 +41,13 @@ fn paper_slt() { &[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)], ); - dominator_tree(&graph); + dominators(&graph); } #[test] fn immediate_dominator() { let graph = TestGraph::new(1, &[(1, 2), (2, 3)]); - let tree = dominator_tree(&graph); + let tree = dominators(&graph); assert_eq!(tree.immediate_dominator(0), None); assert_eq!(tree.immediate_dominator(1), None); assert_eq!(tree.immediate_dominator(2), Some(1)); diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 27a43c0a0e9..9d70dbfa072 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -3,7 +3,6 @@ use crate::mir::{BasicBlock, BasicBlockData, Successors, Terminator, TerminatorK use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph; -use rustc_data_structures::graph::dominators::{dominator_tree, DominatorTree}; use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::OnceCell; @@ -28,7 +27,6 @@ struct Cache { switch_sources: OnceCell, is_cyclic: OnceCell, postorder: OnceCell>, - dominator_tree: OnceCell>, dominators: OnceCell>, } @@ -44,12 +42,8 @@ impl<'tcx> BasicBlocks<'tcx> { *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self)) } - pub fn dominator_tree(&self) -> &DominatorTree { - self.cache.dominator_tree.get_or_init(|| dominator_tree(&self)) - } - pub fn dominators(&self) -> &Dominators { - self.cache.dominators.get_or_init(|| dominators(self.dominator_tree())) + self.cache.dominators.get_or_init(|| dominators(self)) } /// Returns predecessors for each basic block. diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 0126310e9ff..ea1223fbca6 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -2,7 +2,7 @@ use super::Error; use itertools::Itertools; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::dominators::{self, DominatorTree, Dominators}; +use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; @@ -25,7 +25,6 @@ pub(super) struct CoverageGraph { bb_to_bcb: IndexVec>, pub successors: IndexVec>, pub predecessors: IndexVec>, - dominator_tree: Option>, dominators: Option>, } @@ -68,17 +67,9 @@ impl CoverageGraph { } } - let mut basic_coverage_blocks = Self { - bcbs, - bb_to_bcb, - successors, - predecessors, - dominator_tree: None, - dominators: None, - }; - let dominator_tree = dominators::dominator_tree(&basic_coverage_blocks); - let dominators = dominators::dominators(&dominator_tree); - basic_coverage_blocks.dominator_tree = Some(dominator_tree); + let mut basic_coverage_blocks = + Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; + let dominators = dominators::dominators(&basic_coverage_blocks); basic_coverage_blocks.dominators = Some(dominators); basic_coverage_blocks } @@ -227,7 +218,7 @@ impl CoverageGraph { a: BasicCoverageBlock, b: BasicCoverageBlock, ) -> Option { - self.dominator_tree.as_ref().unwrap().rank_partial_cmp(a, b) + self.dominators.as_ref().unwrap().rank_partial_cmp(a, b) } } diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index a5b3873533c..1b3ac78fbc6 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -2,7 +2,7 @@ //! (thus indicating there is a loop in the CFG), or whose terminator is a function call. use crate::MirPass; -use rustc_data_structures::graph::dominators::DominatorTree; +use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::{ BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind, }; @@ -13,7 +13,7 @@ pub struct CtfeLimit; impl<'tcx> MirPass<'tcx> for CtfeLimit { #[instrument(skip(self, _tcx, body))] fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let doms = body.basic_blocks.dominator_tree(); + let doms = body.basic_blocks.dominators(); let indices: Vec = body .basic_blocks .iter_enumerated() @@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for CtfeLimit { } fn has_back_edge( - doms: &DominatorTree, + doms: &Dominators, node: BasicBlock, node_data: &BasicBlockData<'_>, ) -> bool { From ada7f1c2c4900877bac0082ac5482e86b3b3974c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 10:45:12 +0000 Subject: [PATCH 7/9] Do not clone dominator tree for SSA analysis. --- compiler/rustc_mir_transform/src/ssa.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index c2b7e7c2d27..e8e4246b797 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -31,11 +31,11 @@ pub struct SsaLocals { /// We often encounter MIR bodies with 1 or 2 basic blocks. In those cases, it's unnecessary to /// actually compute dominators, we can just compare block indices because bb0 is always the first /// block, and in any body all other blocks are always dominated by bb0. -struct SmallDominators { - inner: Option>, +struct SmallDominators<'a> { + inner: Option<&'a Dominators>, } -impl SmallDominators { +impl SmallDominators<'_> { fn dominates(&self, first: Location, second: Location) -> bool { if first.block == second.block { first.statement_index <= second.statement_index @@ -68,11 +68,8 @@ impl SsaLocals { let assignment_order = Vec::with_capacity(body.local_decls.len()); let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls); - let dominators = if body.basic_blocks.len() > 2 { - Some(body.basic_blocks.dominators().clone()) - } else { - None - }; + let dominators = + if body.basic_blocks.len() > 2 { Some(body.basic_blocks.dominators()) } else { None }; let dominators = SmallDominators { inner: dominators }; let direct_uses = IndexVec::from_elem(0, &body.local_decls); @@ -201,14 +198,14 @@ enum LocationExtended { Arg, } -struct SsaVisitor { - dominators: SmallDominators, +struct SsaVisitor<'a> { + dominators: SmallDominators<'a>, assignments: IndexVec>, assignment_order: Vec, direct_uses: IndexVec, } -impl<'tcx> Visitor<'tcx> for SsaVisitor { +impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> { fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) { match ctxt { PlaceContext::MutatingUse(MutatingUseContext::Projection) From 8c2c695c9ee6f5e0f9fb8ef1037f1ec9c9dd94a1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 17 May 2023 12:18:55 +0000 Subject: [PATCH 8/9] Simplify back-edge logic. --- compiler/rustc_mir_transform/src/ctfe_limit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index 1b3ac78fbc6..bf5722b3d00 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -47,7 +47,7 @@ fn has_back_edge( return false; } // Check if any of the dominators of the node are also the node's successor. - doms.dominators(node).any(|dom| node_data.terminator().successors().any(|succ| succ == dom)) + node_data.terminator().successors().any(|succ| doms.dominates(succ, node)) } fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) { From 7c8f29f02c279acfa7b0c353e2187ad268e25de8 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 18 May 2023 14:36:13 +0000 Subject: [PATCH 9/9] Revert spurious changes. --- .../rustc_const_eval/src/transform/validate.rs | 4 ++-- .../src/graph/dominators/tests.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index fae3589e6f1..3c350e25ba6 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if self.unwind_edge_count <= 1 { return; } - let dom_tree = self.body.basic_blocks.dominators(); + let doms = self.body.basic_blocks.dominators(); let mut post_contract_node = FxHashMap::default(); // Reusing the allocation across invocations of the closure let mut dom_path = vec![]; @@ -164,7 +164,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(root) = post_contract_node.get(&bb) { break *root; } - let parent = dom_tree.immediate_dominator(bb).unwrap(); + let parent = doms.immediate_dominator(bb).unwrap(); dom_path.push(bb); if !self.body.basic_blocks[parent].is_cleanup { break bb; diff --git a/compiler/rustc_data_structures/src/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs index 31531fe3e2a..8b124516623 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/tests.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/tests.rs @@ -6,8 +6,8 @@ use super::super::tests::TestGraph; fn diamond() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); - let tree = dominators(&graph); - let immediate_dominators = &tree.immediate_dominators; + let dominators = dominators(&graph); + let immediate_dominators = &dominators.immediate_dominators; assert_eq!(immediate_dominators[0], None); assert_eq!(immediate_dominators[1], Some(0)); assert_eq!(immediate_dominators[2], Some(0)); @@ -22,8 +22,8 @@ fn paper() { &[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)], ); - let dom_tree = dominators(&graph); - let immediate_dominators = &dom_tree.immediate_dominators; + let dominators = dominators(&graph); + let immediate_dominators = &dominators.immediate_dominators; assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph assert_eq!(immediate_dominators[1], Some(6)); assert_eq!(immediate_dominators[2], Some(6)); @@ -47,9 +47,9 @@ fn paper_slt() { #[test] fn immediate_dominator() { let graph = TestGraph::new(1, &[(1, 2), (2, 3)]); - let tree = dominators(&graph); - assert_eq!(tree.immediate_dominator(0), None); - assert_eq!(tree.immediate_dominator(1), None); - assert_eq!(tree.immediate_dominator(2), Some(1)); - assert_eq!(tree.immediate_dominator(3), Some(2)); + let dominators = dominators(&graph); + assert_eq!(dominators.immediate_dominator(0), None); + assert_eq!(dominators.immediate_dominator(1), None); + assert_eq!(dominators.immediate_dominator(2), Some(1)); + assert_eq!(dominators.immediate_dominator(3), Some(2)); }