Move handling of references and simplify flooding
This commit is contained in:
parent
3f98dc7838
commit
ad99d2e15d
2 changed files with 92 additions and 57 deletions
|
@ -115,35 +115,15 @@ pub trait ValueAnalysis<'tcx> {
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
state: &mut State<Self::Value>,
|
state: &mut State<Self::Value>,
|
||||||
) {
|
) {
|
||||||
match rvalue {
|
|
||||||
Rvalue::Ref(_, BorrowKind::Shared, place) => {
|
|
||||||
let target_deref = self
|
|
||||||
.map()
|
|
||||||
.find(target.as_ref())
|
|
||||||
.and_then(|target| self.map().apply_elem(target, ProjElem::Deref));
|
|
||||||
let place = self.map().find(place.as_ref());
|
|
||||||
match (target_deref, place) {
|
|
||||||
(Some(target_deref), Some(place)) => {
|
|
||||||
state.assign_idx(target_deref, ValueOrPlace::Place(place), self.map())
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
|
|
||||||
state.flood(place.as_ref(), self.map(), Self::Value::top());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let result = self.handle_rvalue(rvalue, state);
|
let result = self.handle_rvalue(rvalue, state);
|
||||||
state.assign(target.as_ref(), result, self.map());
|
state.assign(target.as_ref(), result, self.map());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_rvalue(
|
fn handle_rvalue(
|
||||||
&self,
|
&self,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
state: &mut State<Self::Value>,
|
state: &mut State<Self::Value>,
|
||||||
) -> ValueOrPlace<Self::Value> {
|
) -> ValueOrPlaceOrRef<Self::Value> {
|
||||||
self.super_rvalue(rvalue, state)
|
self.super_rvalue(rvalue, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,16 +131,24 @@ pub trait ValueAnalysis<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
state: &mut State<Self::Value>,
|
state: &mut State<Self::Value>,
|
||||||
) -> ValueOrPlace<Self::Value> {
|
) -> ValueOrPlaceOrRef<Self::Value> {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::Use(operand) => self.handle_operand(operand, state),
|
Rvalue::Use(operand) => self.handle_operand(operand, state).into(),
|
||||||
Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state),
|
Rvalue::Ref(_, BorrowKind::Shared, place) => self
|
||||||
Rvalue::Ref(..) | Rvalue::AddressOf(..) => {
|
.map()
|
||||||
bug!("this rvalue must be handled by handle_assign() or super_assign()")
|
.find(place.as_ref())
|
||||||
|
.map(ValueOrPlaceOrRef::Ref)
|
||||||
|
.unwrap_or(ValueOrPlaceOrRef::Unknown),
|
||||||
|
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
|
||||||
|
state.flood(place.as_ref(), self.map());
|
||||||
|
ValueOrPlaceOrRef::Unknown
|
||||||
|
}
|
||||||
|
Rvalue::CopyForDeref(place) => {
|
||||||
|
self.handle_operand(&Operand::Copy(*place), state).into()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// FIXME: Check that other Rvalues really have no side-effect.
|
// FIXME: Check that other Rvalues really have no side-effect.
|
||||||
ValueOrPlace::Unknown
|
ValueOrPlaceOrRef::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,7 +216,7 @@ pub trait ValueAnalysis<'tcx> {
|
||||||
state: &mut State<Self::Value>,
|
state: &mut State<Self::Value>,
|
||||||
) {
|
) {
|
||||||
return_places.for_each(|place| {
|
return_places.for_each(|place| {
|
||||||
state.flood(place.as_ref(), self.map(), Self::Value::top());
|
state.flood(place.as_ref(), self.map());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +258,7 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper
|
||||||
|
|
||||||
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
||||||
for arg in body.args_iter() {
|
for arg in body.args_iter() {
|
||||||
state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map(), T::Value::top());
|
state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,17 +316,25 @@ rustc_index::newtype_index!(
|
||||||
pub struct State<V>(IndexVec<ValueIndex, V>);
|
pub struct State<V>(IndexVec<ValueIndex, V>);
|
||||||
|
|
||||||
impl<V: Clone + HasTop> State<V> {
|
impl<V: Clone + HasTop> State<V> {
|
||||||
pub fn flood_all(&mut self, value: V) {
|
pub fn flood_all(&mut self) {
|
||||||
|
self.flood_all_with(V::top())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flood_all_with(&mut self, value: V) {
|
||||||
self.0.raw.fill(value);
|
self.0.raw.fill(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
||||||
if let Some(root) = map.find(place) {
|
if let Some(root) = map.find(place) {
|
||||||
self.flood_idx(root, map, value);
|
self.flood_idx_with(root, map, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flood_idx(&mut self, place: PlaceIndex, map: &Map, value: V) {
|
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) {
|
||||||
|
self.flood_with(place, map, V::top())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flood_idx_with(&mut self, place: PlaceIndex, map: &Map, value: V) {
|
||||||
map.preorder_invoke(place, &mut |place| {
|
map.preorder_invoke(place, &mut |place| {
|
||||||
if let Some(vi) = map.places[place].value_index {
|
if let Some(vi) = map.places[place].value_index {
|
||||||
self.0[vi] = value.clone();
|
self.0[vi] = value.clone();
|
||||||
|
@ -346,6 +342,10 @@ impl<V: Clone + HasTop> State<V> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flood_idx(&mut self, place: PlaceIndex, map: &Map) {
|
||||||
|
self.flood_idx_with(place, map, V::top())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
|
pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
|
||||||
if let Some(target_value) = map.places[target].value_index {
|
if let Some(target_value) = map.places[target].value_index {
|
||||||
if let Some(source_value) = map.places[source].value_index {
|
if let Some(source_value) = map.places[source].value_index {
|
||||||
|
@ -360,30 +360,40 @@ impl<V: Clone + HasTop> State<V> {
|
||||||
if let Some(source_child) = map.projections.get(&(source, projection)) {
|
if let Some(source_child) = map.projections.get(&(source, projection)) {
|
||||||
self.assign_place_idx(target_child, *source_child, map);
|
self.assign_place_idx(target_child, *source_child, map);
|
||||||
} else {
|
} else {
|
||||||
self.flood_idx(target_child, map, V::top());
|
self.flood_idx(target_child, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
|
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlaceOrRef<V>, map: &Map) {
|
||||||
if let Some(target) = map.find(target) {
|
if let Some(target) = map.find(target) {
|
||||||
self.assign_idx(target, result, map);
|
self.assign_idx(target, result, map);
|
||||||
|
} else {
|
||||||
|
// We don't track this place nor any projections, assignment can be ignored.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
|
pub fn assign_idx(&mut self, target: PlaceIndex, result: ValueOrPlaceOrRef<V>, map: &Map) {
|
||||||
match result {
|
match result {
|
||||||
ValueOrPlace::Value(value) => {
|
ValueOrPlaceOrRef::Value(value) => {
|
||||||
// First flood the target place in case we also track any projections (although
|
// First flood the target place in case we also track any projections (although
|
||||||
// this scenario is currently not well-supported with the ValueOrPlace interface).
|
// this scenario is currently not well-supported by the API).
|
||||||
self.flood_idx(target, map, V::top());
|
self.flood_idx(target, map);
|
||||||
if let Some(value_index) = map.places[target].value_index {
|
if let Some(value_index) = map.places[target].value_index {
|
||||||
self.0[value_index] = value;
|
self.0[value_index] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueOrPlace::Place(source) => self.assign_place_idx(target, source, map),
|
ValueOrPlaceOrRef::Place(source) => self.assign_place_idx(target, source, map),
|
||||||
ValueOrPlace::Unknown => {
|
ValueOrPlaceOrRef::Ref(source) => {
|
||||||
self.flood_idx(target, map, V::top());
|
if let Some(value_index) = map.places[target].value_index {
|
||||||
|
self.0[value_index] = V::top();
|
||||||
|
}
|
||||||
|
if let Some(target_deref) = map.apply_elem(target, ProjElem::Deref) {
|
||||||
|
self.assign_place_idx(target_deref, source, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueOrPlaceOrRef::Unknown => {
|
||||||
|
self.flood_idx(target, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -578,6 +588,23 @@ pub enum ValueOrPlace<V> {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ValueOrPlaceOrRef<V> {
|
||||||
|
Value(V),
|
||||||
|
Place(PlaceIndex),
|
||||||
|
Ref(PlaceIndex),
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V> From<ValueOrPlace<V>> for ValueOrPlaceOrRef<V> {
|
||||||
|
fn from(x: ValueOrPlace<V>) -> Self {
|
||||||
|
match x {
|
||||||
|
ValueOrPlace::Value(value) => ValueOrPlaceOrRef::Value(value),
|
||||||
|
ValueOrPlace::Place(place) => ValueOrPlaceOrRef::Place(place),
|
||||||
|
ValueOrPlace::Unknown => ValueOrPlaceOrRef::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait HasBottom {
|
pub trait HasBottom {
|
||||||
fn bottom() -> Self;
|
fn bottom() -> Self;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_middle::mir::visit::{MutVisitor, Visitor};
|
use rustc_middle::mir::visit::{MutVisitor, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||||
use rustc_mir_dataflow::value_analysis::{Map, ProjElem, State, ValueAnalysis, ValueOrPlace};
|
use rustc_mir_dataflow::value_analysis::{
|
||||||
|
Map, ProjElem, State, ValueAnalysis, ValueOrPlace, ValueOrPlaceOrRef,
|
||||||
|
};
|
||||||
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
|
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
|
@ -59,6 +61,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::CheckedBinaryOp(op, box (left, right)) => {
|
Rvalue::CheckedBinaryOp(op, box (left, right)) => {
|
||||||
let target = self.map().find(target.as_ref());
|
let target = self.map().find(target.as_ref());
|
||||||
|
if let Some(target) = target {
|
||||||
|
// We should not track any projections other than
|
||||||
|
// what is overwritten below, but just in case...
|
||||||
|
state.flood_idx(target, self.map());
|
||||||
|
}
|
||||||
|
|
||||||
let value_target = target.and_then(|target| {
|
let value_target = target.and_then(|target| {
|
||||||
self.map().apply_elem(target, ProjElem::Field(0_u32.into()))
|
self.map().apply_elem(target, ProjElem::Field(0_u32.into()))
|
||||||
});
|
});
|
||||||
|
@ -70,12 +78,12 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
|
||||||
let (val, overflow) = self.binary_op(state, *op, left, right);
|
let (val, overflow) = self.binary_op(state, *op, left, right);
|
||||||
|
|
||||||
if let Some(value_target) = value_target {
|
if let Some(value_target) = value_target {
|
||||||
state.assign_idx(value_target, ValueOrPlace::Value(val), self.map());
|
state.assign_idx(value_target, ValueOrPlaceOrRef::Value(val), self.map());
|
||||||
}
|
}
|
||||||
if let Some(overflow_target) = overflow_target {
|
if let Some(overflow_target) = overflow_target {
|
||||||
state.assign_idx(
|
state.assign_idx(
|
||||||
overflow_target,
|
overflow_target,
|
||||||
ValueOrPlace::Value(overflow),
|
ValueOrPlaceOrRef::Value(overflow),
|
||||||
self.map(),
|
self.map(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +97,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
state: &mut State<Self::Value>,
|
state: &mut State<Self::Value>,
|
||||||
) -> ValueOrPlace<Self::Value> {
|
) -> ValueOrPlaceOrRef<Self::Value> {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::Cast(CastKind::Misc, operand, ty) => {
|
Rvalue::Cast(CastKind::Misc, operand, ty) => {
|
||||||
let operand = self.eval_operand(operand, state);
|
let operand = self.eval_operand(operand, state);
|
||||||
|
@ -97,24 +105,24 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
|
||||||
FlatSet::Elem(operand) => self
|
FlatSet::Elem(operand) => self
|
||||||
.ecx
|
.ecx
|
||||||
.misc_cast(&operand, *ty)
|
.misc_cast(&operand, *ty)
|
||||||
.map(|result| ValueOrPlace::Value(self.wrap_immediate(result, *ty)))
|
.map(|result| ValueOrPlaceOrRef::Value(self.wrap_immediate(result, *ty)))
|
||||||
.unwrap_or(ValueOrPlace::Unknown),
|
.unwrap_or(ValueOrPlaceOrRef::Unknown),
|
||||||
_ => ValueOrPlace::Unknown,
|
_ => ValueOrPlaceOrRef::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rvalue::BinaryOp(op, box (left, right)) => {
|
Rvalue::BinaryOp(op, box (left, right)) => {
|
||||||
let (val, _overflow) = self.binary_op(state, *op, left, right);
|
let (val, _overflow) = self.binary_op(state, *op, left, right);
|
||||||
// FIXME: Just ignore overflow here?
|
// FIXME: Just ignore overflow here?
|
||||||
ValueOrPlace::Value(val)
|
ValueOrPlaceOrRef::Value(val)
|
||||||
}
|
}
|
||||||
Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
|
Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
|
||||||
FlatSet::Elem(value) => self
|
FlatSet::Elem(value) => self
|
||||||
.ecx
|
.ecx
|
||||||
.unary_op(*op, &value)
|
.unary_op(*op, &value)
|
||||||
.map(|val| ValueOrPlace::Value(self.wrap_immty(val)))
|
.map(|val| ValueOrPlaceOrRef::Value(self.wrap_immty(val)))
|
||||||
.unwrap_or(ValueOrPlace::Value(FlatSet::Top)),
|
.unwrap_or(ValueOrPlaceOrRef::Value(FlatSet::Top)),
|
||||||
FlatSet::Bottom => ValueOrPlace::Value(FlatSet::Bottom),
|
FlatSet::Bottom => ValueOrPlaceOrRef::Value(FlatSet::Bottom),
|
||||||
FlatSet::Top => ValueOrPlace::Value(FlatSet::Top),
|
FlatSet::Top => ValueOrPlaceOrRef::Value(FlatSet::Top),
|
||||||
},
|
},
|
||||||
_ => self.super_rvalue(rvalue, state),
|
_ => self.super_rvalue(rvalue, state),
|
||||||
}
|
}
|
||||||
|
@ -175,7 +183,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
|
||||||
handled = true;
|
handled = true;
|
||||||
} else {
|
} else {
|
||||||
// Branch is not taken, we can flood everything.
|
// Branch is not taken, we can flood everything.
|
||||||
state.flood_all(FlatSet::Bottom);
|
state.flood_all();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue