Skip to content

Commit

Permalink
Implement dataflow state joins through the analysis. Make `JoinSemiLa…
Browse files Browse the repository at this point in the history
…ttice` the default implementation when the join does not require extra context.
  • Loading branch information
Jarcho committed Aug 18, 2023
1 parent c1699a7 commit b2b0317
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 113 deletions.
82 changes: 48 additions & 34 deletions compiler/rustc_mir_dataflow/src/framework/direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub trait Direction {
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
propagate: impl FnMut(BasicBlock, &A::Domain),
propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>;
}
Expand Down Expand Up @@ -221,7 +221,7 @@ impl Direction for Backward {
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
mut propagate: impl FnMut(BasicBlock, &A::Domain),
mut propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
Expand All @@ -237,7 +237,7 @@ impl Direction for Backward {
pred,
CallReturnPlaces::Call(destination),
);
propagate(pred, &tmp);
propagate(analysis, pred, &tmp);
}

mir::TerminatorKind::InlineAsm {
Expand All @@ -249,13 +249,13 @@ impl Direction for Backward {
pred,
CallReturnPlaces::InlineAsm(operands),
);
propagate(pred, &tmp);
propagate(analysis, 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);
propagate(analysis, pred, &tmp);
}

mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
Expand All @@ -271,11 +271,11 @@ impl Direction for Backward {
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);

if !applier.effects_applied {
propagate(pred, exit_state)
propagate(analysis, pred, exit_state)
}
}

_ => propagate(pred, exit_state),
_ => propagate(analysis, pred, exit_state),
}
}
}
Expand All @@ -290,12 +290,17 @@ struct BackwardSwitchIntEdgeEffectsApplier<'a, 'tcx, D, F> {
effects_applied: bool,
}

impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
impl<'tcx, A, F> super::SwitchIntEdgeEffects<'tcx, A>
for BackwardSwitchIntEdgeEffectsApplier<'_, '_, A::Domain, F>
where
D: Clone,
F: FnMut(BasicBlock, &D),
A: Analysis<'tcx>,
F: FnMut(&mut A, BasicBlock, &A::Domain),
{
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
fn apply(
&mut self,
analysis: &mut A,
mut apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, SwitchIntTarget),
) {
assert!(!self.effects_applied);

let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
Expand All @@ -304,8 +309,8 @@ where
let mut tmp = None;
for target in targets {
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
apply_edge_effect(tmp, target);
(self.propagate)(self.pred, tmp);
apply_edge_effect(analysis, tmp, target);
(self.propagate)(analysis, self.pred, tmp);
}

self.effects_applied = true;
Expand Down Expand Up @@ -468,43 +473,43 @@ impl Direction for Forward {
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
mut propagate: impl FnMut(BasicBlock, &A::Domain),
mut propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
) where
A: Analysis<'tcx>,
{
use mir::TerminatorKind::*;
match bb_data.terminator().kind {
Return | Resume | Terminate | GeneratorDrop | Unreachable => {}

Goto { target } => propagate(target, exit_state),
Goto { target } => propagate(analysis, target, exit_state),

Assert { target, unwind, expected: _, msg: _, cond: _ }
| Drop { target, unwind, place: _, replace: _ }
| FalseUnwind { real_target: target, unwind } => {
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
propagate(analysis, unwind, exit_state);
}

propagate(target, exit_state);
propagate(analysis, target, exit_state);
}

FalseEdge { real_target, imaginary_target } => {
propagate(real_target, exit_state);
propagate(imaginary_target, exit_state);
propagate(analysis, real_target, exit_state);
propagate(analysis, imaginary_target, exit_state);
}

Yield { resume: target, drop, resume_arg, value: _ } => {
if let Some(drop) = drop {
propagate(drop, exit_state);
propagate(analysis, drop, exit_state);
}

analysis.apply_yield_resume_effect(exit_state, target, resume_arg);
propagate(target, exit_state);
propagate(analysis, target, exit_state);
}

Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => {
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
propagate(analysis, unwind, exit_state);
}

if let Some(target) = target {
Expand All @@ -515,7 +520,7 @@ impl Direction for Forward {
bb,
CallReturnPlaces::Call(destination),
);
propagate(target, exit_state);
propagate(analysis, target, exit_state);
}
}

Expand All @@ -528,7 +533,7 @@ impl Direction for Forward {
unwind,
} => {
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
propagate(analysis, unwind, exit_state);
}

if let Some(target) = destination {
Expand All @@ -539,7 +544,7 @@ impl Direction for Forward {
bb,
CallReturnPlaces::InlineAsm(operands),
);
propagate(target, exit_state);
propagate(analysis, target, exit_state);
}
}

Expand All @@ -562,7 +567,7 @@ impl Direction for Forward {

if !effects_applied {
for target in targets.all_targets() {
propagate(*target, exit_state);
propagate(analysis, *target, exit_state);
}
}
}
Expand All @@ -578,26 +583,35 @@ struct ForwardSwitchIntEdgeEffectsApplier<'a, D, F> {
effects_applied: bool,
}

impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
impl<'tcx, A, F> super::SwitchIntEdgeEffects<'tcx, A>
for ForwardSwitchIntEdgeEffectsApplier<'_, A::Domain, F>
where
D: Clone,
F: FnMut(BasicBlock, &D),
A: Analysis<'tcx>,
F: FnMut(&mut A, BasicBlock, &A::Domain),
{
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
fn apply(
&mut self,
analysis: &mut A,
mut apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, 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);
apply_edge_effect(analysis, tmp, SwitchIntTarget { value: Some(value), target });
(self.propagate)(analysis, 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);
apply_edge_effect(
analysis,
self.exit_state,
SwitchIntTarget { value: None, target: otherwise },
);
(self.propagate)(analysis, otherwise, self.exit_state);

self.effects_applied = true;
}
Expand Down
14 changes: 6 additions & 8 deletions compiler/rustc_mir_dataflow/src/framework/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
ResultsVisitor,
GenKillSet, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, ResultsVisitor,
};

pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
Expand Down Expand Up @@ -150,7 +149,7 @@ where
impl<'a, 'tcx, A, D, T> Engine<'a, 'tcx, A>
where
A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
D: Clone + JoinSemiLattice + GenKill<T> + BitSetExt<T>,
D: Clone + GenKill<T> + BitSetExt<T>,
T: Idx,
{
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
Expand Down Expand Up @@ -181,10 +180,9 @@ where
}
}

impl<'a, 'tcx, A, D> Engine<'a, 'tcx, A>
impl<'a, 'tcx, A> Engine<'a, 'tcx, A>
where
A: Analysis<'tcx, Domain = D>,
D: Clone + JoinSemiLattice,
A: Analysis<'tcx>,
{
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
/// function.
Expand Down Expand Up @@ -276,8 +274,8 @@ where
body,
&mut state,
(bb, bb_data),
|target: BasicBlock, state: &A::Domain| {
let set_changed = entry_sets[target].join(state);
|analysis: &mut A, target: BasicBlock, state: &A::Domain| {
let set_changed = analysis.join(&mut entry_sets[target], state, target);
if set_changed {
dirty_queue.insert(target);
}
Expand Down
37 changes: 28 additions & 9 deletions compiler/rustc_mir_dataflow/src/framework/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> {
/// 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;
type Domain: Clone + Eq;

/// The direction of this analysis. Either `Forward` or `Backward`.
type Direction: Direction = Forward;
Expand All @@ -127,6 +127,21 @@ pub trait AnalysisDomain<'tcx> {
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
}

/// Gets the context under which to perform a join.
pub trait AnalysisJoin<'tcx>: AnalysisDomain<'tcx> {
fn join(&mut self, state: &mut Self::Domain, other: &Self::Domain, loc: BasicBlock) -> bool;
}

impl<'tcx, T> AnalysisJoin<'tcx> for T
where
T: ?Sized + AnalysisDomain<'tcx>,
<T as AnalysisDomain<'tcx>>::Domain: JoinSemiLattice,
{
fn join(&mut self, state: &mut Self::Domain, other: &Self::Domain, _: BasicBlock) -> bool {
state.join(other)
}
}

/// A dataflow problem with an arbitrarily complex transfer function.
///
/// # Convergence
Expand All @@ -143,7 +158,7 @@ pub trait AnalysisDomain<'tcx> {
/// 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> {
pub trait Analysis<'tcx>: AnalysisJoin<'tcx> {
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_statement_effect(
&mut self,
Expand Down Expand Up @@ -238,7 +253,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
) {
}

Expand Down Expand Up @@ -349,18 +364,18 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
}

/// See `Analysis::apply_switch_int_edge_effects`.
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
fn switch_int_edge_effects(
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_edge_effects: &mut impl SwitchIntEdgeEffects<G>,
_edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
) {
}
}

impl<'tcx, A> Analysis<'tcx> for A
where
A: GenKillAnalysis<'tcx>,
A: GenKillAnalysis<'tcx> + ?Sized,
A::Domain: GenKill<A::Idx> + BitSetExt<A::Idx>,
{
fn apply_statement_effect(
Expand Down Expand Up @@ -423,7 +438,7 @@ where
&mut self,
block: BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,
edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
) {
self.switch_int_edge_effects(block, discr, edge_effects);
}
Expand Down Expand Up @@ -606,10 +621,14 @@ pub struct SwitchIntTarget {
}

/// A type that records the edge-specific effects for a `SwitchInt` terminator.
pub trait SwitchIntEdgeEffects<D> {
pub trait SwitchIntEdgeEffects<'tcx, A: ?Sized + AnalysisDomain<'tcx>> {
/// 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));
fn apply(
&mut self,
analysis: &mut A,
apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, SwitchIntTarget),
);
}

/// List of places that are written to after a successful (non-unwind) return
Expand Down
Loading

0 comments on commit b2b0317

Please sign in to comment.