Skip to content

Commit

Permalink
Auto merge of #133328 - nnethercote:simplify-SwitchInt-handling, r=tm…
Browse files Browse the repository at this point in the history
…iasko

Simplify `SwitchInt` handling

Dataflow handling of `SwitchInt` is currently complicated. This PR simplifies it.

r? `@cjgillot`
  • Loading branch information
bors committed Dec 18, 2024
2 parents 4ba4ac6 + 5f40942 commit c434b4b
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 246 deletions.
12 changes: 1 addition & 11 deletions compiler/rustc_borrowck/src/dataflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_mir_dataflow::impls::{
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
MaybeUninitializedPlacesDomain,
};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
use tracing::debug;

use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
Expand Down Expand Up @@ -101,16 +101,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}

fn apply_switch_int_edge_effects(
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
) {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}
}

impl JoinSemiLattice for BorrowckDomain {
Expand Down
153 changes: 36 additions & 117 deletions compiler/rustc_mir_dataflow/src/framework/direction.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::ops::RangeInclusive;

use rustc_middle::mir::{
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges,
};
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};

use super::visitor::ResultsVisitor;
use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
Expand Down Expand Up @@ -78,8 +76,6 @@ impl Direction for Backward {
for pred in body.basic_blocks.predecessors()[block].iter().copied() {
match body[pred].terminator().kind {
// Apply terminator-specific edge effects.
//
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
mir::TerminatorKind::Call { destination, target: Some(dest), .. }
if dest == block =>
{
Expand Down Expand Up @@ -115,18 +111,18 @@ impl Direction for Backward {
}

mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
body,
pred,
exit_state,
block,
propagate: &mut propagate,
effects_applied: false,
};

analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);

if !applier.effects_applied {
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
let values = &body.basic_blocks.switch_sources()[&(block, pred)];
let targets =
values.iter().map(|&value| SwitchIntTarget { value, target: block });

let mut tmp = analysis.bottom_value(body);
for target in targets {
tmp.clone_from(&exit_state);
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
propagate(pred, &tmp);
}
} else {
propagate(pred, exit_state)
}
}
Expand Down Expand Up @@ -245,37 +241,6 @@ impl Direction for Backward {
}
}

struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
body: &'mir mir::Body<'tcx>,
pred: BasicBlock,
exit_state: &'mir mut D,
block: BasicBlock,
propagate: &'mir mut F,
effects_applied: bool,
}

impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, 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 values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });

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);
}

self.effects_applied = true;
}
}

/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
pub struct Forward;

Expand All @@ -284,7 +249,7 @@ impl Direction for Forward {

fn apply_effects_in_block<'mir, 'tcx, A>(
analysis: &mut A,
_body: &mir::Body<'tcx>,
body: &mir::Body<'tcx>,
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
Expand Down Expand Up @@ -324,23 +289,28 @@ impl Direction for Forward {
}
}
TerminatorEdges::SwitchInt { targets, discr } => {
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
exit_state,
targets,
propagate,
effects_applied: false,
};

analysis.apply_switch_int_edge_effects(block, discr, &mut applier);

let ForwardSwitchIntEdgeEffectsApplier {
exit_state,
mut propagate,
effects_applied,
..
} = applier;

if !effects_applied {
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
let mut tmp = analysis.bottom_value(body);
for (value, target) in targets.iter() {
tmp.clone_from(&exit_state);
analysis.apply_switch_int_edge_effect(
&mut data,
&mut tmp,
SwitchIntTarget { value: Some(value), target },
);
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_switch_int_edge_effect` to save
// a clone of the dataflow state.
let otherwise = targets.otherwise();
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
value: None,
target: otherwise,
});
propagate(otherwise, exit_state);
} else {
for target in targets.all_targets() {
propagate(*target, exit_state);
}
Expand Down Expand Up @@ -454,54 +424,3 @@ impl Direction for Forward {
vis.visit_block_end(state);
}
}

struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
exit_state: &'mir mut D,
targets: &'mir SwitchTargets,
propagate: F,

effects_applied: bool,
}

impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, 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<'a, 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()
}
}
39 changes: 23 additions & 16 deletions compiler/rustc_mir_dataflow/src/framework/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ pub trait Analysis<'tcx> {
/// The direction of this analysis. Either `Forward` or `Backward`.
type Direction: Direction = Forward;

/// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary.
type SwitchIntData = !;

/// 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
Expand Down Expand Up @@ -190,25 +193,36 @@ pub trait Analysis<'tcx> {
) {
}

/// Updates the current dataflow state with the effect of taking a particular branch in a
/// `SwitchInt` terminator.
/// Used to update 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.
/// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in
/// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each
/// outgoing edge and will have access to the dataflow state that will be propagated along that
/// edge, and also the `Self::SwitchIntData` value.
///
/// 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.
fn apply_switch_int_edge_effects(
/// `get_switch_int_data` is actually called.
fn get_switch_int_data(
&mut self,
_block: BasicBlock,
_block: mir::BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
) -> Option<Self::SwitchIntData> {
None
}

/// See comments on `get_switch_int_data`.
fn apply_switch_int_edge_effect(
&mut self,
_data: &mut Self::SwitchIntData,
_state: &mut Self::Domain,
_edge: SwitchIntTarget,
) {
unreachable!();
}

/* Extension methods */
Expand Down Expand Up @@ -421,12 +435,5 @@ pub struct SwitchIntTarget {
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;
Loading

0 comments on commit c434b4b

Please sign in to comment.