Skip to content

Commit c434b4b

Browse files
committed
Auto merge of rust-lang#133328 - nnethercote:simplify-SwitchInt-handling, r=tmiasko
Simplify `SwitchInt` handling Dataflow handling of `SwitchInt` is currently complicated. This PR simplifies it. r? `@cjgillot`
2 parents 4ba4ac6 + 5f40942 commit c434b4b

File tree

5 files changed

+178
-246
lines changed

5 files changed

+178
-246
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_mir_dataflow::impls::{
1212
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
1313
MaybeUninitializedPlacesDomain,
1414
};
15-
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
15+
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
1616
use tracing::debug;
1717

1818
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
@@ -101,16 +101,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
101101
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
102102
unreachable!();
103103
}
104-
105-
fn apply_switch_int_edge_effects(
106-
&mut self,
107-
_block: BasicBlock,
108-
_discr: &mir::Operand<'tcx>,
109-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
110-
) {
111-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
112-
unreachable!();
113-
}
114104
}
115105

116106
impl JoinSemiLattice for BorrowckDomain {

compiler/rustc_mir_dataflow/src/framework/direction.rs

+36-117
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};
@@ -78,8 +76,6 @@ impl Direction for Backward {
7876
for pred in body.basic_blocks.predecessors()[block].iter().copied() {
7977
match body[pred].terminator().kind {
8078
// Apply terminator-specific edge effects.
81-
//
82-
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
8379
mir::TerminatorKind::Call { destination, target: Some(dest), .. }
8480
if dest == block =>
8581
{
@@ -115,18 +111,18 @@ impl Direction for Backward {
115111
}
116112

117113
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
118-
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
119-
body,
120-
pred,
121-
exit_state,
122-
block,
123-
propagate: &mut propagate,
124-
effects_applied: false,
125-
};
126-
127-
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
128-
129-
if !applier.effects_applied {
114+
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
115+
let values = &body.basic_blocks.switch_sources()[&(block, pred)];
116+
let targets =
117+
values.iter().map(|&value| SwitchIntTarget { value, target: block });
118+
119+
let mut tmp = analysis.bottom_value(body);
120+
for target in targets {
121+
tmp.clone_from(&exit_state);
122+
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
123+
propagate(pred, &tmp);
124+
}
125+
} else {
130126
propagate(pred, exit_state)
131127
}
132128
}
@@ -245,37 +241,6 @@ impl Direction for Backward {
245241
}
246242
}
247243

248-
struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
249-
body: &'mir mir::Body<'tcx>,
250-
pred: BasicBlock,
251-
exit_state: &'mir mut D,
252-
block: BasicBlock,
253-
propagate: &'mir mut F,
254-
effects_applied: bool,
255-
}
256-
257-
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
258-
where
259-
D: Clone,
260-
F: FnMut(BasicBlock, &D),
261-
{
262-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
263-
assert!(!self.effects_applied);
264-
265-
let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
266-
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });
267-
268-
let mut tmp = None;
269-
for target in targets {
270-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
271-
apply_edge_effect(tmp, target);
272-
(self.propagate)(self.pred, tmp);
273-
}
274-
275-
self.effects_applied = true;
276-
}
277-
}
278-
279244
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
280245
pub struct Forward;
281246

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

