Skip to content

Commit 0a29306

Browse files
committed
Implement dataflow state joins through the analysis. Make JoinSemiLattice the default implementation when the join does not require extra context.
1 parent 381d699 commit 0a29306

File tree

5 files changed

+88
-58
lines changed

5 files changed

+88
-58
lines changed

compiler/rustc_mir_dataflow/src/framework/direction.rs

+42-28
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub trait Direction {
5656
exit_state: &mut A::Domain,
5757
block: BasicBlock,
5858
edges: TerminatorEdges<'_, 'tcx>,
59-
propagate: impl FnMut(BasicBlock, &A::Domain),
59+
propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
6060
) where
6161
A: Analysis<'tcx>;
6262
}
@@ -223,7 +223,7 @@ impl Direction for Backward {
223223
exit_state: &mut A::Domain,
224224
bb: BasicBlock,
225225
_edges: TerminatorEdges<'_, 'tcx>,
226-
mut propagate: impl FnMut(BasicBlock, &A::Domain),
226+
mut propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
227227
) where
228228
A: Analysis<'tcx>,
229229
{
@@ -239,7 +239,7 @@ impl Direction for Backward {
239239
pred,
240240
CallReturnPlaces::Call(destination),
241241
);
242-
propagate(pred, &tmp);
242+
propagate(analysis, pred, &tmp);
243243
}
244244

245245
mir::TerminatorKind::InlineAsm {
@@ -251,7 +251,7 @@ impl Direction for Backward {
251251
pred,
252252
CallReturnPlaces::InlineAsm(operands),
253253
);
254-
propagate(pred, &tmp);
254+
propagate(analysis, pred, &tmp);
255255
}
256256

257257
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == bb => {
@@ -261,7 +261,7 @@ impl Direction for Backward {
261261
resume,
262262
CallReturnPlaces::Yield(resume_arg),
263263
);
264-
propagate(pred, &tmp);
264+
propagate(analysis, pred, &tmp);
265265
}
266266

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

279279
if !applier.effects_applied {
280-
propagate(pred, exit_state)
280+
propagate(analysis, pred, exit_state)
281281
}
282282
}
283283

284-
_ => propagate(pred, exit_state),
284+
_ => propagate(analysis, pred, exit_state),
285285
}
286286
}
287287
}
@@ -296,12 +296,17 @@ struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
296296
effects_applied: bool,
297297
}
298298

299-
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
299+
impl<'tcx, A, F> super::SwitchIntEdgeEffects<'tcx, A>
300+
for BackwardSwitchIntEdgeEffectsApplier<'_, '_, A::Domain, F>
300301
where
301-
D: Clone,
302-
F: FnMut(BasicBlock, &D),
302+
A: Analysis<'tcx>,
303+
F: FnMut(&mut A, BasicBlock, &A::Domain),
303304
{
304-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
305+
fn apply(
306+
&mut self,
307+
analysis: &mut A,
308+
mut apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, SwitchIntTarget),
309+
) {
305310
assert!(!self.effects_applied);
306311

307312
let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)];
@@ -310,8 +315,8 @@ where
310315
let mut tmp = None;
311316
for target in targets {
312317
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
313-
apply_edge_effect(tmp, target);
314-
(self.propagate)(self.pred, tmp);
318+
apply_edge_effect(analysis, tmp, target);
319+
(self.propagate)(analysis, self.pred, tmp);
315320
}
316321

