about summary refs log tree commit diff
path: root/compiler/rustc_mir/src/dataflow/framework
diff options
context:
space:
mode:
authorCamille GILLOT <gillot.camille@gmail.com>2021-01-05 19:53:07 +0100
committerCamille GILLOT <gillot.camille@gmail.com>2021-09-07 19:57:07 +0200
commitfd9c04fe32d3b7700d600ae1be72d5758ffd66ff (patch)
tree9968ed2ae3ef44610f7669a5221d9a502f33765f /compiler/rustc_mir/src/dataflow/framework
parent81a600b6b7db07ebac28c8ddedd357e3c5b9951d (diff)
downloadrust-fd9c04fe32d3b7700d600ae1be72d5758ffd66ff.tar.gz
rust-fd9c04fe32d3b7700d600ae1be72d5758ffd66ff.zip
Move the dataflow framework to its own crate.
Diffstat (limited to 'compiler/rustc_mir/src/dataflow/framework')
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/cursor.rs226
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/direction.rs564
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/engine.rs414
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/fmt.rs172
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/graphviz.rs644
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/lattice.rs231
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/mod.rs546
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/tests.rs320
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/visitor.rs187
9 files changed, 0 insertions, 3304 deletions
diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs
deleted file mode 100644
index c000e49c14b..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-//! Random access inspection of the results of a dataflow analysis.
-
-use std::borrow::Borrow;
-use std::cmp::Ordering;
-
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::Idx;
-use rustc_middle::mir::{self, BasicBlock, Location};
-
-use super::{Analysis, Direction, Effect, EffectIndex, Results};
-
-/// A `ResultsCursor` that borrows the underlying `Results`.
-pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
-
-/// Allows random access inspection of the results of a dataflow analysis.
-///
-/// This cursor only has linear performance within a basic block when its statements are visited in
-/// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
-/// visited in *reverse* order—performance will be quadratic in the number of statements in the
-/// block. The order in which basic blocks are inspected has no impact on performance.
-///
-/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
-/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
-pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
-where
-    A: Analysis<'tcx>,
-{
-    body: &'mir mir::Body<'tcx>,
-    results: R,
-    state: A::Domain,
-
-    pos: CursorPosition,
-
-    /// Indicates that `state` has been modified with a custom effect.
-    ///
-    /// When this flag is set, we need to reset to an entry set before doing a seek.
-    state_needs_reset: bool,
-
-    #[cfg(debug_assertions)]
-    reachable_blocks: BitSet<BasicBlock>,
-}
-
-impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
-where
-    A: Analysis<'tcx>,
-    R: Borrow<Results<'tcx, A>>,
-{
-    /// Returns a new cursor that can inspect `results`.
-    pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
-        let bottom_value = results.borrow().analysis.bottom_value(body);
-        ResultsCursor {
-            body,
-            results,
-
-            // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
-            // it needs to reset to block entry before the first seek. The cursor position is
-            // immaterial.
-            state_needs_reset: true,
-            state: bottom_value,
-            pos: CursorPosition::block_entry(mir::START_BLOCK),
-
-            #[cfg(debug_assertions)]
-            reachable_blocks: mir::traversal::reachable_as_bitset(body),
-        }
-    }
-
-    /// Returns the underlying `Results`.
-    pub fn results(&self) -> &Results<'tcx, A> {
-        &self.results.borrow()
-    }
-
-    /// Returns the `Analysis` used to generate the underlying `Results`.
-    pub fn analysis(&self) -> &A {
-        &self.results.borrow().analysis
-    }
-
-    /// Returns the dataflow state at the current location.
-    pub fn get(&self) -> &A::Domain {
-        &self.state
-    }
-
-    /// Resets the cursor to hold the entry set for the given basic block.
-    ///
-    /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
-    ///
-    /// For backward dataflow analyses, this is the dataflow state after the terminator.
-    pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
-        #[cfg(debug_assertions)]
-        assert!(self.reachable_blocks.contains(block));
-
-        self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
-        self.pos = CursorPosition::block_entry(block);
-        self.state_needs_reset = false;
-    }
-
-    /// Resets the cursor to hold the state prior to the first statement in a basic block.
-    ///
-    /// For forward analyses, this is the entry set for the given block.
-    ///
-    /// For backward analyses, this is the state that will be propagated to its
-    /// predecessors (ignoring edge-specific effects).
-    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
-        if A::Direction::is_forward() {
-            self.seek_to_block_entry(block)
-        } else {
-            self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
-        }
-    }
-
-    /// Resets the cursor to hold the state after the terminator in a basic block.
-    ///
-    /// For backward analyses, this is the entry set for the given block.
-    ///
-    /// For forward analyses, this is the state that will be propagated to its
-    /// successors (ignoring edge-specific effects).
-    pub fn seek_to_block_end(&mut self, block: BasicBlock) {
-        if A::Direction::is_backward() {
-            self.seek_to_block_entry(block)
-        } else {
-            self.seek_after(self.body.terminator_loc(block), Effect::Primary)
-        }
-    }
-
-    /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
-    /// applied.
-    ///
-    /// The "before" effect at the target location *will be* applied.
-    pub fn seek_before_primary_effect(&mut self, target: Location) {
-        self.seek_after(target, Effect::Before)
-    }
-
-    /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
-    /// applied.
-    ///
-    /// The "before" effect at the target location will be applied as well.
-    pub fn seek_after_primary_effect(&mut self, target: Location) {
-        self.seek_after(target, Effect::Primary)
-    }
-
-    fn seek_after(&mut self, target: Location, effect: Effect) {
-        assert!(target <= self.body.terminator_loc(target.block));
-
-        // Reset to the entry of the target block if any of the following are true:
-        //   - A custom effect has been applied to the cursor state.
-        //   - We are in a different block than the target.
-        //   - We are in the same block but have advanced past the target effect.
-        if self.state_needs_reset || self.pos.block != target.block {
-            self.seek_to_block_entry(target.block);
-        } else if let Some(curr_effect) = self.pos.curr_effect_index {
-            let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
-            if A::Direction::is_backward() {
-                ord = ord.reverse()
-            }
-
-            match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
-                Ordering::Equal => return,
-                Ordering::Greater => self.seek_to_block_entry(target.block),
-                Ordering::Less => {}
-            }
-        }
-
-        // At this point, the cursor is in the same block as the target location at an earlier
-        // statement.
-        debug_assert_eq!(target.block, self.pos.block);
-
-        let block_data = &self.body[target.block];
-        let next_effect = if A::Direction::is_forward() {
-            #[rustfmt::skip]
-            self.pos.curr_effect_index.map_or_else(
-                || Effect::Before.at_index(0),
-                EffectIndex::next_in_forward_order,
-            )
-        } else {
-            self.pos.curr_effect_index.map_or_else(
-                || Effect::Before.at_index(block_data.statements.len()),
-                EffectIndex::next_in_backward_order,
-            )
-        };
-
-        let analysis = &self.results.borrow().analysis;
-        let target_effect_index = effect.at_index(target.statement_index);
-
-        A::Direction::apply_effects_in_range(
-            analysis,
-            &mut self.state,
-            target.block,
-            block_data,
-            next_effect..=target_effect_index,
-        );
-
-        self.pos =
-            CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
-    }
-
-    /// Applies `f` to the cursor's internal state.
-    ///
-    /// This can be used, e.g., to apply the call return effect directly to the cursor without
-    /// creating an extra copy of the dataflow state.
-    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
-        f(&self.results.borrow().analysis, &mut self.state);
-        self.state_needs_reset = true;
-    }
-}
-
-impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R>
-where
-    A: Analysis<'tcx, Domain = BitSet<T>>,
-    T: Idx,
-    R: Borrow<Results<'tcx, A>>,
-{
-    pub fn contains(&self, elem: T) -> bool {
-        self.get().contains(elem)
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-struct CursorPosition {
-    block: BasicBlock,
-    curr_effect_index: Option<EffectIndex>,
-}
-
-impl CursorPosition {
-    fn block_entry(block: BasicBlock) -> CursorPosition {
-        CursorPosition { block, curr_effect_index: None }
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs
deleted file mode 100644
index 8a9ced91eb3..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/direction.rs
+++ /dev/null
@@ -1,564 +0,0 @@
-use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets};
-use rustc_middle::ty::TyCtxt;
-use std::ops::RangeInclusive;
-
-use super::visitor::{ResultsVisitable, ResultsVisitor};
-use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
-
-pub trait Direction {
-    fn is_forward() -> bool;
-
-    fn is_backward() -> bool {
-        !Self::is_forward()
-    }
-
-    /// Applies all effects between the given `EffectIndex`s.
-    ///
-    /// `effects.start()` must precede or equal `effects.end()` in this direction.
-    fn apply_effects_in_range<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-        effects: RangeInclusive<EffectIndex>,
-    ) where
-        A: Analysis<'tcx>;
-
-    fn apply_effects_in_block<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: Analysis<'tcx>;
-
-    fn gen_kill_effects_in_block<A>(
-        analysis: &A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>;
-
-    fn visit_results_in_block<F, R>(
-        state: &mut F,
-        block: BasicBlock,
-        block_data: &'mir mir::BasicBlockData<'tcx>,
-        results: &R,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
-    ) where
-        R: ResultsVisitable<'tcx, FlowState = F>;
-
-    fn join_state_into_successors_of<A>(
-        analysis: &A,
-        tcx: TyCtxt<'tcx>,
-        body: &mir::Body<'tcx>,
-        dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut A::Domain,
-        block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>;
-}
-
-/// Dataflow that runs from the exit of a block (the terminator), to its entry (the first statement).
-pub struct Backward;
-
-impl Direction for Backward {
-    fn is_forward() -> bool {
-        false
-    }
-
-    fn apply_effects_in_block<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: Analysis<'tcx>,
-    {
-        let terminator = block_data.terminator();
-        let location = Location { block, statement_index: block_data.statements.len() };
-        analysis.apply_before_terminator_effect(state, terminator, location);
-        analysis.apply_terminator_effect(state, terminator, location);
-
-        for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
-            let location = Location { block, statement_index };
-            analysis.apply_before_statement_effect(state, statement, location);
-            analysis.apply_statement_effect(state, statement, location);
-        }
-    }
-
-    fn gen_kill_effects_in_block<A>(
-        analysis: &A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>,
-    {
-        let terminator = block_data.terminator();
-        let location = Location { block, statement_index: block_data.statements.len() };
-        analysis.before_terminator_effect(trans, terminator, location);
-        analysis.terminator_effect(trans, terminator, location);
-
-        for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
-            let location = Location { block, statement_index };
-            analysis.before_statement_effect(trans, statement, location);
-            analysis.statement_effect(trans, statement, location);
-        }
-    }
-
-    fn apply_effects_in_range<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-        effects: RangeInclusive<EffectIndex>,
-    ) where
-        A: Analysis<'tcx>,
-    {
-        let (from, to) = (*effects.start(), *effects.end());
-        let terminator_index = block_data.statements.len();
-
-        assert!(from.statement_index <= terminator_index);
-        assert!(!to.precedes_in_backward_order(from));
-
-        // Handle the statement (or terminator) at `from`.
-
-        let next_effect = match from.effect {
-            // If we need to apply the terminator effect in all or in part, do so now.
-            _ if from.statement_index == terminator_index => {
-                let location = Location { block, statement_index: from.statement_index };
-                let terminator = block_data.terminator();
-
-                if from.effect == Effect::Before {
-                    analysis.apply_before_terminator_effect(state, terminator, location);
-                    if to == Effect::Before.at_index(terminator_index) {
-                        return;
-                    }
-                }
-
-                analysis.apply_terminator_effect(state, terminator, location);
-                if to == Effect::Primary.at_index(terminator_index) {
-                    return;
-                }
-
-                // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons
-                // with `to`.
-                from.statement_index - 1
-            }
-
-            Effect::Primary => {
-                let location = Location { block, statement_index: from.statement_index };
-                let statement = &block_data.statements[from.statement_index];
-
-                analysis.apply_statement_effect(state, statement, location);
-                if to == Effect::Primary.at_index(from.statement_index) {
-                    return;
-                }
-
-                from.statement_index - 1
-            }
-
-            Effect::Before => from.statement_index,
-        };
-
-        // Handle all statements between `first_unapplied_idx` and `to.statement_index`.
-
-        for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
-            let location = Location { block, statement_index };
-            let statement = &block_data.statements[statement_index];
-            analysis.apply_before_statement_effect(state, statement, location);
-            analysis.apply_statement_effect(state, statement, location);
-        }
-
-        // Handle the statement at `to`.
-
-        let location = Location { block, statement_index: to.statement_index };
-        let statement = &block_data.statements[to.statement_index];
-        analysis.apply_before_statement_effect(state, statement, location);
-
-        if to.effect == Effect::Before {
-            return;
-        }
-
-        analysis.apply_statement_effect(state, statement, location);
-    }
-
-    fn visit_results_in_block<F, R>(
-        state: &mut F,
-        block: BasicBlock,
-        block_data: &'mir mir::BasicBlockData<'tcx>,
-        results: &R,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
-    ) where
-        R: ResultsVisitable<'tcx, FlowState = F>,
-    {
-        results.reset_to_block_entry(state, block);
-
-        vis.visit_block_end(&state, block_data, block);
-
-        // Terminator
-        let loc = Location { block, statement_index: block_data.statements.len() };
-        let term = block_data.terminator();
-        results.reconstruct_before_terminator_effect(state, term, loc);
-        vis.visit_terminator_before_primary_effect(state, term, loc);
-        results.reconstruct_terminator_effect(state, term, loc);
-        vis.visit_terminator_after_primary_effect(state, term, loc);
-
-        for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
-            let loc = Location { block, statement_index };
-            results.reconstruct_before_statement_effect(state, stmt, loc);
-            vis.visit_statement_before_primary_effect(state, stmt, loc);
-            results.reconstruct_statement_effect(state, stmt, loc);
-            vis.visit_statement_after_primary_effect(state, stmt, loc);
-        }
-
-        vis.visit_block_start(state, block_data, block);
-    }
-
-    fn join_state_into_successors_of<A>(
-        analysis: &A,
-        _tcx: TyCtxt<'tcx>,
-        body: &mir::Body<'tcx>,
-        dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut A::Domain,
-        (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>,
-    {
-        for pred in body.predecessors()[bb].iter().copied() {
-            match body[pred].terminator().kind {
-                // Apply terminator-specific edge effects.
-                //
-                // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
-                mir::TerminatorKind::Call {
-                    destination: Some((return_place, dest)),
-                    ref func,
-                    ref args,
-                    ..
-                } if dest == bb => {
-                    let mut tmp = exit_state.clone();
-                    analysis.apply_call_return_effect(&mut tmp, pred, func, args, return_place);
-                    propagate(pred, &tmp);
-                }
-
-                mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
-                    let mut tmp = exit_state.clone();
-                    analysis.apply_yield_resume_effect(&mut tmp, resume, resume_arg);
-                    propagate(pred, &tmp);
-                }
-
-                // Ignore dead unwinds.
-                mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
-                | mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
-                | mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
-                | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
-                | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
-                    if unwind == bb =>
-                {
-                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
-                        propagate(pred, exit_state);
-                    }
-                }
-
-                _ => propagate(pred, exit_state),
-            }
-        }
-    }
-}
-
-/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
-pub struct Forward;
-
-impl Direction for Forward {
-    fn is_forward() -> bool {
-        true
-    }
-
-    fn apply_effects_in_block<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: Analysis<'tcx>,
-    {
-        for (statement_index, statement) in block_data.statements.iter().enumerate() {
-            let location = Location { block, statement_index };
-            analysis.apply_before_statement_effect(state, statement, location);
-            analysis.apply_statement_effect(state, statement, location);
-        }
-
-        let terminator = block_data.terminator();
-        let location = Location { block, statement_index: block_data.statements.len() };
-        analysis.apply_before_terminator_effect(state, terminator, location);
-        analysis.apply_terminator_effect(state, terminator, location);
-    }
-
-    fn gen_kill_effects_in_block<A>(
-        analysis: &A,
-        trans: &mut GenKillSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) where
-        A: GenKillAnalysis<'tcx>,
-    {
-        for (statement_index, statement) in block_data.statements.iter().enumerate() {
-            let location = Location { block, statement_index };
-            analysis.before_statement_effect(trans, statement, location);
-            analysis.statement_effect(trans, statement, location);
-        }
-
-        let terminator = block_data.terminator();
-        let location = Location { block, statement_index: block_data.statements.len() };
-        analysis.before_terminator_effect(trans, terminator, location);
-        analysis.terminator_effect(trans, terminator, location);
-    }
-
-    fn apply_effects_in_range<A>(
-        analysis: &A,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-        effects: RangeInclusive<EffectIndex>,
-    ) where
-        A: Analysis<'tcx>,
-    {
-        let (from, to) = (*effects.start(), *effects.end());
-        let terminator_index = block_data.statements.len();
-
-        assert!(to.statement_index <= terminator_index);
-        assert!(!to.precedes_in_forward_order(from));
-
-        // If we have applied the before affect of the statement or terminator at `from` but not its
-        // after effect, do so now and start the loop below from the next statement.
-
-        let first_unapplied_index = match from.effect {
-            Effect::Before => from.statement_index,
-
-            Effect::Primary if from.statement_index == terminator_index => {
-                debug_assert_eq!(from, to);
-
-                let location = Location { block, statement_index: terminator_index };
-                let terminator = block_data.terminator();
-                analysis.apply_terminator_effect(state, terminator, location);
-                return;
-            }
-
-            Effect::Primary => {
-                let location = Location { block, statement_index: from.statement_index };
-                let statement = &block_data.statements[from.statement_index];
-                analysis.apply_statement_effect(state, statement, location);
-
-                // If we only needed to apply the after effect of the statement at `idx`, we are done.
-                if from == to {
-                    return;
-                }
-
-                from.statement_index + 1
-            }
-        };
-
-        // Handle all statements between `from` and `to` whose effects must be applied in full.
-
-        for statement_index in first_unapplied_index..to.statement_index {
-            let location = Location { block, statement_index };
-            let statement = &block_data.statements[statement_index];
-            analysis.apply_before_statement_effect(state, statement, location);
-            analysis.apply_statement_effect(state, statement, location);
-        }
-
-        // Handle the statement or terminator at `to`.
-
-        let location = Location { block, statement_index: to.statement_index };
-        if to.statement_index == terminator_index {
-            let terminator = block_data.terminator();
-            analysis.apply_before_terminator_effect(state, terminator, location);
-
-            if to.effect == Effect::Primary {
-                analysis.apply_terminator_effect(state, terminator, location);
-            }
-        } else {
-            let statement = &block_data.statements[to.statement_index];
-            analysis.apply_before_statement_effect(state, statement, location);
-
-            if to.effect == Effect::Primary {
-                analysis.apply_statement_effect(state, statement, location);
-            }
-        }
-    }
-
-    fn visit_results_in_block<F, R>(
-        state: &mut F,
-        block: BasicBlock,
-        block_data: &'mir mir::BasicBlockData<'tcx>,
-        results: &R,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
-    ) where
-        R: ResultsVisitable<'tcx, FlowState = F>,
-    {
-        results.reset_to_block_entry(state, block);
-
-        vis.visit_block_start(state, block_data, block);
-
-        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
-            let loc = Location { block, statement_index };
-            results.reconstruct_before_statement_effect(state, stmt, loc);
-            vis.visit_statement_before_primary_effect(state, stmt, loc);
-            results.reconstruct_statement_effect(state, stmt, loc);
-            vis.visit_statement_after_primary_effect(state, stmt, loc);
-        }
-
-        let loc = Location { block, statement_index: block_data.statements.len() };
-        let term = block_data.terminator();
-        results.reconstruct_before_terminator_effect(state, term, loc);
-        vis.visit_terminator_before_primary_effect(state, term, loc);
-        results.reconstruct_terminator_effect(state, term, loc);
-        vis.visit_terminator_after_primary_effect(state, term, loc);
-
-        vis.visit_block_end(state, block_data, block);
-    }
-
-    fn join_state_into_successors_of<A>(
-        analysis: &A,
-        _tcx: TyCtxt<'tcx>,
-        _body: &mir::Body<'tcx>,
-        dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut A::Domain,
-        (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &A::Domain),
-    ) where
-        A: Analysis<'tcx>,
-    {
-        use mir::TerminatorKind::*;
-        match bb_data.terminator().kind {
-            Return | Resume | Abort | GeneratorDrop | Unreachable => {}
-
-            Goto { target } => propagate(target, exit_state),
-
-            Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ }
-            | Drop { target, unwind, place: _ }
-            | DropAndReplace { target, unwind, value: _, place: _ }
-            | FalseUnwind { real_target: target, unwind } => {
-                if let Some(unwind) = unwind {
-                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
-                        propagate(unwind, exit_state);
-                    }
-                }
-
-                propagate(target, exit_state);
-            }
-
-            FalseEdge { real_target, imaginary_target } => {
-                propagate(real_target, exit_state);
-                propagate(imaginary_target, exit_state);
-            }
-
-            Yield { resume: target, drop, resume_arg, value: _ } => {
-                if let Some(drop) = drop {
-                    propagate(drop, exit_state);
-                }
-
-                analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
-                propagate(target, exit_state);
-            }
-
-            Call { cleanup, destination, ref func, ref args, from_hir_call: _, fn_span: _ } => {
-                if let Some(unwind) = cleanup {
-                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
-                        propagate(unwind, exit_state);
-                    }
-                }
-
-                if let Some((dest_place, target)) = destination {
-                    // N.B.: This must be done *last*, otherwise the unwind path will see the call
-                    // return effect.
-                    analysis.apply_call_return_effect(exit_state, bb, func, args, dest_place);
-                    propagate(target, exit_state);
-                }
-            }
-
-            InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => {
-                if let Some(target) = destination {
-                    propagate(target, exit_state);
-                }
-            }
-
-            SwitchInt { ref targets, ref discr, switch_ty: _ } => {
-                let mut applier = SwitchIntEdgeEffectApplier {
-                    exit_state,
-                    targets,
-                    propagate,
-                    effects_applied: false,
-                };
-
-                analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
-
-                let SwitchIntEdgeEffectApplier {
-                    exit_state, mut propagate, effects_applied, ..
-                } = applier;
-
-                if !effects_applied {
-                    for target in targets.all_targets() {
-                        propagate(*target, exit_state);
-                    }
-                }
-            }
-        }
-    }
-}
-
-struct SwitchIntEdgeEffectApplier<'a, D, F> {
-    exit_state: &'a mut D,
-    targets: &'a SwitchTargets,
-    propagate: F,
-
-    effects_applied: bool,
-}
-
-impl<D, F> super::SwitchIntEdgeEffects<D> for SwitchIntEdgeEffectApplier<'_, D, F>
-where
-    D: Clone,
-    F: FnMut(BasicBlock, &D),
-{
-    fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
-        assert!(!self.effects_applied);
-
-        let mut tmp = None;
-        for (value, target) in self.targets.iter() {
-            let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
-            apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
-            (self.propagate)(target, tmp);
-        }
-
-        // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
-        // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
-        let otherwise = self.targets.otherwise();
-        apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
-        (self.propagate)(otherwise, self.exit_state);
-
-        self.effects_applied = true;
-    }
-}
-
-/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
-/// the more efficient `clone_from` if `opt` was `Some`.
-///
-/// Returns a mutable reference to the new clone that resides in `opt`.
-//
-// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
-// standard library?
-fn opt_clone_from_or_clone<T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
-    if opt.is_some() {
-        let ret = opt.as_mut().unwrap();
-        ret.clone_from(val);
-        ret
-    } else {
-        *opt = Some(val.clone());
-        opt.as_mut().unwrap()
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs
deleted file mode 100644
index 7ff7c860591..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/engine.rs
+++ /dev/null
@@ -1,414 +0,0 @@
-//! A solver for dataflow problems.
-
-use std::borrow::BorrowMut;
-use std::ffi::OsString;
-use std::path::PathBuf;
-
-use rustc_ast as ast;
-use rustc_data_structures::work_queue::WorkQueue;
-use rustc_graphviz as dot;
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::mir::{self, traversal, BasicBlock};
-use rustc_middle::ty::TyCtxt;
-use rustc_span::symbol::{sym, Symbol};
-
-use super::fmt::DebugWithContext;
-use super::graphviz;
-use super::{
-    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
-    ResultsCursor, ResultsVisitor,
-};
-use crate::util::pretty::{create_dump_file, dump_enabled};
-
-/// A dataflow analysis that has converged to fixpoint.
-pub struct Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub analysis: A,
-    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
-}
-
-impl<A> Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    /// Creates a `ResultsCursor` that can inspect these `Results`.
-    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
-        ResultsCursor::new(body, self)
-    }
-
-    /// Gets the dataflow state for the given block.
-    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
-        &self.entry_sets[block]
-    }
-
-    pub fn visit_with(
-        &self,
-        body: &'mir mir::Body<'tcx>,
-        blocks: impl IntoIterator<Item = BasicBlock>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
-    ) {
-        visit_results(body, blocks, self, vis)
-    }
-
-    pub fn visit_reachable_with(
-        &self,
-        body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
-    ) {
-        let blocks = mir::traversal::reachable(body);
-        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
-    }
-}
-
-/// A solver for dataflow problems.
-pub struct Engine<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    tcx: TyCtxt<'tcx>,
-    body: &'a mir::Body<'tcx>,
-    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
-    entry_sets: IndexVec<BasicBlock, A::Domain>,
-    pass_name: Option<&'static str>,
-    analysis: A,
-
-    /// Cached, cumulative transfer functions for each block.
-    //
-    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
-    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
-    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
-    // the message for the commit that added this FIXME for more information.
-    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
-}
-
-impl<A, D, T> Engine<'a, 'tcx, A>
-where
-    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
-    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
-    T: Idx,
-{
-    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
-    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
-        // If there are no back-edges in the control-flow graph, we only ever need to apply the
-        // transfer function for each block exactly once (assuming that we process blocks in RPO).
-        //
-        // In this case, there's no need to compute the block transfer functions ahead of time.
-        if !body.is_cfg_cyclic() {
-            return Self::new(tcx, body, analysis, None);
-        }
-
-        // Otherwise, compute and store the cumulative transfer function for each block.
-
-        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
-        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());
-
-        for (block, block_data) in body.basic_blocks().iter_enumerated() {
-            let trans = &mut trans_for_block[block];
-            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
-        }
-
-        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
-            trans_for_block[bb].apply(state.borrow_mut());
-        });
-
-        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
-    }
-}
-
-impl<A, D> Engine<'a, 'tcx, A>
-where
-    A: Analysis<'tcx, Domain = D>,
-    D: Clone + JoinSemiLattice,
-{
-    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
-    /// function.
-    ///
-    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
-    /// better performance.
-    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
-        Self::new(tcx, body, analysis, None)
-    }
-
-    fn new(
-        tcx: TyCtxt<'tcx>,
-        body: &'a mir::Body<'tcx>,
-        analysis: A,
-        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
-    ) -> Self {
-        let bottom_value = analysis.bottom_value(body);
-        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
-        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
-
-        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
-            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
-        }
-
-        Engine {
-            analysis,
-            tcx,
-            body,
-            dead_unwinds: None,
-            pass_name: None,
-            entry_sets,
-            apply_trans_for_block,
-        }
-    }
-
-    /// Signals that we do not want dataflow state to propagate across unwind edges for these
-    /// `BasicBlock`s.
-    ///
-    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
-    /// unwind during execution. Otherwise, your dataflow results will not be correct.
-    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
-        self.dead_unwinds = Some(dead_unwinds);
-        self
-    }
-
-    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
-    ///
-    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
-    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
-    pub fn pass_name(mut self, name: &'static str) -> Self {
-        self.pass_name = Some(name);
-        self
-    }
-
-    /// Computes the fixpoint for this dataflow problem and returns it.
-    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
-    where
-        A::Domain: DebugWithContext<A>,
-    {
-        let Engine {
-            analysis,
-            body,
-            dead_unwinds,
-            mut entry_sets,
-            tcx,
-            apply_trans_for_block,
-            pass_name,
-            ..
-        } = self;
-
-        let mut dirty_queue: WorkQueue<BasicBlock> =
-            WorkQueue::with_none(body.basic_blocks().len());
-
-        if A::Direction::is_forward() {
-            for (bb, _) in traversal::reverse_postorder(body) {
-                dirty_queue.insert(bb);
-            }
-        } else {
-            // Reverse post-order on the reverse CFG may generate a better iteration order for
-            // backward dataflow analyses, but probably not enough to matter.
-            for (bb, _) in traversal::postorder(body) {
-                dirty_queue.insert(bb);
-            }
-        }
-
-        // `state` is not actually used between iterations;
-        // this is just an optimization to avoid reallocating
-        // every iteration.
-        let mut state = analysis.bottom_value(body);
-        while let Some(bb) = dirty_queue.pop() {
-            let bb_data = &body[bb];
-
-            // Set the state to the entry state of the block.
-            // This is equivalent to `state = entry_sets[bb].clone()`,
-            // but it saves an allocation, thus improving compile times.
-            state.clone_from(&entry_sets[bb]);
-
-            // Apply the block transfer function, using the cached one if it exists.
-            match &apply_trans_for_block {
-                Some(apply) => apply(bb, &mut state),
-                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
-            }
-
-            A::Direction::join_state_into_successors_of(
-                &analysis,
-                tcx,
-                body,
-                dead_unwinds,
-                &mut state,
-                (bb, bb_data),
-                |target: BasicBlock, state: &A::Domain| {
-                    let set_changed = entry_sets[target].join(state);
-                    if set_changed {
-                        dirty_queue.insert(target);
-                    }
-                },
-            );
-        }
-
-        let results = Results { analysis, entry_sets };
-
-        let res = write_graphviz_results(tcx, &body, &results, pass_name);
-        if let Err(e) = res {
-            error!("Failed to write graphviz dataflow results: {}", e);
-        }
-
-        results
-    }
-}
-
-// Graphviz
-
-/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
-/// `rustc_mir` attributes.
-fn write_graphviz_results<A>(
-    tcx: TyCtxt<'tcx>,
-    body: &mir::Body<'tcx>,
-    results: &Results<'tcx, A>,
-    pass_name: Option<&'static str>,
-) -> std::io::Result<()>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    use std::fs;
-    use std::io::{self, Write};
-
-    let def_id = body.source.def_id();
-    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
-        Ok(attrs) => attrs,
-
-        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
-        Err(()) => return Ok(()),
-    };
-
-    let mut file = match attrs.output_path(A::NAME) {
-        Some(path) => {
-            debug!("printing dataflow results for {:?} to {}", def_id, path.display());
-            if let Some(parent) = path.parent() {
-                fs::create_dir_all(parent)?;
-            }
-            io::BufWriter::new(fs::File::create(&path)?)
-        }
-
-        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
-            && dump_enabled(tcx, A::NAME, def_id) =>
-        {
-            create_dump_file(
-                tcx,
-                ".dot",
-                None,
-                A::NAME,
-                &pass_name.unwrap_or("-----"),
-                body.source,
-            )?
-        }
-
-        _ => return Ok(()),
-    };
-
-    let style = match attrs.formatter {
-        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
-        _ => graphviz::OutputStyle::AfterOnly,
-    };
-
-    let mut buf = Vec::new();
-
-    let graphviz = graphviz::Formatter::new(body, results, style);
-    let mut render_opts =
-        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
-    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
-        render_opts.push(dot::RenderOption::DarkTheme);
-    }
-    dot::render_opts(&graphviz, &mut buf, &render_opts)?;
-
-    file.write_all(&buf)?;
-
-    Ok(())
-}
-
-#[derive(Default)]
-struct RustcMirAttrs {
-    basename_and_suffix: Option<PathBuf>,
-    formatter: Option<Symbol>,
-}
-
-impl RustcMirAttrs {
-    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
-        let attrs = tcx.get_attrs(def_id);
-
-        let mut result = Ok(());
-        let mut ret = RustcMirAttrs::default();
-
-        let rustc_mir_attrs = attrs
-            .iter()
-            .filter(|attr| attr.has_name(sym::rustc_mir))
-            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
-
-        for attr in rustc_mir_attrs {
-            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
-                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
-                    let path = PathBuf::from(s.to_string());
-                    match path.file_name() {
-                        Some(_) => Ok(path),
-                        None => {
-                            tcx.sess.span_err(attr.span(), "path must end in a filename");
-                            Err(())
-                        }
-                    }
-                })
-            } else if attr.has_name(sym::borrowck_graphviz_format) {
-                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
-                    sym::gen_kill | sym::two_phase => Ok(s),
-                    _ => {
-                        tcx.sess.span_err(attr.span(), "unknown formatter");
-                        Err(())
-                    }
-                })
-            } else {
-                Ok(())
-            };
-
-            result = result.and(attr_result);
-        }
-
-        result.map(|()| ret)
-    }
-
-    fn set_field<T>(
-        field: &mut Option<T>,
-        tcx: TyCtxt<'tcx>,
-        attr: &ast::NestedMetaItem,
-        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
-    ) -> Result<(), ()> {
-        if field.is_some() {
-            tcx.sess
-                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
-
-            return Err(());
-        }
-
-        if let Some(s) = attr.value_str() {
-            *field = Some(mapper(s)?);
-            Ok(())
-        } else {
-            tcx.sess
-                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
-            Err(())
-        }
-    }
-
-    /// Returns the path where dataflow results should be written, or `None`
-    /// `borrowck_graphviz_postflow` was not specified.
-    ///
-    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
-    ///
-    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
-    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
-        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
-        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
-
-        let mut file_name: OsString = analysis_name.into();
-        file_name.push("_");
-        file_name.push(suffix);
-        ret.set_file_name(file_name);
-
-        Some(ret)
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs
deleted file mode 100644
index 35115ca9db7..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/fmt.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
-//! analysis.
-
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::Idx;
-use std::fmt;
-
-/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`.
-pub trait DebugWithContext<C>: Eq + fmt::Debug {
-    fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(self, f)
-    }
-
-    /// Print the difference between `self` and `old`.
-    ///
-    /// This should print nothing if `self == old`.
-    ///
-    /// `+` and `-` are typically used to indicate differences. However, these characters are
-    /// fairly common and may be needed to print a types representation. If using them to indicate
-    /// a diff, prefix them with the "Unit Separator"  control character (␟  U+001F).
-    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self == old {
-            return Ok(());
-        }
-
-        write!(f, "\u{001f}+")?;
-        self.fmt_with(ctxt, f)?;
-
-        if f.alternate() {
-            write!(f, "\n")?;
-        } else {
-            write!(f, "\t")?;
-        }
-
-        write!(f, "\u{001f}-")?;
-        old.fmt_with(ctxt, f)
-    }
-}
-
-/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`.
-pub struct DebugWithAdapter<'a, T, C> {
-    pub this: T,
-    pub ctxt: &'a C,
-}
-
-impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C>
-where
-    T: DebugWithContext<C>,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.this.fmt_with(self.ctxt, f)
-    }
-}
-
-/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`.
-pub struct DebugDiffWithAdapter<'a, T, C> {
-    pub new: T,
-    pub old: T,
-    pub ctxt: &'a C,
-}
-
-impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C>
-where
-    T: DebugWithContext<C>,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.new.fmt_diff_with(&self.old, self.ctxt, f)
-    }
-}
-
-// Impls
-
-impl<T, C> DebugWithContext<C> for BitSet<T>
-where
-    T: Idx + DebugWithContext<C>,
-{
-    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
-    }
-
-    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let size = self.domain_size();
-        assert_eq!(size, old.domain_size());
-
-        let mut set_in_self = HybridBitSet::new_empty(size);
-        let mut cleared_in_self = HybridBitSet::new_empty(size);
-
-        for i in (0..size).map(T::new) {
-            match (self.contains(i), old.contains(i)) {
-                (true, false) => set_in_self.insert(i),
-                (false, true) => cleared_in_self.insert(i),
-                _ => continue,
-            };
-        }
-
-        let mut first = true;
-        for idx in set_in_self.iter() {
-            let delim = if first {
-                "\u{001f}+"
-            } else if f.alternate() {
-                "\n\u{001f}+"
-            } else {
-                ", "
-            };
-
-            write!(f, "{}", delim)?;
-            idx.fmt_with(ctxt, f)?;
-            first = false;
-        }
-
-        if !f.alternate() {
-            first = true;
-            if !set_in_self.is_empty() && !cleared_in_self.is_empty() {
-                write!(f, "\t")?;
-            }
-        }
-
-        for idx in cleared_in_self.iter() {
-            let delim = if first {
-                "\u{001f}-"
-            } else if f.alternate() {
-                "\n\u{001f}-"
-            } else {
-                ", "
-            };
-
-            write!(f, "{}", delim)?;
-            idx.fmt_with(ctxt, f)?;
-            first = false;
-        }
-
-        Ok(())
-    }
-}
-
-impl<T, C> DebugWithContext<C> for &'_ T
-where
-    T: DebugWithContext<C>,
-{
-    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (*self).fmt_with(ctxt, f)
-    }
-
-    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (*self).fmt_diff_with(*old, ctxt, f)
-    }
-}
-
-impl<C> DebugWithContext<C> for rustc_middle::mir::Local {}
-impl<C> DebugWithContext<C> for crate::dataflow::move_paths::InitIndex {}
-
-impl<'tcx, C> DebugWithContext<C> for crate::dataflow::move_paths::MovePathIndex
-where
-    C: crate::dataflow::move_paths::HasMoveData<'tcx>,
-{
-    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", ctxt.move_data().move_paths[*self])
-    }
-}
-
-impl<T, C> DebugWithContext<C> for crate::dataflow::lattice::Dual<T>
-where
-    T: DebugWithContext<C>,
-{
-    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (self.0).fmt_with(ctxt, f)
-    }
-
-    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (self.0).fmt_diff_with(&old.0, ctxt, f)
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
deleted file mode 100644
index 4e54257a1cb..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
+++ /dev/null
@@ -1,644 +0,0 @@
-//! A helpful diagram for debugging dataflow problems.
-
-use std::borrow::Cow;
-use std::lazy::SyncOnceCell;
-use std::{io, ops, str};
-
-use regex::Regex;
-use rustc_graphviz as dot;
-use rustc_middle::mir::{self, BasicBlock, Body, Location};
-
-use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
-use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor};
-use crate::util::graphviz_safe_def_name;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum OutputStyle {
-    AfterOnly,
-    BeforeAndAfter,
-}
-
-impl OutputStyle {
-    fn num_state_columns(&self) -> usize {
-        match self {
-            Self::AfterOnly => 1,
-            Self::BeforeAndAfter => 2,
-        }
-    }
-}
-
-pub struct Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    body: &'a Body<'tcx>,
-    results: &'a Results<'tcx, A>,
-    style: OutputStyle,
-}
-
-impl<A> Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self {
-        Formatter { body, results, style }
-    }
-}
-
-/// A pair of a basic block and an index into that basic blocks `successors`.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct CfgEdge {
-    source: BasicBlock,
-    index: usize,
-}
-
-fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec<CfgEdge> {
-    body[bb]
-        .terminator()
-        .successors()
-        .enumerate()
-        .map(|(index, _)| CfgEdge { source: bb, index })
-        .collect()
-}
-
-impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    type Node = BasicBlock;
-    type Edge = CfgEdge;
-
-    fn graph_id(&self) -> dot::Id<'_> {
-        let name = graphviz_safe_def_name(self.body.source.def_id());
-        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
-    }
-
-    fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
-        dot::Id::new(format!("bb_{}", n.index())).unwrap()
-    }
-
-    fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
-        let mut label = Vec::new();
-        let mut fmt = BlockFormatter {
-            results: ResultsRefCursor::new(self.body, self.results),
-            style: self.style,
-            bg: Background::Light,
-        };
-
-        fmt.write_node_label(&mut label, self.body, *block).unwrap();
-        dot::LabelText::html(String::from_utf8(label).unwrap())
-    }
-
-    fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
-        Some(dot::LabelText::label("none"))
-    }
-
-    fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
-        let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
-        dot::LabelText::label(label.clone())
-    }
-}
-
-impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    type Node = BasicBlock;
-    type Edge = CfgEdge;
-
-    fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
-        self.body.basic_blocks().indices().collect::<Vec<_>>().into()
-    }
-
-    fn edges(&self) -> dot::Edges<'_, Self::Edge> {
-        self.body
-            .basic_blocks()
-            .indices()
-            .flat_map(|bb| dataflow_successors(self.body, bb))
-            .collect::<Vec<_>>()
-            .into()
-    }
-
-    fn source(&self, edge: &Self::Edge) -> Self::Node {
-        edge.source
-    }
-
-    fn target(&self, edge: &Self::Edge) -> Self::Node {
-        self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
-    }
-}
-
-struct BlockFormatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    results: ResultsRefCursor<'a, 'a, 'tcx, A>,
-    bg: Background,
-    style: OutputStyle,
-}
-
-impl<A> BlockFormatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    const HEADER_COLOR: &'static str = "#a0a0a0";
-
-    fn toggle_background(&mut self) -> Background {
-        let bg = self.bg;
-        self.bg = !bg;
-        bg
-    }
-
-    fn write_node_label(
-        &mut self,
-        w: &mut impl io::Write,
-        body: &'a Body<'tcx>,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        //   Sample output:
-        //   +-+-----------------------------------------------+
-        // A |                      bb4                        |
-        //   +-+----------------------------------+------------+
-        // B |                MIR                 |   STATE    |
-        //   +-+----------------------------------+------------+
-        // C | | (on entry)                       | {_0,_2,_3} |
-        //   +-+----------------------------------+------------+
-        // D |0| StorageLive(_7)                  |            |
-        //   +-+----------------------------------+------------+
-        //   |1| StorageLive(_8)                  |            |
-        //   +-+----------------------------------+------------+
-        //   |2| _8 = &mut _1                     | +_8        |
-        //   +-+----------------------------------+------------+
-        // E |T| _4 = const Foo::twiddle(move _2) | -_2        |
-        //   +-+----------------------------------+------------+
-        // F | | (on unwind)                      | {_0,_3,_8} |
-        //   +-+----------------------------------+------------+
-        //   | | (on successful return)           | +_4        |
-        //   +-+----------------------------------+------------+
-
-        // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
-        // children. This is because `xdot` seemed to have a hard time correctly propagating
-        // attributes. Make sure to test the output before trying to remove the redundancy.
-        // Notably, `align` was found to have no effect when applied only to <table>.
-
-        let table_fmt = concat!(
-            " border=\"1\"",
-            " cellborder=\"1\"",
-            " cellspacing=\"0\"",
-            " cellpadding=\"3\"",
-            " sides=\"rb\"",
-        );
-        write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
-
-        // A + B: Block header
-        match self.style {
-            OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?,
-            OutputStyle::BeforeAndAfter => {
-                self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])?
-            }
-        }
-
-        // C: State at start of block
-        self.bg = Background::Light;
-        self.results.seek_to_block_start(block);
-        let block_start_state = self.results.get().clone();
-        self.write_row_with_full_state(w, "", "(on start)")?;
-
-        // D + E: Statement and terminator transfer functions
-        self.write_statements_and_terminator(w, body, block)?;
-
-        // F: State at end of block
-
-        let terminator = body[block].terminator();
-
-        // Write the full dataflow state immediately after the terminator if it differs from the
-        // state at block entry.
-        self.results.seek_to_block_end(block);
-        if self.results.get() != &block_start_state || A::Direction::is_backward() {
-            let after_terminator_name = match terminator.kind {
-                mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
-                _ => "(on end)",
-            };
-
-            self.write_row_with_full_state(w, "", after_terminator_name)?;
-        }
-
-        // Write any changes caused by terminator-specific effects.
-        //
-        // FIXME: These should really be printed as part of each outgoing edge rather than the node
-        // for the basic block itself. That way, we could display terminator-specific effects for
-        // backward dataflow analyses as well as effects for `SwitchInt` terminators.
-        match terminator.kind {
-            mir::TerminatorKind::Call {
-                destination: Some((return_place, _)),
-                ref func,
-                ref args,
-                ..
-            } => {
-                self.write_row(w, "", "(on successful return)", |this, w, fmt| {
-                    let state_on_unwind = this.results.get().clone();
-                    this.results.apply_custom_effect(|analysis, state| {
-                        analysis.apply_call_return_effect(state, block, func, args, return_place);
-                    });
-
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
-                        colspan = this.style.num_state_columns(),
-                        fmt = fmt,
-                        diff = diff_pretty(
-                            this.results.get(),
-                            &state_on_unwind,
-                            this.results.analysis()
-                        ),
-                    )
-                })?;
-            }
-
-            mir::TerminatorKind::Yield { resume, resume_arg, .. } => {
-                self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
-                    let state_on_generator_drop = this.results.get().clone();
-                    this.results.apply_custom_effect(|analysis, state| {
-                        analysis.apply_yield_resume_effect(state, resume, resume_arg);
-                    });
-
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
-                        colspan = this.style.num_state_columns(),
-                        fmt = fmt,
-                        diff = diff_pretty(
-                            this.results.get(),
-                            &state_on_generator_drop,
-                            this.results.analysis()
-                        ),
-                    )
-                })?;
-            }
-
-            _ => {}
-        };
-
-        write!(w, "</table>")
-    }
-
-    fn write_block_header_simple(
-        &mut self,
-        w: &mut impl io::Write,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        //   +-------------------------------------------------+
-        // A |                      bb4                        |
-        //   +-----------------------------------+-------------+
-        // B |                MIR                |    STATE    |
-        //   +-+---------------------------------+-------------+
-        //   | |              ...                |             |
-
-        // A
-        write!(
-            w,
-            concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
-            block_id = block.index(),
-        )?;
-
-        // B
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td colspan="2" {fmt}>MIR</td>"#,
-                r#"<td {fmt}>STATE</td>"#,
-                "</tr>",
-            ),
-            fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
-        )
-    }
-
-    fn write_block_header_with_state_columns(
-        &mut self,
-        w: &mut impl io::Write,
-        block: BasicBlock,
-        state_column_names: &[&str],
-    ) -> io::Result<()> {
-        //   +------------------------------------+-------------+
-        // A |                bb4                 |    STATE    |
-        //   +------------------------------------+------+------+
-        // B |                MIR                 |  GEN | KILL |
-        //   +-+----------------------------------+------+------+
-        //   | |              ...                 |      |      |
-
-        // A
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
-                r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
-                "</tr>",
-            ),
-            fmt = "sides=\"tl\"",
-            num_state_cols = state_column_names.len(),
-            block_id = block.index(),
-        )?;
-
-        // B
-        let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
-        write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
-
-        for name in state_column_names {
-            write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
-        }
-
-        write!(w, "</tr>")
-    }
-
-    fn write_statements_and_terminator(
-        &mut self,
-        w: &mut impl io::Write,
-        body: &'a Body<'tcx>,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style);
-
-        let mut befores = diffs.before.map(|v| v.into_iter());
-        let mut afters = diffs.after.into_iter();
-
-        let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| {
-            if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() }
-        };
-
-        for (i, statement) in body[block].statements.iter().enumerate() {
-            let statement_str = format!("{:?}", statement);
-            let index_str = format!("{}", i);
-
-            let after = next_in_dataflow_order(&mut afters);
-            let before = befores.as_mut().map(next_in_dataflow_order);
-
-            self.write_row(w, &index_str, &statement_str, |_this, w, fmt| {
-                if let Some(before) = before {
-                    write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
-                }
-
-                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
-            })?;
-        }
-
-        let after = next_in_dataflow_order(&mut afters);
-        let before = befores.as_mut().map(next_in_dataflow_order);
-
-        assert!(afters.is_empty());
-        assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty));
-
-        let terminator = body[block].terminator();
-        let mut terminator_str = String::new();
-        terminator.kind.fmt_head(&mut terminator_str).unwrap();
-
-        self.write_row(w, "T", &terminator_str, |_this, w, fmt| {
-            if let Some(before) = before {
-                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
-            }
-
-            write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
-        })
-    }
-
-    /// Write a row with the given index and MIR, using the function argument to fill in the
-    /// "STATE" column(s).
-    fn write_row<W: io::Write>(
-        &mut self,
-        w: &mut W,
-        i: &str,
-        mir: &str,
-        f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
-    ) -> io::Result<()> {
-        let bg = self.toggle_background();
-        let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
-
-        let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
-
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td {fmt} align="right">{i}</td>"#,
-                r#"<td {fmt} align="left">{mir}</td>"#,
-            ),
-            i = i,
-            fmt = fmt,
-            mir = dot::escape_html(mir),
-        )?;
-
-        f(self, w, &fmt)?;
-        write!(w, "</tr>")
-    }
-
-    fn write_row_with_full_state(
-        &mut self,
-        w: &mut impl io::Write,
-        i: &str,
-        mir: &str,
-    ) -> io::Result<()> {
-        self.write_row(w, i, mir, |this, w, fmt| {
-            let state = this.results.get();
-            let analysis = this.results.analysis();
-
-            // FIXME: The full state vector can be quite long. It would be nice to split on commas
-            // and use some text wrapping algorithm.
-            write!(
-                w,
-                r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#,
-                colspan = this.style.num_state_columns(),
-                fmt = fmt,
-                state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }),
-            )
-        })
-    }
-}
-
-struct StateDiffCollector<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    analysis: &'a A,
-    prev_state: A::Domain,
-    before: Option<Vec<String>>,
-    after: Vec<String>,
-}
-
-impl<A> StateDiffCollector<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    fn run(
-        body: &'a mir::Body<'tcx>,
-        block: BasicBlock,
-        results: &'a Results<'tcx, A>,
-        style: OutputStyle,
-    ) -> Self {
-        let mut collector = StateDiffCollector {
-            analysis: &results.analysis,
-            prev_state: results.analysis.bottom_value(body),
-            after: vec![],
-            before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
-        };
-
-        results.visit_with(body, std::iter::once(block), &mut collector);
-        collector
-    }
-}
-
-impl<A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    type FlowState = A::Domain;
-
-    fn visit_block_start(
-        &mut self,
-        state: &Self::FlowState,
-        _block_data: &'mir mir::BasicBlockData<'tcx>,
-        _block: BasicBlock,
-    ) {
-        if A::Direction::is_forward() {
-            self.prev_state.clone_from(state);
-        }
-    }
-
-    fn visit_block_end(
-        &mut self,
-        state: &Self::FlowState,
-        _block_data: &'mir mir::BasicBlockData<'tcx>,
-        _block: BasicBlock,
-    ) {
-        if A::Direction::is_backward() {
-            self.prev_state.clone_from(state);
-        }
-    }
-
-    fn visit_statement_before_primary_effect(
-        &mut self,
-        state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-        if let Some(before) = self.before.as_mut() {
-            before.push(diff_pretty(state, &self.prev_state, self.analysis));
-            self.prev_state.clone_from(state)
-        }
-    }
-
-    fn visit_statement_after_primary_effect(
-        &mut self,
-        state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
-        self.prev_state.clone_from(state)
-    }
-
-    fn visit_terminator_before_primary_effect(
-        &mut self,
-        state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-        if let Some(before) = self.before.as_mut() {
-            before.push(diff_pretty(state, &self.prev_state, self.analysis));
-            self.prev_state.clone_from(state)
-        }
-    }
-
-    fn visit_terminator_after_primary_effect(
-        &mut self,
-        state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
-        self.prev_state.clone_from(state)
-    }
-}
-
-macro_rules! regex {
-    ($re:literal $(,)?) => {{
-        static RE: SyncOnceCell<regex::Regex> = SyncOnceCell::new();
-        RE.get_or_init(|| Regex::new($re).unwrap())
-    }};
-}
-
-fn diff_pretty<T, C>(new: T, old: T, ctxt: &C) -> String
-where
-    T: DebugWithContext<C>,
-{
-    if new == old {
-        return String::new();
-    }
-
-    let re = regex!("\t?\u{001f}([+-])");
-
-    let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt });
-
-    // Replace newlines in the `Debug` output with `<br/>`
-    let raw_diff = raw_diff.replace('\n', r#"<br align="left"/>"#);
-
-    let mut inside_font_tag = false;
-    let html_diff = re.replace_all(&raw_diff, |captures: &regex::Captures<'_>| {
-        let mut ret = String::new();
-        if inside_font_tag {
-            ret.push_str(r#"</font>"#);
-        }
-
-        let tag = match &captures[1] {
-            "+" => r#"<font color="darkgreen">+"#,
-            "-" => r#"<font color="red">-"#,
-            _ => unreachable!(),
-        };
-
-        inside_font_tag = true;
-        ret.push_str(tag);
-        ret
-    });
-
-    let mut html_diff = match html_diff {
-        Cow::Borrowed(_) => return raw_diff,
-        Cow::Owned(s) => s,
-    };
-
-    if inside_font_tag {
-        html_diff.push_str("</font>");
-    }
-
-    html_diff
-}
-
-/// The background color used for zebra-striping the table.
-#[derive(Clone, Copy)]
-enum Background {
-    Light,
-    Dark,
-}
-
-impl Background {
-    fn attr(self) -> &'static str {
-        match self {
-            Self::Dark => "bgcolor=\"#f0f0f0\"",
-            Self::Light => "",
-        }
-    }
-}
-
-impl ops::Not for Background {
-    type Output = Self;
-
-    fn not(self) -> Self {
-        match self {
-            Self::Light => Self::Dark,
-            Self::Dark => Self::Light,
-        }
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs
deleted file mode 100644
index f937b31f4cf..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/lattice.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-//! Traits used to represent [lattices] for use as the domain of a dataflow analysis.
-//!
-//! # Overview
-//!
-//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse
-//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that
-//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are
-//! *incomparable*, not equal.
-//!
-//! ```text
-//!      {X, Y}    <- top
-//!       /  \
-//!    {X}    {Y}
-//!       \  /
-//!        {}      <- bottom
-//!
-//! ```
-//!
-//! The defining characteristic of a lattice—the one that differentiates it from a [partially
-//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for
-//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the
-//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator
-//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and
-//! will work with any join-semilattice, but both should be specified when possible.
-//!
-//! ## `PartialOrd`
-//!
-//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
-//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This
-//! is because most standard library types use lexicographic ordering instead of set inclusion for
-//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
-//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
-//! only benefit would be the ability to check that the least upper (or greatest lower) bound
-//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs.
-//!
-//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
-//! [set inclusion]: https://en.wikipedia.org/wiki/Subset
-//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
-//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
-
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::{Idx, IndexVec};
-use std::iter;
-
-/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements
-/// in the set.
-///
-/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum
-/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
-pub trait JoinSemiLattice: Eq {
-    /// Computes the least upper bound of two elements, storing the result in `self` and returning
-    /// `true` if `self` has changed.
-    ///
-    /// The lattice join operator is abbreviated as `∨`.
-    fn join(&mut self, other: &Self) -> bool;
-}
-
-/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of
-/// elements in the set.
-///
-/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not
-/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both
-/// so that they can be used with [`Dual`].
-///
-/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum
-/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
-pub trait MeetSemiLattice: Eq {
-    /// Computes the greatest lower bound of two elements, storing the result in `self` and
-    /// returning `true` if `self` has changed.
-    ///
-    /// The lattice meet operator is abbreviated as `∧`.
-    fn meet(&mut self, other: &Self) -> bool;
-}
-
-/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
-///
-/// ```text
-///      true
-///        |
-///      false
-/// ```
-impl JoinSemiLattice for bool {
-    fn join(&mut self, other: &Self) -> bool {
-        if let (false, true) = (*self, *other) {
-            *self = true;
-            return true;
-        }
-
-        false
-    }
-}
-
-impl MeetSemiLattice for bool {
-    fn meet(&mut self, other: &Self) -> bool {
-        if let (true, false) = (*self, *other) {
-            *self = false;
-            return true;
-        }
-
-        false
-    }
-}
-
-/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
-/// of the least upper bounds of each element of the tuple (or list).
-///
-/// In other words:
-///     (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ)
-impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
-    fn join(&mut self, other: &Self) -> bool {
-        assert_eq!(self.len(), other.len());
-
-        let mut changed = false;
-        for (a, b) in iter::zip(self, other) {
-            changed |= a.join(b);
-        }
-        changed
-    }
-}
-
-impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        assert_eq!(self.len(), other.len());
-
-        let mut changed = false;
-        for (a, b) in iter::zip(self, other) {
-            changed |= a.meet(b);
-        }
-        changed
-    }
-}
-
-/// A `BitSet` represents the lattice formed by the powerset of all possible values of
-/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
-/// one for each possible value of `T`.
-impl<T: Idx> JoinSemiLattice for BitSet<T> {
-    fn join(&mut self, other: &Self) -> bool {
-        self.union(other)
-    }
-}
-
-impl<T: Idx> MeetSemiLattice for BitSet<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        self.intersect(other)
-    }
-}
-
-/// The counterpart of a given semilattice `T` using the [inverse order].
-///
-/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a
-/// powerset has the empty set as its top element and the full set as its bottom element and uses
-/// set *intersection* as its join operator.
-///
-/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory)
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct Dual<T>(pub T);
-
-impl<T> std::borrow::Borrow<T> for Dual<T> {
-    fn borrow(&self) -> &T {
-        &self.0
-    }
-}
-
-impl<T> std::borrow::BorrowMut<T> for Dual<T> {
-    fn borrow_mut(&mut self) -> &mut T {
-        &mut self.0
-    }
-}
-
-impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> {
-    fn join(&mut self, other: &Self) -> bool {
-        self.0.meet(&other.0)
-    }
-}
-
-impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        self.0.join(&other.0)
-    }
-}
-
-/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
-/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]:
-///
-/// ```text
-///         top
-///       / /  \ \
-/// all possible values of `T`
-///       \ \  / /
-///        bottom
-/// ```
-///
-/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum FlatSet<T> {
-    Bottom,
-    Elem(T),
-    Top,
-}
-
-impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> {
-    fn join(&mut self, other: &Self) -> bool {
-        let result = match (&*self, other) {
-            (Self::Top, _) | (_, Self::Bottom) => return false,
-            (Self::Elem(a), Self::Elem(b)) if a == b => return false,
-
-            (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()),
-
-            _ => Self::Top,
-        };
-
-        *self = result;
-        true
-    }
-}
-
-impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
-    fn meet(&mut self, other: &Self) -> bool {
-        let result = match (&*self, other) {
-            (Self::Bottom, _) | (_, Self::Top) => return false,
-            (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false,
-
-            (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()),
-
-            _ => Self::Bottom,
-        };
-
-        *self = result;
-        true
-    }
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs
deleted file mode 100644
index 0bf62db1ada..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/mod.rs
+++ /dev/null
@@ -1,546 +0,0 @@
-//! A framework that can express both [gen-kill] and generic dataflow problems.
-//!
-//! To actually use this framework, you must implement either the `Analysis` or the
-//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
-//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
-//! `impls` module contains several examples of gen/kill dataflow analyses.
-//!
-//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
-//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
-//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
-//! `visit_results`. The following example uses the `ResultsCursor` approach.
-//!
-//! ```ignore (cross-crate-imports)
-//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
-//!
-//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
-//!     let analysis = MyAnalysis::new()
-//!         .into_engine(tcx, body)
-//!         .iterate_to_fixpoint()
-//!         .into_results_cursor(body);
-//!
-//!     // Print the dataflow state *after* each statement in the start block.
-//!     for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
-//!         cursor.seek_after(Location { block: START_BLOCK, statement_index });
-//!         let state = cursor.get();
-//!         println!("{:?}", state);
-//!     }
-//! }
-//! ```
-//!
-//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
-
-use std::borrow::BorrowMut;
-use std::cmp::Ordering;
-
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::Idx;
-use rustc_middle::mir::{self, BasicBlock, Location};
-use rustc_middle::ty::TyCtxt;
-
-mod cursor;
-mod direction;
-mod engine;
-pub mod fmt;
-pub mod graphviz;
-pub mod lattice;
-mod visitor;
-
-pub use self::cursor::{ResultsCursor, ResultsRefCursor};
-pub use self::direction::{Backward, Direction, Forward};
-pub use self::engine::{Engine, Results};
-pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
-pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
-
-/// Define the domain of a dataflow problem.
-///
-/// This trait specifies the lattice on which this analysis operates (the domain) as well as its
-/// initial value at the entry point of each basic block.
-pub trait AnalysisDomain<'tcx> {
-    /// The type that holds the dataflow state at any given point in the program.
-    type Domain: Clone + JoinSemiLattice;
-
-    /// The direction of this analysis. Either `Forward` or `Backward`.
-    type Direction: Direction = Forward;
-
-    /// A descriptive name for this analysis. Used only for debugging.
-    ///
-    /// This name should be brief and contain no spaces, periods or other characters that are not
-    /// suitable as part of a filename.
-    const NAME: &'static str;
-
-    /// The initial value of the dataflow state upon entry to each basic block.
-    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain;
-
-    /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`.
-    ///
-    /// For backward analyses, initial state besides the bottom value is not yet supported. Trying
-    /// to mutate the initial state will result in a panic.
-    //
-    // FIXME: For backward dataflow analyses, the initial state should be applied to every basic
-    // block where control flow could exit the MIR body (e.g., those terminated with `return` or
-    // `resume`). It's not obvious how to handle `yield` points in generators, however.
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
-}
-
-/// A dataflow problem with an arbitrarily complex transfer function.
-///
-/// # Convergence
-///
-/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a
-/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence,
-/// your transfer functions must maintain the following invariant:
-///
-/// > If the dataflow state **before** some point in the program changes to be greater
-/// than the prior state **before** that point, the dataflow state **after** that point must
-/// also change to be greater than the prior state **after** that point.
-///
-/// This invariant guarantees that the dataflow state at a given point in the program increases
-/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
-/// to the same point in the program at different points in time. The dataflow state at a given
-/// point in the program may or may not be greater than the state at any preceding point.
-pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
-    /// Updates the current dataflow state with the effect of evaluating a statement.
-    fn apply_statement_effect(
-        &self,
-        state: &mut Self::Domain,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    /// Updates the current dataflow state with an effect that occurs immediately *before* the
-    /// given statement.
-    ///
-    /// This method is useful if the consumer of the results of this analysis needs only to observe
-    /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
-    /// analyses should not implement this without implementing `apply_statement_effect`.
-    fn apply_before_statement_effect(
-        &self,
-        _state: &mut Self::Domain,
-        _statement: &mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Updates the current dataflow state with the effect of evaluating a terminator.
-    ///
-    /// The effect of a successful return from a `Call` terminator should **not** be accounted for
-    /// in this function. That should go in `apply_call_return_effect`. For example, in the
-    /// `InitializedPlaces` analyses, the return place for a function call is not marked as
-    /// initialized here.
-    fn apply_terminator_effect(
-        &self,
-        state: &mut Self::Domain,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    /// Updates the current dataflow state with an effect that occurs immediately *before* the
-    /// given terminator.
-    ///
-    /// This method is useful if the consumer of the results of this analysis needs only to observe
-    /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
-    /// analyses should not implement this without implementing `apply_terminator_effect`.
-    fn apply_before_terminator_effect(
-        &self,
-        _state: &mut Self::Domain,
-        _terminator: &mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /* Edge-specific effects */
-
-    /// Updates the current dataflow state with the effect of a successful return from a `Call`
-    /// terminator.
-    ///
-    /// This is separate from `apply_terminator_effect` to properly track state across unwind
-    /// edges.
-    fn apply_call_return_effect(
-        &self,
-        state: &mut Self::Domain,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
-    );
-
-    /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
-    ///
-    /// This is similar to `apply_call_return_effect` in that it only takes place after the
-    /// generator is resumed, not when it is dropped.
-    ///
-    /// By default, no effects happen.
-    fn apply_yield_resume_effect(
-        &self,
-        _state: &mut Self::Domain,
-        _resume_block: BasicBlock,
-        _resume_place: mir::Place<'tcx>,
-    ) {
-    }
-
-    /// Updates the current dataflow state with the effect of taking a particular branch in a
-    /// `SwitchInt` terminator.
-    ///
-    /// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain`
-    /// directly, overriders of this method must pass a callback to
-    /// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and
-    /// will have access to the dataflow state that will be propagated along that edge.
-    ///
-    /// This interface is somewhat more complex than the other visitor-like "effect" methods.
-    /// However, it is both more ergonomic—callers don't need to recompute or cache information
-    /// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
-    /// engine doesn't need to clone the exit state for a block unless
-    /// `SwitchIntEdgeEffects::apply` is actually called.
-    ///
-    /// FIXME: This class of effects is not supported for backward dataflow analyses.
-    fn apply_switch_int_edge_effects(
-        &self,
-        _block: BasicBlock,
-        _discr: &mir::Operand<'tcx>,
-        _apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
-    ) {
-    }
-
-    /* Extension methods */
-
-    /// Creates an `Engine` to find the fixpoint for this dataflow problem.
-    ///
-    /// You shouldn't need to override this outside this module, since the combination of the
-    /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
-    /// Its purpose is to enable method chaining like so:
-    ///
-    /// ```ignore (cross-crate-imports)
-    /// let results = MyAnalysis::new(tcx, body)
-    ///     .into_engine(tcx, body, def_id)
-    ///     .iterate_to_fixpoint()
-    ///     .into_results_cursor(body);
-    /// ```
-    fn into_engine(self, tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Engine<'mir, 'tcx, Self>
-    where
-        Self: Sized,
-    {
-        Engine::new_generic(tcx, body, self)
-    }
-}
-
-/// A gen/kill dataflow problem.
-///
-/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
-/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
-/// functions for each statement in this way, the transfer function for an entire basic block can
-/// be computed efficiently.
-///
-/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
-pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
-    type Idx: Idx;
-
-    /// See `Analysis::apply_statement_effect`.
-    fn statement_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    /// See `Analysis::apply_before_statement_effect`.
-    fn before_statement_effect(
-        &self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// See `Analysis::apply_terminator_effect`.
-    fn terminator_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    /// See `Analysis::apply_before_terminator_effect`.
-    fn before_terminator_effect(
-        &self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _terminator: &mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /* Edge-specific effects */
-
-    /// See `Analysis::apply_call_return_effect`.
-    fn call_return_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
-    );
-
-    /// See `Analysis::apply_yield_resume_effect`.
-    fn yield_resume_effect(
-        &self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _resume_block: BasicBlock,
-        _resume_place: mir::Place<'tcx>,
-    ) {
-    }
-
-    /// See `Analysis::apply_switch_int_edge_effects`.
-    fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
-        &self,
-        _block: BasicBlock,
-        _discr: &mir::Operand<'tcx>,
-        _edge_effects: &mut impl SwitchIntEdgeEffects<G>,
-    ) {
-    }
-}
-
-impl<A> Analysis<'tcx> for A
-where
-    A: GenKillAnalysis<'tcx>,
-    A::Domain: GenKill<A::Idx> + BorrowMut<BitSet<A::Idx>>,
-{
-    fn apply_statement_effect(
-        &self,
-        state: &mut A::Domain,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.statement_effect(state, statement, location);
-    }
-
-    fn apply_before_statement_effect(
-        &self,
-        state: &mut A::Domain,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.before_statement_effect(state, statement, location);
-    }
-
-    fn apply_terminator_effect(
-        &self,
-        state: &mut A::Domain,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        self.terminator_effect(state, terminator, location);
-    }
-
-    fn apply_before_terminator_effect(
-        &self,
-        state: &mut A::Domain,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        self.before_terminator_effect(state, terminator, location);
-    }
-
-    /* Edge-specific effects */
-
-    fn apply_call_return_effect(
-        &self,
-        state: &mut A::Domain,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: mir::Place<'tcx>,
-    ) {
-        self.call_return_effect(state, block, func, args, return_place);
-    }
-
-    fn apply_yield_resume_effect(
-        &self,
-        state: &mut A::Domain,
-        resume_block: BasicBlock,
-        resume_place: mir::Place<'tcx>,
-    ) {
-        self.yield_resume_effect(state, resume_block, resume_place);
-    }
-
-    fn apply_switch_int_edge_effects(
-        &self,
-        block: BasicBlock,
-        discr: &mir::Operand<'tcx>,
-        edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,
-    ) {
-        self.switch_int_edge_effects(block, discr, edge_effects);
-    }
-
-    /* Extension methods */
-
-    fn into_engine(self, tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Engine<'mir, 'tcx, Self>
-    where
-        Self: Sized,
-    {
-        Engine::new_gen_kill(tcx, body, self)
-    }
-}
-
-/// The legal operations for a transfer function in a gen/kill problem.
-///
-/// This abstraction exists because there are two different contexts in which we call the methods in
-/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
-/// applied multiple times, such as when computing the cumulative transfer function for each block.
-/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
-/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
-/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
-/// building up a `GenKillSet` and then throwing it away.
-pub trait GenKill<T> {
-    /// Inserts `elem` into the state vector.
-    fn gen(&mut self, elem: T);
-
-    /// Removes `elem` from the state vector.
-    fn kill(&mut self, elem: T);
-
-    /// Calls `gen` for each element in `elems`.
-    fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
-        for elem in elems {
-            self.gen(elem);
-        }
-    }
-
-    /// Calls `kill` for each element in `elems`.
-    fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
-        for elem in elems {
-            self.kill(elem);
-        }
-    }
-}
-
-/// Stores a transfer function for a gen/kill problem.
-///
-/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
-/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
-/// the same element, the most recent one takes precedence.
-#[derive(Clone)]
-pub struct GenKillSet<T> {
-    gen: HybridBitSet<T>,
-    kill: HybridBitSet<T>,
-}
-
-impl<T: Idx> GenKillSet<T> {
-    /// Creates a new transfer function that will leave the dataflow state unchanged.
-    pub fn identity(universe: usize) -> Self {
-        GenKillSet {
-            gen: HybridBitSet::new_empty(universe),
-            kill: HybridBitSet::new_empty(universe),
-        }
-    }
-
-    pub fn apply(&self, state: &mut BitSet<T>) {
-        state.union(&self.gen);
-        state.subtract(&self.kill);
-    }
-}
-
-impl<T: Idx> GenKill<T> for GenKillSet<T> {
-    fn gen(&mut self, elem: T) {
-        self.gen.insert(elem);
-        self.kill.remove(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.kill.insert(elem);
-        self.gen.remove(elem);
-    }
-}
-
-impl<T: Idx> GenKill<T> for BitSet<T> {
-    fn gen(&mut self, elem: T) {
-        self.insert(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.remove(elem);
-    }
-}
-
-impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> {
-    fn gen(&mut self, elem: T) {
-        self.0.insert(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.0.remove(elem);
-    }
-}
-
-// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Effect {
-    /// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or
-    /// terminator).
-    Before,
-
-    /// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator).
-    Primary,
-}
-
-impl Effect {
-    pub const fn at_index(self, statement_index: usize) -> EffectIndex {
-        EffectIndex { effect: self, statement_index }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub struct EffectIndex {
-    statement_index: usize,
-    effect: Effect,
-}
-
-impl EffectIndex {
-    fn next_in_forward_order(self) -> Self {
-        match self.effect {
-            Effect::Before => Effect::Primary.at_index(self.statement_index),
-            Effect::Primary => Effect::Before.at_index(self.statement_index + 1),
-        }
-    }
-
-    fn next_in_backward_order(self) -> Self {
-        match self.effect {
-            Effect::Before => Effect::Primary.at_index(self.statement_index),
-            Effect::Primary => Effect::Before.at_index(self.statement_index - 1),
-        }
-    }
-
-    /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other`
-    /// in forward order.
-    fn precedes_in_forward_order(self, other: Self) -> bool {
-        let ord = self
-            .statement_index
-            .cmp(&other.statement_index)
-            .then_with(|| self.effect.cmp(&other.effect));
-        ord == Ordering::Less
-    }
-
-    /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other`
-    /// in backward order.
-    fn precedes_in_backward_order(self, other: Self) -> bool {
-        let ord = other
-            .statement_index
-            .cmp(&self.statement_index)
-            .then_with(|| self.effect.cmp(&other.effect));
-        ord == Ordering::Less
-    }
-}
-
-pub struct SwitchIntTarget {
-    pub value: Option<u128>,
-    pub target: BasicBlock,
-}
-
-/// A type that records the edge-specific effects for a `SwitchInt` terminator.
-pub trait SwitchIntEdgeEffects<D> {
-    /// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
-    /// records the results.
-    fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs
deleted file mode 100644
index a5989121679..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/tests.rs
+++ /dev/null
@@ -1,320 +0,0 @@
-//! A test for the logic that updates the state in a `ResultsCursor` during seek.
-
-use std::marker::PhantomData;
-
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
-use rustc_middle::mir::{self, BasicBlock, Location};
-use rustc_middle::ty;
-use rustc_span::DUMMY_SP;
-
-use super::*;
-
-/// Creates a `mir::Body` with a few disconnected basic blocks.
-///
-/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not
-/// important.
-fn mock_body() -> mir::Body<'static> {
-    let source_info = mir::SourceInfo::outermost(DUMMY_SP);
-
-    let mut blocks = IndexVec::new();
-    let mut block = |n, kind| {
-        let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop };
-
-        blocks.push(mir::BasicBlockData {
-            statements: std::iter::repeat(&nop).cloned().take(n).collect(),
-            terminator: Some(mir::Terminator { source_info, kind }),
-            is_cleanup: false,
-        })
-    };
-
-    let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() };
-
-    block(4, mir::TerminatorKind::Return);
-    block(1, mir::TerminatorKind::Return);
-    block(
-        2,
-        mir::TerminatorKind::Call {
-            func: mir::Operand::Copy(dummy_place.clone()),
-            args: vec![],
-            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
-            cleanup: None,
-            from_hir_call: false,
-            fn_span: DUMMY_SP,
-        },
-    );
-    block(3, mir::TerminatorKind::Return);
-    block(0, mir::TerminatorKind::Return);
-    block(
-        4,
-        mir::TerminatorKind::Call {
-            func: mir::Operand::Copy(dummy_place.clone()),
-            args: vec![],
-            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
-            cleanup: None,
-            from_hir_call: false,
-            fn_span: DUMMY_SP,
-        },
-    );
-
-    mir::Body::new_cfg_only(blocks)
-}
-
-/// A dataflow analysis whose state is unique at every possible `SeekTarget`.
-///
-/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and
-/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is
-/// *globally* unique (see `mock_entry_set`).
-///
-/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each
-/// location ("+x" indicates that "x" is added to the state).
-///
-/// | Location               | Before            | After  |
-/// |------------------------|-------------------|--------|
-/// | (on_entry)             | {102}                     ||
-/// | statement 0            | +0                | +1     |
-/// | statement 1            | +2                | +3     |
-/// | `Call` terminator      | +4                | +5     |
-/// | (on unwind)            | {102,0,1,2,3,4,5}         ||
-///
-/// The `102` in the block's entry set is derived from the basic block index and ensures that the
-/// expected state is unique across all basic blocks. Remember, it is generated by
-/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint.
-struct MockAnalysis<'tcx, D> {
-    body: &'tcx mir::Body<'tcx>,
-    dir: PhantomData<D>,
-}
-
-impl<D: Direction> MockAnalysis<'tcx, D> {
-    const BASIC_BLOCK_OFFSET: usize = 100;
-
-    /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
-    /// avoid colliding with the statement/terminator effects.
-    fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
-        let mut ret = self.bottom_value(self.body);
-        ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
-        ret
-    }
-
-    fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
-        let empty = self.bottom_value(self.body);
-        let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
-
-        for (bb, _) in self.body.basic_blocks().iter_enumerated() {
-            ret[bb] = self.mock_entry_set(bb);
-        }
-
-        ret
-    }
-
-    /// Returns the index that should be added to the dataflow state at the given target.
-    fn effect(&self, loc: EffectIndex) -> usize {
-        let idx = match loc.effect {
-            Effect::Before => loc.statement_index * 2,
-            Effect::Primary => loc.statement_index * 2 + 1,
-        };
-
-        assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block");
-        idx
-    }
-
-    /// Returns the expected state at the given `SeekTarget`.
-    ///
-    /// This is the union of index of the target basic block, the index assigned to the
-    /// target statement or terminator, and the indices of all preceding statements in the target
-    /// basic block.
-    ///
-    /// For example, the expected state when calling
-    /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })`
-    /// would be `[102, 0, 1, 2, 3, 4]`.
-    fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
-        let block = target.block();
-        let mut ret = self.bottom_value(self.body);
-        ret.insert(Self::BASIC_BLOCK_OFFSET + block.index());
-
-        let target = match target {
-            SeekTarget::BlockEntry { .. } => return ret,
-            SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index),
-            SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index),
-        };
-
-        let mut pos = if D::is_forward() {
-            Effect::Before.at_index(0)
-        } else {
-            Effect::Before.at_index(self.body[block].statements.len())
-        };
-
-        loop {
-            ret.insert(self.effect(pos));
-
-            if pos == target {
-                return ret;
-            }
-
-            if D::is_forward() {
-                pos = pos.next_in_forward_order();
-            } else {
-                pos = pos.next_in_backward_order();
-            }
-        }
-    }
-}
-
-impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
-    type Domain = BitSet<usize>;
-    type Direction = D;
-
-    const NAME: &'static str = "mock";
-
-    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
-        BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len())
-    }
-
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
-        unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
-    }
-}
-
-impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
-    fn apply_statement_effect(
-        &self,
-        state: &mut Self::Domain,
-        _statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect(Effect::Primary.at_index(location.statement_index));
-        assert!(state.insert(idx));
-    }
-
-    fn apply_before_statement_effect(
-        &self,
-        state: &mut Self::Domain,
-        _statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect(Effect::Before.at_index(location.statement_index));
-        assert!(state.insert(idx));
-    }
-
-    fn apply_terminator_effect(
-        &self,
-        state: &mut Self::Domain,
-        _terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect(Effect::Primary.at_index(location.statement_index));
-        assert!(state.insert(idx));
-    }
-
-    fn apply_before_terminator_effect(
-        &self,
-        state: &mut Self::Domain,
-        _terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect(Effect::Before.at_index(location.statement_index));
-        assert!(state.insert(idx));
-    }
-
-    fn apply_call_return_effect(
-        &self,
-        _state: &mut Self::Domain,
-        _block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _return_place: mir::Place<'tcx>,
-    ) {
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-enum SeekTarget {
-    BlockEntry(BasicBlock),
-    Before(Location),
-    After(Location),
-}
-
-impl SeekTarget {
-    fn block(&self) -> BasicBlock {
-        use SeekTarget::*;
-
-        match *self {
-            BlockEntry(block) => block,
-            Before(loc) | After(loc) => loc.block,
-        }
-    }
-
-    /// An iterator over all possible `SeekTarget`s in a given block in order, starting with
-    /// `BlockEntry`.
-    fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator<Item = Self> {
-        let statements_and_terminator = (0..=body[block].statements.len())
-            .flat_map(|i| (0..2).map(move |j| (i, j)))
-            .map(move |(i, kind)| {
-                let loc = Location { block, statement_index: i };
-                match kind {
-                    0 => SeekTarget::Before(loc),
-                    1 => SeekTarget::After(loc),
-                    _ => unreachable!(),
-                }
-            });
-
-        std::iter::once(SeekTarget::BlockEntry(block)).chain(statements_and_terminator)
-    }
-}
-
-fn test_cursor<D: Direction>(analysis: MockAnalysis<'tcx, D>) {
-    let body = analysis.body;
-
-    let mut cursor =
-        Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
-
-    let every_target = || {
-        body.basic_blocks()
-            .iter_enumerated()
-            .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
-    };
-
-    let mut seek_to_target = |targ| {
-        use SeekTarget::*;
-
-        match targ {
-            BlockEntry(block) => cursor.seek_to_block_entry(block),
-            Before(loc) => cursor.seek_before_primary_effect(loc),
-            After(loc) => cursor.seek_after_primary_effect(loc),
-        }
-
-        assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ));
-    };
-
-    // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`.
-    //
-    // By resetting the cursor to `from` each time it changes, we end up checking some edges twice.
-    // What we really want is an Eulerian cycle for the complete digraph over all possible
-    // `SeekTarget`s, but it's not worth spending the time to compute it.
-    for from in every_target() {
-        seek_to_target(from);
-
-        for to in every_target() {
-            dbg!(from);
-            dbg!(to);
-            seek_to_target(to);
-            seek_to_target(from);
-        }
-    }
-}
-
-#[test]
-fn backward_cursor() {
-    let body = mock_body();
-    let body = &body;
-    let analysis = MockAnalysis { body, dir: PhantomData::<Backward> };
-    test_cursor(analysis)
-}
-
-#[test]
-fn forward_cursor() {
-    let body = mock_body();
-    let body = &body;
-    let analysis = MockAnalysis { body, dir: PhantomData::<Forward> };
-    test_cursor(analysis)
-}
diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs
deleted file mode 100644
index 84136c4d78c..00000000000
--- a/compiler/rustc_mir/src/dataflow/framework/visitor.rs
+++ /dev/null
@@ -1,187 +0,0 @@
-use rustc_middle::mir::{self, BasicBlock, Location};
-
-use super::{Analysis, Direction, Results};
-
-/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
-/// dataflow state at that location.
-pub fn visit_results<F, V>(
-    body: &'mir mir::Body<'tcx>,
-    blocks: impl IntoIterator<Item = BasicBlock>,
-    results: &V,
-    vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
-) where
-    V: ResultsVisitable<'tcx, FlowState = F>,
-{
-    let mut state = results.new_flow_state(body);
-
-    #[cfg(debug_assertions)]
-    let reachable_blocks = mir::traversal::reachable_as_bitset(body);
-
-    for block in blocks {
-        #[cfg(debug_assertions)]
-        assert!(reachable_blocks.contains(block));
-
-        let block_data = &body[block];
-        V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
-    }
-}
-
-pub trait ResultsVisitor<'mir, 'tcx> {
-    type FlowState;
-
-    fn visit_block_start(
-        &mut self,
-        _state: &Self::FlowState,
-        _block_data: &'mir mir::BasicBlockData<'tcx>,
-        _block: BasicBlock,
-    ) {
-    }
-
-    /// Called with the `before_statement_effect` of the given statement applied to `state` but not
-    /// its `statement_effect`.
-    fn visit_statement_before_primary_effect(
-        &mut self,
-        _state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with both the `before_statement_effect` and the `statement_effect` of the given
-    /// statement applied to `state`.
-    fn visit_statement_after_primary_effect(
-        &mut self,
-        _state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not
-    /// its `terminator_effect`.
-    fn visit_terminator_before_primary_effect(
-        &mut self,
-        _state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given
-    /// terminator applied to `state`.
-    ///
-    /// The `call_return_effect` (if one exists) will *not* be applied to `state`.
-    fn visit_terminator_after_primary_effect(
-        &mut self,
-        _state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    fn visit_block_end(
-        &mut self,
-        _state: &Self::FlowState,
-        _block_data: &'mir mir::BasicBlockData<'tcx>,
-        _block: BasicBlock,
-    ) {
-    }
-}
-
-/// Things that can be visited by a `ResultsVisitor`.
-///
-/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously.
-/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below.
-pub trait ResultsVisitable<'tcx> {
-    type Direction: Direction;
-    type FlowState;
-
-    /// Creates an empty `FlowState` to hold the transient state for these dataflow results.
-    ///
-    /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_entry`
-    /// before it can be observed by a `ResultsVisitor`.
-    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState;
-
-    fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock);
-
-    fn reconstruct_before_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_before_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-}
-
-impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    type FlowState = A::Domain;
-
-    type Direction = A::Direction;
-
-    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
-        self.analysis.bottom_value(body)
-    }
-
-    fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
-        state.clone_from(&self.entry_set_for_block(block));
-    }
-
-    fn reconstruct_before_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        stmt: &mir::Statement<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_before_statement_effect(state, stmt, loc);
-    }
-
-    fn reconstruct_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        stmt: &mir::Statement<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_statement_effect(state, stmt, loc);
-    }
-
-    fn reconstruct_before_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        term: &mir::Terminator<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_before_terminator_effect(state, term, loc);
-    }
-
-    fn reconstruct_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        term: &mir::Terminator<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_terminator_effect(state, term, loc);
-    }
-}