285250
fn apply_effects_in_block<'mir, 'tcx, A>(
286251
analysis: &mut A,
287-
_body: &mir::Body<'tcx>,
252+
body: &mir::Body<'tcx>,
288253
state: &mut A::Domain,
289254
block: BasicBlock,
290255
block_data: &'mir mir::BasicBlockData<'tcx>,
@@ -324,23 +289,28 @@ impl Direction for Forward {
324289
}
325290
}
326291
TerminatorEdges::SwitchInt { targets, discr } => {
327-
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
328-
exit_state,
329-
targets,
330-
propagate,
331-
effects_applied: false,
332-
};
333-
334-
analysis.apply_switch_int_edge_effects(block, discr, &mut applier);
335-
336-
let ForwardSwitchIntEdgeEffectsApplier {
337-
exit_state,
338-
mut propagate,
339-
effects_applied,
340-
..
341-
} = applier;
342-
343-
if !effects_applied {
292+
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
293+
let mut tmp = analysis.bottom_value(body);
294+
for (value, target) in targets.iter() {
295+
tmp.clone_from(&exit_state);
296+
analysis.apply_switch_int_edge_effect(
297+
&mut data,
298+
&mut tmp,
299+
SwitchIntTarget { value: Some(value), target },
300+
);
301+
propagate(target, &tmp);
302+
}
303+
304+
// Once we get to the final, "otherwise" branch, there is no need to preserve
305+
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
306+
// a clone of the dataflow state.
307+
let otherwise = targets.otherwise();
308+
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
309+
value: None,
310+
target: otherwise,
311+
});
312+
propagate(otherwise, exit_state);
313+
} else {
344314
for target in targets.all_targets() {
345315
propagate(*target, exit_state);
346316
}
@@ -454,54 +424,3 @@ impl Direction for Forward {
454424
vis.visit_block_end(state);
455425
}
456426
}
457-
458-
struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
459-
exit_state: &'mir mut D,
460-
targets: &'mir SwitchTargets,
461-
propagate: F,
462-
463-
effects_applied: bool,
464-
}
465-
466-
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
467-
where
468-
D: Clone,
469-
F: FnMut(BasicBlock, &D),
470-
{
471-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
472-
assert!(!self.effects_applied);
473-
474-
let mut tmp = None;
475-
for (value, target) in self.targets.iter() {
476-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
477-
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
478-
(self.propagate)(target, tmp);
479-
}
480-
481-
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
482-
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
483-
let otherwise = self.targets.otherwise();
484-
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
485-
(self.propagate)(otherwise, self.exit_state);
486-
487-
self.effects_applied = true;
488-
}
489-
}
490-
491-
/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
492-
/// the more efficient `clone_from` if `opt` was `Some`.
493-
///
494-
/// Returns a mutable reference to the new clone that resides in `opt`.
495-
//
496-
// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
497-
// standard library?
498-
fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
499-
if opt.is_some() {
500-
let ret = opt.as_mut().unwrap();
501-
ret.clone_from(val);
502-
ret
503-
} else {
504-
*opt = Some(val.clone());
505-
opt.as_mut().unwrap()
506-
}
507-
}

compiler/rustc_mir_dataflow/src/framework/mod.rs

+23-16
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub trait Analysis<'tcx> {
103103
/// The direction of this analysis. Either `Forward` or `Backward`.
104104
type Direction: Direction = Forward;
105105

106+
/// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary.
107+
type SwitchIntData = !;
108+
106109
/// A descriptive name for this analysis. Used only for debugging.
107110
///
108111
/// This name should be brief and contain no spaces, periods or other characters that are not
@@ -190,25 +193,36 @@ pub trait Analysis<'tcx> {
190193
) {
191194
}
192195

193-
/// Updates the current dataflow state with the effect of taking a particular branch in a
194-
/// `SwitchInt` terminator.
196+
/// Used to update the current dataflow state with the effect of taking a particular branch in
197+
/// a `SwitchInt` terminator.
195198
///
196199
/// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain`
197-
/// directly, overriders of this method must pass a callback to
198-
/// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and
199-
/// will have access to the dataflow state that will be propagated along that edge.
200+
/// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in
201+
/// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each
202+
/// outgoing edge and will have access to the dataflow state that will be propagated along that
203+
/// edge, and also the `Self::SwitchIntData` value.
200204
///
201205
/// This interface is somewhat more complex than the other visitor-like "effect" methods.
202206
/// However, it is both more ergonomic—callers don't need to recompute or cache information
203207
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
204208
/// engine doesn't need to clone the exit state for a block unless
205-
/// `SwitchIntEdgeEffects::apply` is actually called.
206-
fn apply_switch_int_edge_effects(
209+
/// `get_switch_int_data` is actually called.
210+
fn get_switch_int_data(
207211
&mut self,
208-
_block: BasicBlock,
212+
_block: mir::BasicBlock,
209213
_discr: &mir::Operand<'tcx>,
210-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
214+
) -> Option<Self::SwitchIntData> {
215+
None
216+
}
217+
218+
/// See comments on `get_switch_int_data`.
219+
fn apply_switch_int_edge_effect(
220+
&mut self,
221+
_data: &mut Self::SwitchIntData,
222+
_state: &mut Self::Domain,
223+
_edge: SwitchIntTarget,
211224
) {
225+
unreachable!();
212226
}
213227

214228
/* Extension methods */
@@ -421,12 +435,5 @@ pub struct SwitchIntTarget {
421435
pub target: BasicBlock,
422436
}
423437

424-
/// A type that records the edge-specific effects for a `SwitchInt` terminator.
425-
pub trait SwitchIntEdgeEffects<D> {
426-
/// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
427-
/// records the results.
428-
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
429-
}
430-
431438
#[cfg(test)]
432439
mod tests;

0 commit comments

Comments
 (0)