317322
self.effects_applied = true;
@@ -475,25 +480,25 @@ impl Direction for Forward {
475480
exit_state: &mut A::Domain,
476481
bb: BasicBlock,
477482
edges: TerminatorEdges<'_, 'tcx>,
478-
mut propagate: impl FnMut(BasicBlock, &A::Domain),
483+
mut propagate: impl FnMut(&mut A, BasicBlock, &A::Domain),
479484
) where
480485
A: Analysis<'tcx>,
481486
{
482487
match edges {
483488
TerminatorEdges::None => {}
484-
TerminatorEdges::Single(target) => propagate(target, exit_state),
489+
TerminatorEdges::Single(target) => propagate(analysis, target, exit_state),
485490
TerminatorEdges::Double(target, unwind) => {
486-
propagate(target, exit_state);
487-
propagate(unwind, exit_state);
491+
propagate(analysis, target, exit_state);
492+
propagate(analysis, unwind, exit_state);
488493
}
489494
TerminatorEdges::AssignOnReturn { return_, cleanup, place } => {
490495
// This must be done *first*, otherwise the unwind path will see the assignments.
491496
if let Some(cleanup) = cleanup {
492-
propagate(cleanup, exit_state);
497+
propagate(analysis, cleanup, exit_state);
493498
}
494499
if let Some(return_) = return_ {
495500
analysis.apply_call_return_effect(exit_state, bb, place);
496-
propagate(return_, exit_state);
501+
propagate(analysis, return_, exit_state);
497502
}
498503
}
499504
TerminatorEdges::SwitchInt { targets, discr } => {
@@ -515,7 +520,7 @@ impl Direction for Forward {
515520

516521
if !effects_applied {
517522
for target in targets.all_targets() {
518-
propagate(*target, exit_state);
523+
propagate(analysis, *target, exit_state);
519524
}
520525
}
521526
}
@@ -531,26 +536,35 @@ struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
531536
effects_applied: bool,
532537
}
533538

534-
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
539+
impl<'tcx, A, F> super::SwitchIntEdgeEffects<'tcx, A>
540+
for ForwardSwitchIntEdgeEffectsApplier<'_, A::Domain, F>
535541
where
536-
D: Clone,
537-
F: FnMut(BasicBlock, &D),
542+
A: Analysis<'tcx>,
543+
F: FnMut(&mut A, BasicBlock, &A::Domain),
538544
{
539-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
545+
fn apply(
546+
&mut self,
547+
analysis: &mut A,
548+
mut apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, SwitchIntTarget),
549+
) {
540550
assert!(!self.effects_applied);
541551

542552
let mut tmp = None;
543553
for (value, target) in self.targets.iter() {
544554
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
545-
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
546-
(self.propagate)(target, tmp);
555+
apply_edge_effect(analysis, tmp, SwitchIntTarget { value: Some(value), target });
556+
(self.propagate)(analysis, target, tmp);
547557
}
548558

549559
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
550560
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
551561
let otherwise = self.targets.otherwise();
552-
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
553-
(self.propagate)(otherwise, self.exit_state);
562+
apply_edge_effect(
563+
analysis,
564+
self.exit_state,
565+
SwitchIntTarget { value: None, target: otherwise },
566+
);
567+
(self.propagate)(analysis, otherwise, self.exit_state);
554568

555569
self.effects_applied = true;
556570
}

compiler/rustc_mir_dataflow/src/framework/engine.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use super::fmt::DebugWithContext;
2323
use super::graphviz;
2424
use super::{
2525
visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet,
26-
JoinSemiLattice, ResultsCursor, ResultsVisitor,
26+
ResultsCursor, ResultsVisitor,
2727
};
2828

2929
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
@@ -97,7 +97,7 @@ where
9797
impl<'mir, 'tcx, A, D, T> Engine<'mir, 'tcx, A>
9898
where
9999
A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
100-
D: Clone + JoinSemiLattice + GenKill<T> + BitSetExt<T>,
100+
D: Clone + Eq + GenKill<T> + BitSetExt<T>,
101101
T: Idx,
102102
{
103103
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
@@ -136,7 +136,7 @@ where
136136
impl<'mir, 'tcx, A, D> Engine<'mir, 'tcx, A>
137137
where
138138
A: Analysis<'tcx, Domain = D>,
139-
D: Clone + JoinSemiLattice,
139+
D: Clone + Eq,
140140
{
141141
/// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
142142
/// function.
@@ -229,8 +229,8 @@ where
229229
&mut state,
230230
bb,
231231
edges,
232-
|target: BasicBlock, state: &A::Domain| {
233-
let set_changed = entry_sets[target].join(state);
232+
|analysis: &mut A, target: BasicBlock, state: &A::Domain| {
233+
let set_changed = analysis.join(&mut entry_sets[target], state, target);
234234
if set_changed {
235235
dirty_queue.insert(target);
236236
}

compiler/rustc_mir_dataflow/src/framework/mod.rs

+32-16
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> {
9393
/// initial value at the entry point of each basic block.
9494
pub trait AnalysisDomain<'tcx> {
9595
/// The type that holds the dataflow state at any given point in the program.
96-
type Domain: Clone + JoinSemiLattice;
96+
type Domain: Clone + Eq;
9797

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

121+
/// The join operation of a dataflow analysis. A default implementation is used when the
122+
/// `Domain` type implements `JoinSemiLattice`.
123+
pub trait AnalysisJoin<'tcx>: AnalysisDomain<'tcx> {
124+
/// Computes the least upper bound of two elements, storing the result in `state` and returning
125+
/// `true` if `state` has changed.
126+
fn join(&mut self, state: &mut Self::Domain, other: &Self::Domain, loc: BasicBlock) -> bool;
127+
}
128+
129+
impl<'tcx, T> AnalysisJoin<'tcx> for T
130+
where
131+
T: ?Sized + AnalysisDomain<'tcx>,
132+
<T as AnalysisDomain<'tcx>>::Domain: JoinSemiLattice,
133+
{
134+
fn join(&mut self, state: &mut Self::Domain, other: &Self::Domain, _: BasicBlock) -> bool {
135+
state.join(other)
136+
}
137+
}
138+
121139
/// A dataflow problem with an arbitrarily complex transfer function.
122140
///
123141
/// # Convergence
@@ -134,7 +152,7 @@ pub trait AnalysisDomain<'tcx> {
134152
/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
135153
/// to the same point in the program at different points in time. The dataflow state at a given
136154
/// point in the program may or may not be greater than the state at any preceding point.
137-
pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
155+
pub trait Analysis<'tcx>: AnalysisJoin<'tcx> + Sized {
138156
/// Updates the current dataflow state with the effect of evaluating a statement.
139157
fn apply_statement_effect(
140158
&mut self,
@@ -215,7 +233,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
215233
&mut self,
216234
_block: BasicBlock,
217235
_discr: &mir::Operand<'tcx>,
218-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
236+
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
219237
) {
220238
}
221239

@@ -238,10 +256,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
238256
self,
239257
tcx: TyCtxt<'tcx>,
240258
body: &'mir mir::Body<'tcx>,
241-
) -> Engine<'mir, 'tcx, Self>
242-
where
243-
Self: Sized,
244-
{
259+
) -> Engine<'mir, 'tcx, Self> {
245260
Engine::new_generic(tcx, body, self)
246261
}
247262
}
@@ -306,11 +321,11 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
306321
);
307322

308323
/// See `Analysis::apply_switch_int_edge_effects`.
309-
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
324+
fn switch_int_edge_effects(
310325
&mut self,
311326
_block: BasicBlock,
312327
_discr: &mir::Operand<'tcx>,
313-
_edge_effects: &mut impl SwitchIntEdgeEffects<G>,
328+
_edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
314329
) {
315330
}
316331
}
@@ -372,7 +387,7 @@ where
372387
&mut self,
373388
block: BasicBlock,
374389
discr: &mir::Operand<'tcx>,
375-
edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,
390+
edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, A>,
376391
) {
377392
self.switch_int_edge_effects(block, discr, edge_effects);
378393
}
@@ -383,10 +398,7 @@ where
383398
self,
384399
tcx: TyCtxt<'tcx>,
385400
body: &'mir mir::Body<'tcx>,
386-
) -> Engine<'mir, 'tcx, Self>
387-
where
388-
Self: Sized,
389-
{
401+
) -> Engine<'mir, 'tcx, Self> {
390402
Engine::new_gen_kill(tcx, body, self)
391403
}
392404
}
@@ -573,10 +585,14 @@ pub struct SwitchIntTarget {
573585
}
574586

575587
/// A type that records the edge-specific effects for a `SwitchInt` terminator.
576-
pub trait SwitchIntEdgeEffects<D> {
588+
pub trait SwitchIntEdgeEffects<'tcx, A: AnalysisDomain<'tcx>> {
577589
/// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
578590
/// records the results.
579-
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
591+
fn apply(
592+
&mut self,
593+
analysis: &mut A,
594+
apply_edge_effect: impl FnMut(&mut A, &mut A::Domain, SwitchIntTarget),
595+
);
580596
}
581597

582598
#[cfg(test)]

compiler/rustc_mir_dataflow/src/impls/initialized.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
396396
});
397397
}
398398

