1
Fork 0

Use TerminatorEdge for dataflow-const-prop.

This commit is contained in:
Camille GILLOT 2023-05-07 10:43:20 +00:00
parent 3acfa092db
commit f19cd3f2e1
2 changed files with 48 additions and 62 deletions

View file

@ -241,11 +241,19 @@ pub trait ValueAnalysis<'tcx> {
/// The effect of a successful function call return should not be /// The effect of a successful function call return should not be
/// applied here, see [`Analysis::apply_terminator_effect`]. /// applied here, see [`Analysis::apply_terminator_effect`].
fn handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) { fn handle_terminator<'mir>(
&self,
terminator: &'mir Terminator<'tcx>,
state: &mut State<Self::Value>,
) -> TerminatorEdge<'mir, 'tcx> {
self.super_terminator(terminator, state) self.super_terminator(terminator, state)
} }
fn super_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) { fn super_terminator<'mir>(
&self,
terminator: &'mir Terminator<'tcx>,
state: &mut State<Self::Value>,
) -> TerminatorEdge<'mir, 'tcx> {
match &terminator.kind { match &terminator.kind {
TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => {
// Effect is applied by `handle_call_return`. // Effect is applied by `handle_call_return`.
@ -257,8 +265,10 @@ pub trait ValueAnalysis<'tcx> {
// They would have an effect, but are not allowed in this phase. // They would have an effect, but are not allowed in this phase.
bug!("encountered disallowed terminator"); bug!("encountered disallowed terminator");
} }
TerminatorKind::SwitchInt { discr, targets } => {
return self.handle_switch_int(discr, targets, state);
}
TerminatorKind::Goto { .. } TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Resume | TerminatorKind::Resume
| TerminatorKind::Terminate | TerminatorKind::Terminate
| TerminatorKind::Return | TerminatorKind::Return
@ -270,6 +280,7 @@ pub trait ValueAnalysis<'tcx> {
// These terminators have no effect on the analysis. // These terminators have no effect on the analysis.
} }
} }
terminator.edges()
} }
fn handle_call_return( fn handle_call_return(
@ -290,19 +301,22 @@ pub trait ValueAnalysis<'tcx> {
}) })
} }
fn handle_switch_int( fn handle_switch_int<'mir>(
&self, &self,
discr: &Operand<'tcx>, discr: &'mir Operand<'tcx>,
apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>, targets: &'mir SwitchTargets,
) { state: &mut State<Self::Value>,
self.super_switch_int(discr, apply_edge_effects) ) -> TerminatorEdge<'mir, 'tcx> {
self.super_switch_int(discr, targets, state)
} }
fn super_switch_int( fn super_switch_int<'mir>(
&self, &self,
_discr: &Operand<'tcx>, discr: &'mir Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>, targets: &'mir SwitchTargets,
) { _state: &mut State<Self::Value>,
) -> TerminatorEdge<'mir, 'tcx> {
TerminatorEdge::SwitchInt { discr, targets }
} }
fn wrap(self) -> ValueAnalysisWrapper<Self> fn wrap(self) -> ValueAnalysisWrapper<Self>
@ -359,9 +373,10 @@ where
_location: Location, _location: Location,
) -> TerminatorEdge<'mir, 'tcx> { ) -> TerminatorEdge<'mir, 'tcx> {
if state.is_reachable() { if state.is_reachable() {
self.0.handle_terminator(terminator, state); self.0.handle_terminator(terminator, state)
} else {
TerminatorEdge::None
} }
terminator.edges()
} }
fn apply_call_return_effect( fn apply_call_return_effect(
@ -378,11 +393,9 @@ where
fn apply_switch_int_edge_effects( fn apply_switch_int_edge_effects(
&mut self, &mut self,
_block: BasicBlock, _block: BasicBlock,
discr: &Operand<'tcx>, _discr: &Operand<'tcx>,
apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, _apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
) { ) {
// FIXME: Dataflow framework provides no access to current state here.
self.0.handle_switch_int(discr, apply_edge_effects)
} }
} }

View file

@ -13,9 +13,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::value_analysis::{ use rustc_mir_dataflow::value_analysis::{
Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
}; };
use rustc_mir_dataflow::{ use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects,
};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, FieldIdx, VariantIdx}; use rustc_target::abi::{Align, FieldIdx, VariantIdx};
@ -249,49 +247,24 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
.unwrap_or(FlatSet::Top) .unwrap_or(FlatSet::Top)
} }
fn handle_switch_int( fn handle_switch_int<'mir>(
&self, &self,
discr: &Operand<'tcx>, discr: &'mir Operand<'tcx>,
apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>, targets: &'mir SwitchTargets,
) { state: &mut State<Self::Value>,
// FIXME: The dataflow framework only provides the state if we call `apply()`, which makes ) -> TerminatorEdge<'mir, 'tcx> {
// this more inefficient than it has to be.
let mut discr_value = None;
let mut handled = false;
apply_edge_effects.apply(|state, target| {
let discr_value = match discr_value {
Some(value) => value,
None => {
let value = match self.handle_operand(discr, state) { let value = match self.handle_operand(discr, state) {
ValueOrPlace::Value(value) => value, ValueOrPlace::Value(value) => value,
ValueOrPlace::Place(place) => state.get_idx(place, self.map()), ValueOrPlace::Place(place) => state.get_idx(place, self.map()),
}; };
let result = match value { let FlatSet::Elem(ScalarTy(scalar, _)) = value else {
FlatSet::Top => FlatSet::Top,
FlatSet::Elem(ScalarTy(scalar, _)) => {
let int = scalar.assert_int();
FlatSet::Elem(int.assert_bits(int.size()))
}
FlatSet::Bottom => FlatSet::Bottom,
};
discr_value = Some(result);
result
}
};
let FlatSet::Elem(choice) = discr_value else {
// Do nothing if we don't know which branch will be taken. // Do nothing if we don't know which branch will be taken.
return; return TerminatorEdge::SwitchInt { discr, targets };
}; };
if target.value.map(|n| n == choice).unwrap_or(!handled) { let int = scalar.assert_int();
// Branch is taken. Has no effect on state. let choice = int.assert_bits(int.size());
handled = true; TerminatorEdge::Single(targets.target_for_value(choice))
} else {
// Branch is not taken.
state.mark_unreachable();
}
})
} }
} }