Auto merge of #76044 - ecstatic-morse:dataflow-lattice, r=oli-obk
Support dataflow problems on arbitrary lattices This PR implements last of the proposed extensions I mentioned in the design meeting for the original dataflow refactor. It extends the current dataflow framework to work with arbitrary lattices, not just `BitSet`s. This is a prerequisite for dataflow-enabled MIR const-propagation. Personally, I am skeptical of the usefulness of doing const-propagation pre-monomorphization, since many useful constants only become known after monomorphization (e.g. `size_of::<T>()`) and users have a natural tendency to hand-optimize the rest. It's probably worth exprimenting with, however, and others have shown interest cc `@rust-lang/wg-mir-opt.` The `Idx` associated type is moved from `AnalysisDomain` to `GenKillAnalysis` and replaced with an associated `Domain` type that must implement `JoinSemiLattice`. Like before, each `Analysis` defines the "bottom value" for its domain, but can no longer override the dataflow join operator. Analyses that want to use set intersection must now use the `lattice::Dual` newtype. `GenKillAnalysis` impls have an additional requirement that `Self::Domain: BorrowMut<BitSet<Self::Idx>>`, which effectively means that they must use `BitSet<Self::Idx>` or `lattice::Dual<BitSet<Self::Idx>>` as their domain. Most of these changes were mechanical. However, because a `Domain` is no longer always a powerset of some index type, we can no longer use an `IndexVec<BasicBlock, GenKillSet<A::Idx>>>` to store cached block transfer functions. Instead, we use a boxed `dyn Fn` trait object. I discuss a few alternatives to the current approach in a commit message. The majority of new lines of code are to preserve existing Graphviz diagrams for those unlucky enough to have to debug dataflow analyses. I find these diagrams incredibly useful when things are going wrong and considered regressing them unacceptable, especially the pretty-printing of `MovePathIndex`s, which are used in many dataflow analyses. This required a parallel `fmt` trait used only for printing dataflow domains, as well as a refactoring of the `graphviz` module now that we cannot expect the domain to be a `BitSet`. Some features did have to be removed, such as the gen/kill display mode (which I didn't use but existed to mirror the output of the old dataflow framework) and line wrapping. Since I had to rewrite much of it anyway, I took the opportunity to switch to a `Visitor` for printing dataflow state diffs instead of using cursors, which are error prone for code that must be generic over both forward and backward analyses. As a side-effect of this change, we no longer have quadratic behavior when writing graphviz diagrams for backward dataflow analyses. r? `@pnkfelix`
This commit is contained in:
@@ -18,7 +18,7 @@ pub trait Direction {
|
||||
/// `effects.start()` must precede or equal `effects.end()` in this direction.
|
||||
fn apply_effects_in_range<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
effects: RangeInclusive<EffectIndex>,
|
||||
@@ -27,7 +27,7 @@ pub trait Direction {
|
||||
|
||||
fn apply_effects_in_block<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
) where
|
||||
@@ -55,9 +55,9 @@ pub trait Direction {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
dead_unwinds: Option<&BitSet<BasicBlock>>,
|
||||
exit_state: &mut BitSet<A::Idx>,
|
||||
exit_state: &mut A::Domain,
|
||||
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
||||
propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
|
||||
propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||
) where
|
||||
A: Analysis<'tcx>;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ impl Direction for Backward {
|
||||
|
||||
fn apply_effects_in_block<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
) where
|
||||
@@ -112,7 +112,7 @@ impl Direction for Backward {
|
||||
|
||||
fn apply_effects_in_range<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
effects: RangeInclusive<EffectIndex>,
|
||||
@@ -224,9 +224,9 @@ impl Direction for Backward {
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
dead_unwinds: Option<&BitSet<BasicBlock>>,
|
||||
exit_state: &mut BitSet<A::Idx>,
|
||||
exit_state: &mut A::Domain,
|
||||
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
||||
mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
|
||||
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
@@ -281,7 +281,7 @@ impl Direction for Forward {
|
||||
|
||||
fn apply_effects_in_block<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
) where
|
||||
@@ -321,7 +321,7 @@ impl Direction for Forward {
|
||||
|
||||
fn apply_effects_in_range<A>(
|
||||
analysis: &A,
|
||||
state: &mut BitSet<A::Idx>,
|
||||
state: &mut A::Domain,
|
||||
block: BasicBlock,
|
||||
block_data: &mir::BasicBlockData<'tcx>,
|
||||
effects: RangeInclusive<EffectIndex>,
|
||||
@@ -428,9 +428,9 @@ impl Direction for Forward {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &mir::Body<'tcx>,
|
||||
dead_unwinds: Option<&BitSet<BasicBlock>>,
|
||||
exit_state: &mut BitSet<A::Idx>,
|
||||
exit_state: &mut A::Domain,
|
||||
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
|
||||
mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
|
||||
mut propagate: impl FnMut(BasicBlock, &A::Domain),
|
||||
) where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
@@ -499,7 +499,7 @@ impl Direction for Forward {
|
||||
// MIR building adds discriminants to the `values` array in the same order as they
|
||||
// are yielded by `AdtDef::discriminants`. We rely on this to match each
|
||||
// discriminant in `values` to its corresponding variant in linear time.
|
||||
let mut tmp = BitSet::new_empty(exit_state.domain_size());
|
||||
let mut tmp = analysis.bottom_value(body);
|
||||
let mut discriminants = enum_def.discriminants(tcx);
|
||||
for (value, target) in values.iter().zip(targets.iter().copied()) {
|
||||
let (variant_idx, _) =
|
||||
@@ -508,7 +508,7 @@ impl Direction for Forward {
|
||||
from that of `SwitchInt::values`",
|
||||
);
|
||||
|
||||
tmp.overwrite(exit_state);
|
||||
tmp.clone_from(exit_state);
|
||||
analysis.apply_discriminant_switch_effect(
|
||||
&mut tmp,
|
||||
bb,
|
||||
|
||||
Reference in New Issue
Block a user