399-
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
399+
fn switch_int_edge_effects(
400400
&mut self,
401401
block: mir::BasicBlock,
402402
discr: &mir::Operand<'tcx>,
403-
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
403+
edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
404404
) {
405405
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
406406
return;
@@ -415,7 +415,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
415415
};
416416

417417
let mut discriminants = enum_def.discriminants(self.tcx);
418-
edge_effects.apply(|trans, edge| {
418+
edge_effects.apply(self, |self_, trans, edge| {
419419
let Some(value) = edge.value else {
420420
return;
421421
};
@@ -430,7 +430,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
430430
// Kill all move paths that correspond to variants we know to be inactive along this
431431
// particular outgoing edge of a `SwitchInt`.
432432
drop_flag_effects::on_all_inactive_variants(
433-
self.move_data(),
433+
self_.move_data(),
434434
enum_place,
435435
variant,
436436
|mpi| trans.kill(mpi),
@@ -521,11 +521,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
521521
});
522522
}
523523

524-
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
524+
fn switch_int_edge_effects(
525525
&mut self,
526526
block: mir::BasicBlock,
527527
discr: &mir::Operand<'tcx>,
528-
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
528+
edge_effects: &mut impl SwitchIntEdgeEffects<'tcx, Self>,
529529
) {
530530
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
531531
return;
@@ -544,7 +544,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
544544
};
545545

546546
let mut discriminants = enum_def.discriminants(self.tcx);
547-
edge_effects.apply(|trans, edge| {
547+
edge_effects.apply(self, |self_, trans, edge| {
548548
let Some(value) = edge.value else {
549549
return;
550550
};
@@ -559,7 +559,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
559559
// Mark all move paths that correspond to variants other than this one as maybe
560560
// uninitialized (in reality, they are *definitely* uninitialized).
561561
drop_flag_effects::on_all_inactive_variants(
562-
self.move_data(),
562+
self_.move_data(),
563563
enum_place,
564564
variant,
565565
|mpi| trans.gen(mpi),

0 commit comments

Comments
 (0)