Skip to content

Commit 6137232

Browse files
committed
Simplify dataflow SwitchInt handling.
Current `SwitchInt` handling has complicated control flow. - The dataflow engine calls `Analysis::apply_switch_int_edge_effects`, passing in an "applier" that impls `SwitchIntEdgeEffects`. - `apply_switch_int_edge_effects` possibly calls `apply` on the applier, passing it a closure. - The `apply` method calls the closure on each `SwitchInt` edge. - The closure operates on the edge. I.e. control flow goes from the engine, to the analysis, to the applier (which came from the engine), to the closure (which came from the analysis). It took me a while to work this out. This commit changes to a simpler structure that maintains the important characteristics. - The dataflow engine calls `Analysis::get_switch_int_data`. - `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value. - If that returned value was `Some`, the dataflow engine calls `Analysis::apply_switch_int_edge_effect` on each edge, passing the `Self::SwitchIntData` value. - `Analysis::apply_switch_int_edge_effect` operates on the edge. I.e. control flow goes from the engine, to the analysis, to the engine, to the analysis. Added: - The `Analysis::SwitchIntData` assoc type and the `Analysis::get_switch_int_data` method. Both only need to be defined by analyses that look at `SwitchInt` terminators. - The `MaybePlacesSwitchIntData` struct, which has three fields. Changes: - `Analysis::apply_switch_int_edge_effects` becomes `Analysis::apply_switch_int_edge_effect`, which is a little simpler because it's dealing with a single edge instead of all edges. Removed: - The `SwitchIntEdgeEffects` trait, and its two impls: `BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and `ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields). - The closure. The new structure is more concise and simpler.
1 parent ac74713 commit 6137232

File tree

5 files changed

+139
-184
lines changed

5 files changed

+139
-184
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::mir::{
99
use rustc_middle::ty::{RegionVid, TyCtxt};
1010
use rustc_mir_dataflow::fmt::DebugWithContext;
1111
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
12-
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
12+
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
1313
use tracing::debug;
1414

1515
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
@@ -98,16 +98,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
9898
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
9999
unreachable!();
100100
}
101-
102-
fn apply_switch_int_edge_effects(
103-
&mut self,
104-
_block: BasicBlock,
105-
_discr: &mir::Operand<'tcx>,
106-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
107-
) {
108-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
109-
unreachable!();
110-
}
111101
}
112102

113103
impl JoinSemiLattice for BorrowckDomain<'_, '_> {

compiler/rustc_mir_dataflow/src/framework/direction.rs

+34-96
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::ops::RangeInclusive;
22

3-
use rustc_middle::mir::{
4-
self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges,
5-
};
3+
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
64

75
use super::visitor::ResultsVisitor;
86
use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
@@ -237,18 +235,18 @@ impl Direction for Backward {
237235
}
238236

239237
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
240-
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
241-
body,
242-
pred,
243-
exit_state,
244-
bb,
245-
propagate: &mut propagate,
246-
effects_applied: false,
247-
};
248-
249-
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
250-
251-
if !applier.effects_applied {
238+
if let Some(mut data) = analysis.get_switch_int_data(bb, discr) {
239+
let values = &body.basic_blocks.switch_sources()[&(bb, pred)];
240+
let targets =
241+
values.iter().map(|&value| SwitchIntTarget { value, target: bb });
242+
243+
let mut tmp = None;
244+
for target in targets {
245+
let tmp = opt_clone_from_or_clone(&mut tmp, exit_state);
246+
analysis.apply_switch_int_edge_effect(&mut data, tmp, target);
247+
propagate(pred, tmp);
248+
}
249+
} else {
252250
propagate(pred, exit_state)
253251
}
254252
}
@@ -259,37 +257,6 @@ impl Direction for Backward {
259257
}
260258
}
261259

262-
struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
263-
body: &'mir mir::Body<'tcx>,
264-
pred: BasicBlock,
265-
exit_state: &'mir mut D,
266-
bb: BasicBlock,
267-
propagate: &'mir mut F,
268-
effects_applied: bool,
269-
}
270-
271-
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
272-
where
273-
D: Clone,
274-
F: FnMut(BasicBlock, &D),
275-
{
276-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
277-
assert!(!self.effects_applied);
278-
279-
let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
280-
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb });
281-
282-
let mut tmp = None;
283-
for target in targets {
284-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
285-
apply_edge_effect(tmp, target);
286-
(self.propagate)(self.pred, tmp);
287-
}
288-
289-
self.effects_applied = true;
290-
}
291-
}
292-
293260
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
294261
pub struct Forward;
295262

@@ -451,23 +418,27 @@ impl Direction for Forward {
451418
}
452419
}
453420
TerminatorEdges::SwitchInt { targets, discr } => {
454-
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
455-
exit_state,
456-
targets,
457-
propagate,
458-
effects_applied: false,
459-
};
460-
461-
analysis.apply_switch_int_edge_effects(bb, discr, &mut applier);
462-
463-
let ForwardSwitchIntEdgeEffectsApplier {
464-
exit_state,
465-
mut propagate,
466-
effects_applied,
467-
..
468-
} = applier;
469-
470-
if !effects_applied {
421+
if let Some(mut data) = analysis.get_switch_int_data(bb, discr) {
422+
let mut tmp = None;
423+
for (value, target) in targets.iter() {
424+
let tmp = opt_clone_from_or_clone(&mut tmp, exit_state);
425+
analysis.apply_switch_int_edge_effect(&mut data, tmp, SwitchIntTarget {
426+
value: Some(value),
427+
target,
428+
});
429+
propagate(target, tmp);
430+
}
431+
432+
// Once we get to the final, "otherwise" branch, there is no need to preserve
433+
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
434+
// a clone of the dataflow state.
435+
let otherwise = targets.otherwise();
436+
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
437+
value: None,
438+
target: otherwise,
439+
});
440+
propagate(otherwise, exit_state);
441+
} else {
471442
for target in targets.all_targets() {
472443
propagate(*target, exit_state);
473444
}
@@ -477,39 +448,6 @@ impl Direction for Forward {
477448
}
478449
}
479450

480-
struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
481-
exit_state: &'mir mut D,
482-
targets: &'mir SwitchTargets,
483-
propagate: F,
484-
485-
effects_applied: bool,
486-
}
487-
488-
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
489-
where
490-
D: Clone,
491-
F: FnMut(BasicBlock, &D),
492-
{
493-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
494-
assert!(!self.effects_applied);
495-
496-
let mut tmp = None;
497-
for (value, target) in self.targets.iter() {
498-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
499-
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
500-
(self.propagate)(target, tmp);
501-
}
502-
503-
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
504-
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
505-
let otherwise = self.targets.otherwise();
506-
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
507-
(self.propagate)(otherwise, self.exit_state);
508-
509-
self.effects_applied = true;
510-
}
511-
}
512-
513451
/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
514452
/// the more efficient `clone_from` if `opt` was `Some`.
515453
///

compiler/rustc_mir_dataflow/src/framework/mod.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ pub trait Analysis<'tcx> {
120120
/// The direction of this analysis. Either `Forward` or `Backward`.
121121
type Direction: Direction = Forward;
122122

123+
/// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary.
124+
type SwitchIntData = !;
125+
123126
/// A descriptive name for this analysis. Used only for debugging.
124127
///
125128
/// This name should be brief and contain no spaces, periods or other characters that are not
@@ -205,25 +208,36 @@ pub trait Analysis<'tcx> {
205208
) {
206209
}
207210

208-
/// Updates the current dataflow state with the effect of taking a particular branch in a
209-
/// `SwitchInt` terminator.
211+
/// Used to update the current dataflow state with the effect of taking a particular branch in
212+
/// a `SwitchInt` terminator.
210213
///
211214
/// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain`
212-
/// directly, overriders of this method must pass a callback to
213-
/// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and
214-
/// will have access to the dataflow state that will be propagated along that edge.
215+
/// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in
216+
/// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each
217+
/// outgoing edge and will have access to the dataflow state that will be propagated along that
218+
/// edge, and also the `Self::SwitchIntData` value.
215219
///
216220
/// This interface is somewhat more complex than the other visitor-like "effect" methods.
217221
/// However, it is both more ergonomic—callers don't need to recompute or cache information
218222
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
219223
/// engine doesn't need to clone the exit state for a block unless
220-
/// `SwitchIntEdgeEffects::apply` is actually called.
221-
fn apply_switch_int_edge_effects(
224+
/// `get_switch_int_data` is actually called.
225+
fn get_switch_int_data(
222226
&mut self,
223-
_block: BasicBlock,
227+
_block: mir::BasicBlock,
224228
_discr: &mir::Operand<'tcx>,
225-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
229+
) -> Option<Self::SwitchIntData> {
230+
None
231+
}
232+
233+
/// See comments on `get_switch_int_data`.
234+
fn apply_switch_int_edge_effect(
235+
&mut self,
236+
_data: &mut Self::SwitchIntData,
237+
_state: &mut Self::Domain,
238+
_edge: SwitchIntTarget,
226239
) {
240+
unreachable!();
227241
}
228242

229243
/* Extension methods */
@@ -285,7 +299,6 @@ pub trait Analysis<'tcx> {
285299
// but it saves an allocation, thus improving compile times.
286300
state.clone_from(&entry_sets[bb]);
287301

288-
// Apply the block transfer function, using the cached one if it exists.
289302
let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data);
290303

291304
Self::Direction::join_state_into_successors_of(
@@ -452,12 +465,5 @@ pub struct SwitchIntTarget {
452465
pub target: BasicBlock,
453466
}
454467

455-
/// A type that records the edge-specific effects for a `SwitchInt` terminator.
456-
pub trait SwitchIntEdgeEffects<D> {
457-
/// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
458-
/// records the results.
459-
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
460-
}
461-
462468
#[cfg(test)]
463469
mod tests;

0 commit comments

Comments
 (0)