Auto merge of #127087 - cjgillot:small-map, r=oli-obk
Only track mentioned places for jump threading This PR aims to reduce the state space size in jump threading and dataflow const-prop opts. The current implementation walks the types of all locals, and creates a place for each possible projection. This can easily lead to a large number of places and tracked values, most being useless to the actual pass. With this PR, we instead collect places that appear syntactically in the MIR (first commit). However, this is not sufficient (second commit), and we miss places that we could track in aggregate assignments. The third commit tracks such assignments to mirror place projections, see the inline comment. This is complementary to https://github.com/rust-lang/rust/pull/127036 r? `@oli-obk`
This commit is contained in:
commit
0ffbddd09e
15 changed files with 633 additions and 271 deletions
|
@ -32,15 +32,16 @@
|
||||||
//! Because of that, we can assume that the only way to change the value behind a tracked place is
|
//! Because of that, we can assume that the only way to change the value behind a tracked place is
|
||||||
//! by direct assignment.
|
//! by direct assignment.
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashMap, StdEntry};
|
use rustc_data_structures::captures::Captures;
|
||||||
|
use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry};
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_index::IndexVec;
|
use rustc_index::IndexVec;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
|
use rustc_middle::mir::tcx::PlaceTy;
|
||||||
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
|
@ -58,7 +59,7 @@ pub trait ValueAnalysis<'tcx> {
|
||||||
|
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
|
|
||||||
fn map(&self) -> ⤅
|
fn map(&self) -> &Map<'tcx>;
|
||||||
|
|
||||||
fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
|
fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
|
||||||
self.super_statement(statement, state)
|
self.super_statement(statement, state)
|
||||||
|
@ -523,12 +524,12 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign `value` to all places that are contained in `place` or may alias one.
|
/// Assign `value` to all places that are contained in `place` or may alias one.
|
||||||
pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
|
||||||
self.flood_with_tail_elem(place, None, map, value)
|
self.flood_with_tail_elem(place, None, map, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign `TOP` to all places that are contained in `place` or may alias one.
|
/// Assign `TOP` to all places that are contained in `place` or may alias one.
|
||||||
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map)
|
pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
|
||||||
where
|
where
|
||||||
V: HasTop,
|
V: HasTop,
|
||||||
{
|
{
|
||||||
|
@ -536,12 +537,12 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign `value` to the discriminant of `place` and all places that may alias it.
|
/// Assign `value` to the discriminant of `place` and all places that may alias it.
|
||||||
fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
|
fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
|
||||||
self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
|
self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign `TOP` to the discriminant of `place` and all places that may alias it.
|
/// Assign `TOP` to the discriminant of `place` and all places that may alias it.
|
||||||
pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map)
|
pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
|
||||||
where
|
where
|
||||||
V: HasTop,
|
V: HasTop,
|
||||||
{
|
{
|
||||||
|
@ -559,7 +560,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
place: PlaceRef<'_>,
|
place: PlaceRef<'_>,
|
||||||
tail_elem: Option<TrackElem>,
|
tail_elem: Option<TrackElem>,
|
||||||
map: &Map,
|
map: &Map<'_>,
|
||||||
value: V,
|
value: V,
|
||||||
) {
|
) {
|
||||||
let State::Reachable(values) = self else { return };
|
let State::Reachable(values) = self else { return };
|
||||||
|
@ -570,7 +571,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// This does nothing if the place is not tracked.
|
/// This does nothing if the place is not tracked.
|
||||||
///
|
///
|
||||||
/// The target place must have been flooded before calling this method.
|
/// The target place must have been flooded before calling this method.
|
||||||
fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
|
fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map<'_>) {
|
||||||
match result {
|
match result {
|
||||||
ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
|
ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
|
||||||
ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
|
ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
|
||||||
|
@ -581,7 +582,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// This does nothing if the place is not tracked.
|
/// This does nothing if the place is not tracked.
|
||||||
///
|
///
|
||||||
/// The target place must have been flooded before calling this method.
|
/// The target place must have been flooded before calling this method.
|
||||||
pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map) {
|
pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map<'_>) {
|
||||||
let State::Reachable(values) = self else { return };
|
let State::Reachable(values) = self else { return };
|
||||||
if let Some(value_index) = map.places[target].value_index {
|
if let Some(value_index) = map.places[target].value_index {
|
||||||
values.insert(value_index, value)
|
values.insert(value_index, value)
|
||||||
|
@ -595,7 +596,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// places that are non-overlapping or identical.
|
/// places that are non-overlapping or identical.
|
||||||
///
|
///
|
||||||
/// The target place must have been flooded before calling this method.
|
/// The target place must have been flooded before calling this method.
|
||||||
pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
|
pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map<'_>) {
|
||||||
let State::Reachable(values) = self else { return };
|
let State::Reachable(values) = self else { return };
|
||||||
|
|
||||||
// If both places are tracked, we copy the value to the target.
|
// If both places are tracked, we copy the value to the target.
|
||||||
|
@ -616,7 +617,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method to interpret `target = result`.
|
/// Helper method to interpret `target = result`.
|
||||||
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
|
pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
|
||||||
where
|
where
|
||||||
V: HasTop,
|
V: HasTop,
|
||||||
{
|
{
|
||||||
|
@ -627,7 +628,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method for assignments to a discriminant.
|
/// Helper method for assignments to a discriminant.
|
||||||
pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
|
pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
|
||||||
where
|
where
|
||||||
V: HasTop,
|
V: HasTop,
|
||||||
{
|
{
|
||||||
|
@ -638,25 +639,25 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the value stored for a place, or `None` if it is not tracked.
|
/// Retrieve the value stored for a place, or `None` if it is not tracked.
|
||||||
pub fn try_get(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
pub fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
|
||||||
let place = map.find(place)?;
|
let place = map.find(place)?;
|
||||||
self.try_get_idx(place, map)
|
self.try_get_idx(place, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the discriminant stored for a place, or `None` if it is not tracked.
|
/// Retrieve the discriminant stored for a place, or `None` if it is not tracked.
|
||||||
pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
|
||||||
let place = map.find_discr(place)?;
|
let place = map.find_discr(place)?;
|
||||||
self.try_get_idx(place, map)
|
self.try_get_idx(place, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the slice length stored for a place, or `None` if it is not tracked.
|
/// Retrieve the slice length stored for a place, or `None` if it is not tracked.
|
||||||
pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
|
pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
|
||||||
let place = map.find_len(place)?;
|
let place = map.find_len(place)?;
|
||||||
self.try_get_idx(place, map)
|
self.try_get_idx(place, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the value stored for a place index, or `None` if it is not tracked.
|
/// Retrieve the value stored for a place index, or `None` if it is not tracked.
|
||||||
pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> {
|
pub fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option<V> {
|
||||||
match self {
|
match self {
|
||||||
State::Reachable(values) => {
|
State::Reachable(values) => {
|
||||||
map.places[place].value_index.map(|v| values.get(v).clone())
|
map.places[place].value_index.map(|v| values.get(v).clone())
|
||||||
|
@ -668,7 +669,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||||
///
|
///
|
||||||
/// This method returns ⊥ if the place is tracked and the state is unreachable.
|
/// This method returns ⊥ if the place is tracked and the state is unreachable.
|
||||||
pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V
|
pub fn get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
|
||||||
where
|
where
|
||||||
V: HasBottom + HasTop,
|
V: HasBottom + HasTop,
|
||||||
{
|
{
|
||||||
|
@ -682,7 +683,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||||
///
|
///
|
||||||
/// This method returns ⊥ the current state is unreachable.
|
/// This method returns ⊥ the current state is unreachable.
|
||||||
pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V
|
pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
|
||||||
where
|
where
|
||||||
V: HasBottom + HasTop,
|
V: HasBottom + HasTop,
|
||||||
{
|
{
|
||||||
|
@ -696,7 +697,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
|
||||||
///
|
///
|
||||||
/// This method returns ⊥ the current state is unreachable.
|
/// This method returns ⊥ the current state is unreachable.
|
||||||
pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V
|
pub fn get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
|
||||||
where
|
where
|
||||||
V: HasBottom + HasTop,
|
V: HasBottom + HasTop,
|
||||||
{
|
{
|
||||||
|
@ -710,7 +711,7 @@ impl<V: Clone + HasBottom> State<V> {
|
||||||
/// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
|
/// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
|
||||||
///
|
///
|
||||||
/// This method returns ⊥ the current state is unreachable.
|
/// This method returns ⊥ the current state is unreachable.
|
||||||
pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V
|
pub fn get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> V
|
||||||
where
|
where
|
||||||
V: HasBottom + HasTop,
|
V: HasBottom + HasTop,
|
||||||
{
|
{
|
||||||
|
@ -746,25 +747,25 @@ impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> {
|
||||||
/// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
|
/// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
|
||||||
/// - To directly get the child for a specific projection, there is a `projections` map.
|
/// - To directly get the child for a specific projection, there is a `projections` map.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Map {
|
pub struct Map<'tcx> {
|
||||||
locals: IndexVec<Local, Option<PlaceIndex>>,
|
locals: IndexVec<Local, Option<PlaceIndex>>,
|
||||||
projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
|
projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
|
||||||
places: IndexVec<PlaceIndex, PlaceInfo>,
|
places: IndexVec<PlaceIndex, PlaceInfo<'tcx>>,
|
||||||
value_count: usize,
|
value_count: usize,
|
||||||
// The Range corresponds to a slice into `inner_values_buffer`.
|
// The Range corresponds to a slice into `inner_values_buffer`.
|
||||||
inner_values: IndexVec<PlaceIndex, Range<usize>>,
|
inner_values: IndexVec<PlaceIndex, Range<usize>>,
|
||||||
inner_values_buffer: Vec<ValueIndex>,
|
inner_values_buffer: Vec<ValueIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map {
|
impl<'tcx> Map<'tcx> {
|
||||||
/// Returns a map that only tracks places whose type has scalar layout.
|
/// Returns a map that only tracks places whose type has scalar layout.
|
||||||
///
|
///
|
||||||
/// This is currently the only way to create a [`Map`]. The way in which the tracked places are
|
/// This is currently the only way to create a [`Map`]. The way in which the tracked places are
|
||||||
/// chosen is an implementation detail and may not be relied upon (other than that their type
|
/// chosen is an implementation detail and may not be relied upon (other than that their type
|
||||||
/// are scalars).
|
/// are scalars).
|
||||||
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
|
pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
|
||||||
let mut map = Self {
|
let mut map = Self {
|
||||||
locals: IndexVec::new(),
|
locals: IndexVec::from_elem(None, &body.local_decls),
|
||||||
projections: FxHashMap::default(),
|
projections: FxHashMap::default(),
|
||||||
places: IndexVec::new(),
|
places: IndexVec::new(),
|
||||||
value_count: 0,
|
value_count: 0,
|
||||||
|
@ -778,18 +779,15 @@ impl Map {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register all non-excluded places that have scalar layout.
|
/// Register all non-excluded places that have scalar layout.
|
||||||
fn register<'tcx>(
|
#[tracing::instrument(level = "trace", skip(self, tcx, body))]
|
||||||
|
fn register(
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
exclude: BitSet<Local>,
|
exclude: BitSet<Local>,
|
||||||
value_limit: Option<usize>,
|
value_limit: Option<usize>,
|
||||||
) {
|
) {
|
||||||
let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len()));
|
|
||||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
|
||||||
|
|
||||||
// Start by constructing the places for each bare local.
|
// Start by constructing the places for each bare local.
|
||||||
self.locals = IndexVec::from_elem(None, &body.local_decls);
|
|
||||||
for (local, decl) in body.local_decls.iter_enumerated() {
|
for (local, decl) in body.local_decls.iter_enumerated() {
|
||||||
if exclude.contains(local) {
|
if exclude.contains(local) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -797,16 +795,60 @@ impl Map {
|
||||||
|
|
||||||
// Create a place for the local.
|
// Create a place for the local.
|
||||||
debug_assert!(self.locals[local].is_none());
|
debug_assert!(self.locals[local].is_none());
|
||||||
let place = self.places.push(PlaceInfo::new(None));
|
let place = self.places.push(PlaceInfo::new(decl.ty, None));
|
||||||
self.locals[local] = Some(place);
|
self.locals[local] = Some(place);
|
||||||
|
|
||||||
// And push the eventual children places to the worklist.
|
|
||||||
self.register_children(tcx, param_env, place, decl.ty, &mut worklist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `place.elem1.elem2` with type `ty`.
|
// Collect syntactic places and assignments between them.
|
||||||
// `elem1` is either `Some(Variant(i))` or `None`.
|
let mut collector =
|
||||||
while let Some((mut place, elem1, elem2, ty)) = worklist.pop_front() {
|
PlaceCollector { tcx, body, map: self, assignments: Default::default() };
|
||||||
|
collector.visit_body(body);
|
||||||
|
let PlaceCollector { mut assignments, .. } = collector;
|
||||||
|
|
||||||
|
// Just collecting syntactic places is not enough. We may need to propagate this pattern:
|
||||||
|
// _1 = (const 5u32, const 13i64);
|
||||||
|
// _2 = _1;
|
||||||
|
// _3 = (_2.0 as u32);
|
||||||
|
//
|
||||||
|
// `_1.0` does not appear, but we still need to track it. This is achieved by propagating
|
||||||
|
// projections from assignments. We recorded an assignment between `_2` and `_1`, so we
|
||||||
|
// want `_1` and `_2` to have the same sub-places.
|
||||||
|
//
|
||||||
|
// This is what this fixpoint loop does. While we are still creating places, run through
|
||||||
|
// all the assignments, and register places for children.
|
||||||
|
let mut num_places = 0;
|
||||||
|
while num_places < self.places.len() {
|
||||||
|
num_places = self.places.len();
|
||||||
|
|
||||||
|
for assign in 0.. {
|
||||||
|
let Some(&(lhs, rhs)) = assignments.get_index(assign) else { break };
|
||||||
|
|
||||||
|
// Mirror children from `lhs` in `rhs`.
|
||||||
|
let mut child = self.places[lhs].first_child;
|
||||||
|
while let Some(lhs_child) = child {
|
||||||
|
let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[lhs_child];
|
||||||
|
let rhs_child =
|
||||||
|
self.register_place(ty, rhs, proj_elem.expect("child is not a projection"));
|
||||||
|
assignments.insert((lhs_child, rhs_child));
|
||||||
|
child = next_sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversely, mirror children from `rhs` in `lhs`.
|
||||||
|
let mut child = self.places[rhs].first_child;
|
||||||
|
while let Some(rhs_child) = child {
|
||||||
|
let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[rhs_child];
|
||||||
|
let lhs_child =
|
||||||
|
self.register_place(ty, lhs, proj_elem.expect("child is not a projection"));
|
||||||
|
assignments.insert((lhs_child, rhs_child));
|
||||||
|
child = next_sibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(assignments);
|
||||||
|
|
||||||
|
// Create values for places whose type have scalar layout.
|
||||||
|
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||||
|
for place_info in self.places.iter_mut() {
|
||||||
// The user requires a bound on the number of created values.
|
// The user requires a bound on the number of created values.
|
||||||
if let Some(value_limit) = value_limit
|
if let Some(value_limit) = value_limit
|
||||||
&& self.value_count >= value_limit
|
&& self.value_count >= value_limit
|
||||||
|
@ -814,19 +856,18 @@ impl Map {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a place for this projection.
|
if let Ok(ty) = tcx.try_normalize_erasing_regions(param_env, place_info.ty) {
|
||||||
for elem in [elem1, Some(elem2)].into_iter().flatten() {
|
place_info.ty = ty;
|
||||||
place = *self.projections.entry((place, elem)).or_insert_with(|| {
|
|
||||||
// Prepend new child to the linked list.
|
|
||||||
let next = self.places.push(PlaceInfo::new(Some(elem)));
|
|
||||||
self.places[next].next_sibling = self.places[place].first_child;
|
|
||||||
self.places[place].first_child = Some(next);
|
|
||||||
next
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// And push the eventual children places to the worklist.
|
// Allocate a value slot if it doesn't have one, and the user requested one.
|
||||||
self.register_children(tcx, param_env, place, ty, &mut worklist);
|
assert!(place_info.value_index.is_none());
|
||||||
|
if let Ok(layout) = tcx.layout_of(param_env.and(place_info.ty))
|
||||||
|
&& layout.abi.is_scalar()
|
||||||
|
{
|
||||||
|
place_info.value_index = Some(self.value_count.into());
|
||||||
|
self.value_count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute the tree of ValueIndex nested in each PlaceIndex.
|
// Pre-compute the tree of ValueIndex nested in each PlaceIndex.
|
||||||
|
@ -852,68 +893,14 @@ impl Map {
|
||||||
self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
|
self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Potentially register the (local, projection) place and its fields, recursively.
|
#[tracing::instrument(level = "trace", skip(self), ret)]
|
||||||
///
|
fn register_place(&mut self, ty: Ty<'tcx>, base: PlaceIndex, elem: TrackElem) -> PlaceIndex {
|
||||||
/// Invariant: The projection must only contain trackable elements.
|
*self.projections.entry((base, elem)).or_insert_with(|| {
|
||||||
fn register_children<'tcx>(
|
let next = self.places.push(PlaceInfo::new(ty, Some(elem)));
|
||||||
&mut self,
|
self.places[next].next_sibling = self.places[base].first_child;
|
||||||
tcx: TyCtxt<'tcx>,
|
self.places[base].first_child = Some(next);
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
next
|
||||||
place: PlaceIndex,
|
})
|
||||||
ty: Ty<'tcx>,
|
|
||||||
worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>,
|
|
||||||
) {
|
|
||||||
// Allocate a value slot if it doesn't have one, and the user requested one.
|
|
||||||
assert!(self.places[place].value_index.is_none());
|
|
||||||
if tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.abi.is_scalar()) {
|
|
||||||
self.places[place].value_index = Some(self.value_count.into());
|
|
||||||
self.value_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For enums, directly create the `Discriminant`, as that's their main use.
|
|
||||||
if ty.is_enum() {
|
|
||||||
// Prepend new child to the linked list.
|
|
||||||
let discr = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
|
|
||||||
self.places[discr].next_sibling = self.places[place].first_child;
|
|
||||||
self.places[place].first_child = Some(discr);
|
|
||||||
let old = self.projections.insert((place, TrackElem::Discriminant), discr);
|
|
||||||
assert!(old.is_none());
|
|
||||||
|
|
||||||
// Allocate a value slot since it doesn't have one.
|
|
||||||
assert!(self.places[discr].value_index.is_none());
|
|
||||||
self.places[discr].value_index = Some(self.value_count.into());
|
|
||||||
self.value_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind()
|
|
||||||
&& let ty::Slice(..) = ref_ty.kind()
|
|
||||||
// The user may have written a predicate like `[T]: Sized` in their where clauses,
|
|
||||||
// which makes slices scalars.
|
|
||||||
&& self.places[place].value_index.is_none()
|
|
||||||
{
|
|
||||||
// Prepend new child to the linked list.
|
|
||||||
let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen)));
|
|
||||||
self.places[len].next_sibling = self.places[place].first_child;
|
|
||||||
self.places[place].first_child = Some(len);
|
|
||||||
|
|
||||||
let old = self.projections.insert((place, TrackElem::DerefLen), len);
|
|
||||||
assert!(old.is_none());
|
|
||||||
|
|
||||||
// Allocate a value slot since it doesn't have one.
|
|
||||||
assert!(self.places[len].value_index.is_none());
|
|
||||||
self.places[len].value_index = Some(self.value_count.into());
|
|
||||||
self.value_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse with all fields of this place.
|
|
||||||
iter_fields(ty, tcx, param_env, |variant, field, ty| {
|
|
||||||
worklist.push_back((
|
|
||||||
place,
|
|
||||||
variant.map(TrackElem::Variant),
|
|
||||||
TrackElem::Field(field),
|
|
||||||
ty,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Precompute the list of values inside `root` and store it inside
|
/// Precompute the list of values inside `root` and store it inside
|
||||||
|
@ -934,7 +921,108 @@ impl Map {
|
||||||
let end = self.inner_values_buffer.len();
|
let end = self.inner_values_buffer.len();
|
||||||
self.inner_values[root] = start..end;
|
self.inner_values[root] = start..end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PlaceCollector<'a, 'b, 'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
body: &'b Body<'tcx>,
|
||||||
|
map: &'a mut Map<'tcx>,
|
||||||
|
assignments: FxIndexSet<(PlaceIndex, PlaceIndex)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> PlaceCollector<'_, '_, 'tcx> {
|
||||||
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
|
fn register_place(&mut self, place: Place<'tcx>) -> Option<PlaceIndex> {
|
||||||
|
// Create a place for this projection.
|
||||||
|
let mut place_index = self.map.locals[place.local]?;
|
||||||
|
let mut ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty);
|
||||||
|
tracing::trace!(?place_index, ?ty);
|
||||||
|
|
||||||
|
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
|
||||||
|
&& let ty::Slice(..) = ref_ty.kind()
|
||||||
|
{
|
||||||
|
self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
|
||||||
|
} else if ty.ty.is_enum() {
|
||||||
|
let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
|
||||||
|
self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
|
||||||
|
}
|
||||||
|
|
||||||
|
for proj in place.projection {
|
||||||
|
let track_elem = proj.try_into().ok()?;
|
||||||
|
ty = ty.projection_ty(self.tcx, proj);
|
||||||
|
place_index = self.map.register_place(ty.ty, place_index, track_elem);
|
||||||
|
tracing::trace!(?proj, ?place_index, ?ty);
|
||||||
|
|
||||||
|
if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
|
||||||
|
&& let ty::Slice(..) = ref_ty.kind()
|
||||||
|
{
|
||||||
|
self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
|
||||||
|
} else if ty.ty.is_enum() {
|
||||||
|
let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
|
||||||
|
self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(place_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
|
||||||
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
|
fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
|
||||||
|
if !ctxt.is_use() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.register_place(*place);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign(&mut self, lhs: &Place<'tcx>, rhs: &Rvalue<'tcx>, location: Location) {
|
||||||
|
self.super_assign(lhs, rhs, location);
|
||||||
|
|
||||||
|
match rhs {
|
||||||
|
Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs)) | Rvalue::CopyForDeref(rhs) => {
|
||||||
|
let Some(lhs) = self.register_place(*lhs) else { return };
|
||||||
|
let Some(rhs) = self.register_place(*rhs) else { return };
|
||||||
|
self.assignments.insert((lhs, rhs));
|
||||||
|
}
|
||||||
|
Rvalue::Aggregate(kind, fields) => {
|
||||||
|
let Some(mut lhs) = self.register_place(*lhs) else { return };
|
||||||
|
match **kind {
|
||||||
|
// Do not propagate unions.
|
||||||
|
AggregateKind::Adt(_, _, _, _, Some(_)) => return,
|
||||||
|
AggregateKind::Adt(_, variant, _, _, None) => {
|
||||||
|
let ty = self.map.places[lhs].ty;
|
||||||
|
if ty.is_enum() {
|
||||||
|
lhs = self.map.register_place(ty, lhs, TrackElem::Variant(variant));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AggregateKind::RawPtr(..)
|
||||||
|
| AggregateKind::Array(_)
|
||||||
|
| AggregateKind::Tuple
|
||||||
|
| AggregateKind::Closure(..)
|
||||||
|
| AggregateKind::Coroutine(..)
|
||||||
|
| AggregateKind::CoroutineClosure(..) => {}
|
||||||
|
}
|
||||||
|
for (index, field) in fields.iter_enumerated() {
|
||||||
|
if let Some(rhs) = field.place()
|
||||||
|
&& let Some(rhs) = self.register_place(rhs)
|
||||||
|
{
|
||||||
|
let lhs = self.map.register_place(
|
||||||
|
self.map.places[rhs].ty,
|
||||||
|
lhs,
|
||||||
|
TrackElem::Field(index),
|
||||||
|
);
|
||||||
|
self.assignments.insert((lhs, rhs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Map<'tcx> {
|
||||||
/// Applies a single projection element, yielding the corresponding child.
|
/// Applies a single projection element, yielding the corresponding child.
|
||||||
pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
|
pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
|
||||||
self.projections.get(&(place, elem)).copied()
|
self.projections.get(&(place, elem)).copied()
|
||||||
|
@ -974,7 +1062,10 @@ impl Map {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all direct children.
|
/// Iterate over all direct children.
|
||||||
fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
|
fn children(
|
||||||
|
&self,
|
||||||
|
parent: PlaceIndex,
|
||||||
|
) -> impl Iterator<Item = PlaceIndex> + Captures<'_> + Captures<'tcx> {
|
||||||
Children::new(self, parent)
|
Children::new(self, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,7 +1172,10 @@ impl Map {
|
||||||
/// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
|
/// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
|
||||||
/// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
|
/// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PlaceInfo {
|
struct PlaceInfo<'tcx> {
|
||||||
|
/// Type of the referenced place.
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
|
||||||
/// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
|
/// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
|
||||||
value_index: Option<ValueIndex>,
|
value_index: Option<ValueIndex>,
|
||||||
|
|
||||||
|
@ -1095,24 +1189,24 @@ struct PlaceInfo {
|
||||||
next_sibling: Option<PlaceIndex>,
|
next_sibling: Option<PlaceIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceInfo {
|
impl<'tcx> PlaceInfo<'tcx> {
|
||||||
fn new(proj_elem: Option<TrackElem>) -> Self {
|
fn new(ty: Ty<'tcx>, proj_elem: Option<TrackElem>) -> Self {
|
||||||
Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
|
Self { ty, next_sibling: None, first_child: None, proj_elem, value_index: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Children<'a> {
|
struct Children<'a, 'tcx> {
|
||||||
map: &'a Map,
|
map: &'a Map<'tcx>,
|
||||||
next: Option<PlaceIndex>,
|
next: Option<PlaceIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Children<'a> {
|
impl<'a, 'tcx> Children<'a, 'tcx> {
|
||||||
fn new(map: &'a Map, parent: PlaceIndex) -> Self {
|
fn new(map: &'a Map<'tcx>, parent: PlaceIndex) -> Self {
|
||||||
Self { map, next: map.places[parent].first_child }
|
Self { map, next: map.places[parent].first_child }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Children<'a> {
|
impl Iterator for Children<'_, '_> {
|
||||||
type Item = PlaceIndex;
|
type Item = PlaceIndex;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -1261,7 +1355,7 @@ fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
|
||||||
place_str: &str,
|
place_str: &str,
|
||||||
new: &StateData<V>,
|
new: &StateData<V>,
|
||||||
old: Option<&StateData<V>>,
|
old: Option<&StateData<V>>,
|
||||||
map: &Map,
|
map: &Map<'_>,
|
||||||
f: &mut Formatter<'_>,
|
f: &mut Formatter<'_>,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
if let Some(value) = map.places[place].value_index {
|
if let Some(value) = map.places[place].value_index {
|
||||||
|
@ -1305,7 +1399,7 @@ fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
|
||||||
fn debug_with_context<V: Debug + Eq + HasBottom>(
|
fn debug_with_context<V: Debug + Eq + HasBottom>(
|
||||||
new: &StateData<V>,
|
new: &StateData<V>,
|
||||||
old: Option<&StateData<V>>,
|
old: Option<&StateData<V>>,
|
||||||
map: &Map,
|
map: &Map<'_>,
|
||||||
f: &mut Formatter<'_>,
|
f: &mut Formatter<'_>,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
for (local, place) in map.locals.iter_enumerated() {
|
for (local, place) in map.locals.iter_enumerated() {
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConstAnalysis<'a, 'tcx> {
|
struct ConstAnalysis<'a, 'tcx> {
|
||||||
map: Map,
|
map: Map<'tcx>,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
local_decls: &'a LocalDecls<'tcx>,
|
local_decls: &'a LocalDecls<'tcx>,
|
||||||
ecx: InterpCx<'tcx, DummyMachine>,
|
ecx: InterpCx<'tcx, DummyMachine>,
|
||||||
|
@ -78,7 +78,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
|
||||||
|
|
||||||
const NAME: &'static str = "ConstAnalysis";
|
const NAME: &'static str = "ConstAnalysis";
|
||||||
|
|
||||||
fn map(&self) -> &Map {
|
fn map(&self) -> &Map<'tcx> {
|
||||||
&self.map
|
&self.map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
|
||||||
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map) -> Self {
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self {
|
||||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||||
Self {
|
Self {
|
||||||
map,
|
map,
|
||||||
|
@ -560,12 +560,13 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> {
|
||||||
Self { patch: Patch::new(tcx), local_decls }
|
Self { patch: Patch::new(tcx), local_decls }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(self, ecx, map), ret)]
|
||||||
fn try_make_constant(
|
fn try_make_constant(
|
||||||
&self,
|
&self,
|
||||||
ecx: &mut InterpCx<'tcx, DummyMachine>,
|
ecx: &mut InterpCx<'tcx, DummyMachine>,
|
||||||
place: Place<'tcx>,
|
place: Place<'tcx>,
|
||||||
state: &State<FlatSet<Scalar>>,
|
state: &State<FlatSet<Scalar>>,
|
||||||
map: &Map,
|
map: &Map<'tcx>,
|
||||||
) -> Option<Const<'tcx>> {
|
) -> Option<Const<'tcx>> {
|
||||||
let ty = place.ty(self.local_decls, self.patch.tcx).ty;
|
let ty = place.ty(self.local_decls, self.patch.tcx).ty;
|
||||||
let layout = ecx.layout_of(ty).ok()?;
|
let layout = ecx.layout_of(ty).ok()?;
|
||||||
|
@ -598,10 +599,11 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(map), ret)]
|
||||||
fn propagatable_scalar(
|
fn propagatable_scalar(
|
||||||
place: PlaceIndex,
|
place: PlaceIndex,
|
||||||
state: &State<FlatSet<Scalar>>,
|
state: &State<FlatSet<Scalar>>,
|
||||||
map: &Map,
|
map: &Map<'_>,
|
||||||
) -> Option<Scalar> {
|
) -> Option<Scalar> {
|
||||||
if let FlatSet::Elem(value) = state.get_idx(place, map)
|
if let FlatSet::Elem(value) = state.get_idx(place, map)
|
||||||
&& value.try_to_scalar_int().is_ok()
|
&& value.try_to_scalar_int().is_ok()
|
||||||
|
@ -613,14 +615,14 @@ fn propagatable_scalar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(ecx, state, map))]
|
#[instrument(level = "trace", skip(ecx, state, map), ret)]
|
||||||
fn try_write_constant<'tcx>(
|
fn try_write_constant<'tcx>(
|
||||||
ecx: &mut InterpCx<'tcx, DummyMachine>,
|
ecx: &mut InterpCx<'tcx, DummyMachine>,
|
||||||
dest: &PlaceTy<'tcx>,
|
dest: &PlaceTy<'tcx>,
|
||||||
place: PlaceIndex,
|
place: PlaceIndex,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
state: &State<FlatSet<Scalar>>,
|
state: &State<FlatSet<Scalar>>,
|
||||||
map: &Map,
|
map: &Map<'tcx>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let layout = ecx.layout_of(ty)?;
|
let layout = ecx.layout_of(ty)?;
|
||||||
|
|
||||||
|
@ -719,6 +721,7 @@ impl<'mir, 'tcx>
|
||||||
{
|
{
|
||||||
type FlowState = State<FlatSet<Scalar>>;
|
type FlowState = State<FlatSet<Scalar>>;
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(self, results, statement))]
|
||||||
fn visit_statement_before_primary_effect(
|
fn visit_statement_before_primary_effect(
|
||||||
&mut self,
|
&mut self,
|
||||||
results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
|
results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
|
||||||
|
@ -740,6 +743,7 @@ impl<'mir, 'tcx>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", skip(self, results, statement))]
|
||||||
fn visit_statement_after_primary_effect(
|
fn visit_statement_after_primary_effect(
|
||||||
&mut self,
|
&mut self,
|
||||||
results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
|
results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
|
||||||
|
@ -834,7 +838,7 @@ struct OperandCollector<'tcx, 'map, 'locals, 'a> {
|
||||||
state: &'a State<FlatSet<Scalar>>,
|
state: &'a State<FlatSet<Scalar>>,
|
||||||
visitor: &'a mut Collector<'tcx, 'locals>,
|
visitor: &'a mut Collector<'tcx, 'locals>,
|
||||||
ecx: &'map mut InterpCx<'tcx, DummyMachine>,
|
ecx: &'map mut InterpCx<'tcx, DummyMachine>,
|
||||||
map: &'map Map,
|
map: &'map Map<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
|
impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
|
||||||
|
|
|
@ -123,7 +123,7 @@ struct TOFinder<'tcx, 'a> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
ecx: InterpCx<'tcx, DummyMachine>,
|
ecx: InterpCx<'tcx, DummyMachine>,
|
||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
map: &'a Map,
|
map: &'a Map<'tcx>,
|
||||||
loop_headers: &'a BitSet<BasicBlock>,
|
loop_headers: &'a BitSet<BasicBlock>,
|
||||||
/// We use an arena to avoid cloning the slices when cloning `state`.
|
/// We use an arena to avoid cloning the slices when cloning `state`.
|
||||||
arena: &'a DroplessArena,
|
arena: &'a DroplessArena,
|
||||||
|
|
|
@ -31,16 +31,18 @@ Number of file 0 mappings: 24
|
||||||
= (c0 - c1)
|
= (c0 - c1)
|
||||||
- Code(Counter(0)) at (prev + 1, 5) to (start + 3, 2)
|
- Code(Counter(0)) at (prev + 1, 5) to (start + 3, 2)
|
||||||
|
|
||||||
Function name: closure::main::{closure#0} (unused)
|
Function name: closure::main::{closure#0}
|
||||||
Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06]
|
Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06]
|
||||||
Number of files: 1
|
Number of files: 1
|
||||||
- file 0 => global file 1
|
- file 0 => global file 1
|
||||||
Number of expressions: 0
|
Number of expressions: 1
|
||||||
|
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||||
Number of file 0 mappings: 4
|
Number of file 0 mappings: 4
|
||||||
- Code(Zero) at (prev + 40, 5) to (start + 2, 20)
|
- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20)
|
||||||
- Code(Zero) at (prev + 2, 21) to (start + 2, 10)
|
- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
|
||||||
- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
|
- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
|
||||||
- Code(Zero) at (prev + 1, 9) to (start + 1, 6)
|
= (c0 - c1)
|
||||||
|
- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6)
|
||||||
|
|
||||||
Function name: closure::main::{closure#10} (unused)
|
Function name: closure::main::{closure#10} (unused)
|
||||||
Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21]
|
Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21]
|
||||||
|
|
|
@ -81,101 +81,130 @@ Number of file 0 mappings: 11
|
||||||
= (((c4 + Zero) + Zero) + c3)
|
= (((c4 + Zero) + Zero) + c3)
|
||||||
|
|
||||||
Function name: try_error_result::test2
|
Function name: try_error_result::test2
|
||||||
Raw bytes (280): 0x[01, 01, 24, 01, 07, 00, 09, 03, 0d, 41, 00, 1e, 00, 41, 00, 1e, 00, 41, 00, 4a, 00, 4e, 00, 52, 41, 03, 0d, 52, 41, 03, 0d, 4e, 00, 52, 41, 03, 0d, 4a, 00, 4e, 00, 52, 41, 03, 0d, 66, 00, 45, 00, 45, 00, 66, 00, 45, 00, 7a, 00, 4d, 00, 4d, 00, 7a, 00, 4d, 00, 83, 01, 0d, 87, 01, 00, 00, 8b, 01, 8f, 01, 00, 19, 00, 28, 01, 3d, 01, 03, 17, 03, 08, 09, 00, 0e, 52, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 00, 00, 2f, 00, 30, 1e, 00, 31, 03, 35, 00, 04, 11, 00, 12, 1a, 02, 11, 04, 12, 00, 05, 11, 00, 14, 1a, 00, 17, 00, 41, 19, 00, 41, 00, 42, 00, 00, 43, 00, 5f, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 20, 00, 01, 11, 00, 14, 00, 00, 17, 00, 41, 00, 00, 41, 00, 42, 00, 00, 43, 00, 60, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 20, 46, 04, 11, 00, 14, 4e, 00, 17, 00, 42, 00, 00, 42, 00, 43, 4a, 00, 44, 00, 61, 00, 00, 61, 00, 62, 46, 01, 0d, 00, 20, 62, 01, 11, 00, 14, 45, 00, 17, 01, 36, 00, 01, 36, 00, 37, 66, 01, 12, 00, 2f, 00, 00, 2f, 00, 30, 62, 01, 0d, 00, 20, 76, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 00, 02, 11, 00, 12, 7a, 01, 12, 00, 2f, 00, 01, 11, 00, 12, 76, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, 7f, 01, 01, 00, 02]
|
Raw bytes (358): 0x[01, 01, 3b, 01, 07, 05, 09, 03, 0d, 41, 11, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 4a, 15, 41, 11, 46, 19, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 5e, 25, 49, 21, 49, 21, 5e, 25, 49, 21, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, 92, 01, 41, 03, 0d, 8e, 01, 29, 92, 01, 41, 03, 0d, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, a6, 01, 35, 45, 31, 45, 31, a6, 01, 35, 45, 31, ba, 01, 3d, 4d, 39, 4d, 39, ba, 01, 3d, 4d, 39, c3, 01, 0d, c7, 01, db, 01, cb, 01, cf, 01, 11, 15, d3, 01, d7, 01, 19, 1d, 21, 25, df, 01, e3, 01, 29, 2d, e7, 01, eb, 01, 31, 35, 39, 3d, 28, 01, 3d, 01, 03, 17, 03, 08, 09, 00, 0e, 92, 01, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 11, 00, 2f, 00, 30, 4a, 00, 31, 03, 35, 15, 04, 11, 00, 12, 46, 02, 11, 04, 12, 3e, 05, 11, 00, 14, 46, 00, 17, 00, 41, 19, 00, 41, 00, 42, 42, 00, 43, 00, 5f, 1d, 00, 5f, 00, 60, 3e, 01, 0d, 00, 20, 5a, 01, 11, 00, 14, 49, 00, 17, 00, 41, 21, 00, 41, 00, 42, 5e, 00, 43, 00, 60, 25, 00, 60, 00, 61, 5a, 01, 0d, 00, 20, 86, 01, 04, 11, 00, 14, 8e, 01, 00, 17, 00, 42, 29, 00, 42, 00, 43, 8a, 01, 00, 44, 00, 61, 2d, 00, 61, 00, 62, 86, 01, 01, 0d, 00, 20, a2, 01, 01, 11, 00, 14, 45, 00, 17, 01, 36, 31, 01, 36, 00, 37, a6, 01, 01, 12, 00, 2f, 35, 00, 2f, 00, 30, a2, 01, 01, 0d, 00, 20, b6, 01, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 39, 02, 11, 00, 12, ba, 01, 01, 12, 00, 2f, 3d, 01, 11, 00, 12, b6, 01, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, bf, 01, 01, 01, 00, 02]
|
||||||
Number of files: 1
|
Number of files: 1
|
||||||
- file 0 => global file 1
|
- file 0 => global file 1
|
||||||
Number of expressions: 36
|
Number of expressions: 59
|
||||||
- expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add)
|
- expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add)
|
||||||
- expression 1 operands: lhs = Zero, rhs = Counter(2)
|
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
|
||||||
- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
||||||
- expression 3 operands: lhs = Counter(16), rhs = Zero
|
- expression 3 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 4 operands: lhs = Expression(7, Sub), rhs = Zero
|
- expression 4 operands: lhs = Expression(18, Sub), rhs = Counter(5)
|
||||||
- expression 5 operands: lhs = Counter(16), rhs = Zero
|
- expression 5 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 6 operands: lhs = Expression(7, Sub), rhs = Zero
|
- expression 6 operands: lhs = Expression(16, Sub), rhs = Counter(7)
|
||||||
- expression 7 operands: lhs = Counter(16), rhs = Zero
|
- expression 7 operands: lhs = Expression(17, Sub), rhs = Counter(6)
|
||||||
- expression 8 operands: lhs = Expression(18, Sub), rhs = Zero
|
- expression 8 operands: lhs = Expression(18, Sub), rhs = Counter(5)
|
||||||
- expression 9 operands: lhs = Expression(19, Sub), rhs = Zero
|
- expression 9 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 10 operands: lhs = Expression(20, Sub), rhs = Counter(16)
|
- expression 10 operands: lhs = Expression(18, Sub), rhs = Counter(5)
|
||||||
- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
- expression 11 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 12 operands: lhs = Expression(20, Sub), rhs = Counter(16)
|
- expression 12 operands: lhs = Expression(17, Sub), rhs = Counter(6)
|
||||||
- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(5)
|
||||||
- expression 14 operands: lhs = Expression(19, Sub), rhs = Zero
|
- expression 14 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(16)
|
- expression 15 operands: lhs = Expression(16, Sub), rhs = Counter(7)
|
||||||
- expression 16 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
- expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(6)
|
||||||
- expression 17 operands: lhs = Expression(18, Sub), rhs = Zero
|
- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5)
|
||||||
- expression 18 operands: lhs = Expression(19, Sub), rhs = Zero
|
- expression 18 operands: lhs = Counter(16), rhs = Counter(4)
|
||||||
- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(16)
|
- expression 19 operands: lhs = Expression(23, Sub), rhs = Counter(9)
|
||||||
- expression 20 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
- expression 20 operands: lhs = Counter(18), rhs = Counter(8)
|
||||||
- expression 21 operands: lhs = Expression(25, Sub), rhs = Zero
|
- expression 21 operands: lhs = Counter(18), rhs = Counter(8)
|
||||||
- expression 22 operands: lhs = Counter(17), rhs = Zero
|
- expression 22 operands: lhs = Expression(23, Sub), rhs = Counter(9)
|
||||||
- expression 23 operands: lhs = Counter(17), rhs = Zero
|
- expression 23 operands: lhs = Counter(18), rhs = Counter(8)
|
||||||
- expression 24 operands: lhs = Expression(25, Sub), rhs = Zero
|
- expression 24 operands: lhs = Expression(34, Sub), rhs = Counter(11)
|
||||||
- expression 25 operands: lhs = Counter(17), rhs = Zero
|
- expression 25 operands: lhs = Expression(35, Sub), rhs = Counter(10)
|
||||||
- expression 26 operands: lhs = Expression(30, Sub), rhs = Zero
|
- expression 26 operands: lhs = Expression(36, Sub), rhs = Counter(16)
|
||||||
- expression 27 operands: lhs = Counter(19), rhs = Zero
|
- expression 27 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
||||||
- expression 28 operands: lhs = Counter(19), rhs = Zero
|
- expression 28 operands: lhs = Expression(36, Sub), rhs = Counter(16)
|
||||||
- expression 29 operands: lhs = Expression(30, Sub), rhs = Zero
|
- expression 29 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
||||||
- expression 30 operands: lhs = Counter(19), rhs = Zero
|
- expression 30 operands: lhs = Expression(35, Sub), rhs = Counter(10)
|
||||||
- expression 31 operands: lhs = Expression(32, Add), rhs = Counter(3)
|
- expression 31 operands: lhs = Expression(36, Sub), rhs = Counter(16)
|
||||||
- expression 32 operands: lhs = Expression(33, Add), rhs = Zero
|
- expression 32 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
||||||
- expression 33 operands: lhs = Zero, rhs = Expression(34, Add)
|
- expression 33 operands: lhs = Expression(34, Sub), rhs = Counter(11)
|
||||||
- expression 34 operands: lhs = Expression(35, Add), rhs = Zero
|
- expression 34 operands: lhs = Expression(35, Sub), rhs = Counter(10)
|
||||||
- expression 35 operands: lhs = Counter(6), rhs = Zero
|
- expression 35 operands: lhs = Expression(36, Sub), rhs = Counter(16)
|
||||||
|
- expression 36 operands: lhs = Expression(0, Add), rhs = Counter(3)
|
||||||
|
- expression 37 operands: lhs = Expression(41, Sub), rhs = Counter(13)
|
||||||
|
- expression 38 operands: lhs = Counter(17), rhs = Counter(12)
|
||||||
|
- expression 39 operands: lhs = Counter(17), rhs = Counter(12)
|
||||||
|
- expression 40 operands: lhs = Expression(41, Sub), rhs = Counter(13)
|
||||||
|
- expression 41 operands: lhs = Counter(17), rhs = Counter(12)
|
||||||
|
- expression 42 operands: lhs = Expression(46, Sub), rhs = Counter(15)
|
||||||
|
- expression 43 operands: lhs = Counter(19), rhs = Counter(14)
|
||||||
|
- expression 44 operands: lhs = Counter(19), rhs = Counter(14)
|
||||||
|
- expression 45 operands: lhs = Expression(46, Sub), rhs = Counter(15)
|
||||||
|
- expression 46 operands: lhs = Counter(19), rhs = Counter(14)
|
||||||
|
- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(3)
|
||||||
|
- expression 48 operands: lhs = Expression(49, Add), rhs = Expression(54, Add)
|
||||||
|
- expression 49 operands: lhs = Expression(50, Add), rhs = Expression(51, Add)
|
||||||
|
- expression 50 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 51 operands: lhs = Expression(52, Add), rhs = Expression(53, Add)
|
||||||
|
- expression 52 operands: lhs = Counter(6), rhs = Counter(7)
|
||||||
|
- expression 53 operands: lhs = Counter(8), rhs = Counter(9)
|
||||||
|
- expression 54 operands: lhs = Expression(55, Add), rhs = Expression(56, Add)
|
||||||
|
- expression 55 operands: lhs = Counter(10), rhs = Counter(11)
|
||||||
|
- expression 56 operands: lhs = Expression(57, Add), rhs = Expression(58, Add)
|
||||||
|
- expression 57 operands: lhs = Counter(12), rhs = Counter(13)
|
||||||
|
- expression 58 operands: lhs = Counter(14), rhs = Counter(15)
|
||||||
Number of file 0 mappings: 40
|
Number of file 0 mappings: 40
|
||||||
- Code(Counter(0)) at (prev + 61, 1) to (start + 3, 23)
|
- Code(Counter(0)) at (prev + 61, 1) to (start + 3, 23)
|
||||||
- Code(Expression(0, Add)) at (prev + 8, 9) to (start + 0, 14)
|
- Code(Expression(0, Add)) at (prev + 8, 9) to (start + 0, 14)
|
||||||
= (c0 + (Zero + c2))
|
= (c0 + (c1 + c2))
|
||||||
- Code(Expression(20, Sub)) at (prev + 2, 9) to (start + 4, 26)
|
- Code(Expression(36, Sub)) at (prev + 2, 9) to (start + 4, 26)
|
||||||
= ((c0 + (Zero + c2)) - c3)
|
= ((c0 + (c1 + c2)) - c3)
|
||||||
- Code(Counter(16)) at (prev + 6, 13) to (start + 0, 47)
|
- Code(Counter(16)) at (prev + 6, 13) to (start + 0, 47)
|
||||||
- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
|
- Code(Counter(4)) at (prev + 0, 47) to (start + 0, 48)
|
||||||
- Code(Expression(7, Sub)) at (prev + 0, 49) to (start + 3, 53)
|
- Code(Expression(18, Sub)) at (prev + 0, 49) to (start + 3, 53)
|
||||||
= (c16 - Zero)
|
= (c16 - c4)
|
||||||
- Code(Zero) at (prev + 4, 17) to (start + 0, 18)
|
- Code(Counter(5)) at (prev + 4, 17) to (start + 0, 18)
|
||||||
- Code(Expression(6, Sub)) at (prev + 2, 17) to (start + 4, 18)
|
- Code(Expression(17, Sub)) at (prev + 2, 17) to (start + 4, 18)
|
||||||
= ((c16 - Zero) - Zero)
|
= ((c16 - c4) - c5)
|
||||||
- Code(Zero) at (prev + 5, 17) to (start + 0, 20)
|
- Code(Expression(15, Sub)) at (prev + 5, 17) to (start + 0, 20)
|
||||||
- Code(Expression(6, Sub)) at (prev + 0, 23) to (start + 0, 65)
|
= ((((c16 - c4) - c5) - c6) - c7)
|
||||||
= ((c16 - Zero) - Zero)
|
- Code(Expression(17, Sub)) at (prev + 0, 23) to (start + 0, 65)
|
||||||
|
= ((c16 - c4) - c5)
|
||||||
- Code(Counter(6)) at (prev + 0, 65) to (start + 0, 66)
|
- Code(Counter(6)) at (prev + 0, 65) to (start + 0, 66)
|
||||||
- Code(Zero) at (prev + 0, 67) to (start + 0, 95)
|
- Code(Expression(16, Sub)) at (prev + 0, 67) to (start + 0, 95)
|
||||||
- Code(Zero) at (prev + 0, 95) to (start + 0, 96)
|
= (((c16 - c4) - c5) - c6)
|
||||||
- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
|
- Code(Counter(7)) at (prev + 0, 95) to (start + 0, 96)
|
||||||
- Code(Zero) at (prev + 1, 17) to (start + 0, 20)
|
- Code(Expression(15, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
||||||
- Code(Zero) at (prev + 0, 23) to (start + 0, 65)
|
= ((((c16 - c4) - c5) - c6) - c7)
|
||||||
- Code(Zero) at (prev + 0, 65) to (start + 0, 66)
|
- Code(Expression(22, Sub)) at (prev + 1, 17) to (start + 0, 20)
|
||||||
- Code(Zero) at (prev + 0, 67) to (start + 0, 96)
|
= ((c18 - c8) - c9)
|
||||||
- Code(Zero) at (prev + 0, 96) to (start + 0, 97)
|
- Code(Counter(18)) at (prev + 0, 23) to (start + 0, 65)
|
||||||
- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
|
- Code(Counter(8)) at (prev + 0, 65) to (start + 0, 66)
|
||||||
- Code(Expression(17, Sub)) at (prev + 4, 17) to (start + 0, 20)
|
- Code(Expression(23, Sub)) at (prev + 0, 67) to (start + 0, 96)
|
||||||
= (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
|
= (c18 - c8)
|
||||||
- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 66)
|
- Code(Counter(9)) at (prev + 0, 96) to (start + 0, 97)
|
||||||
= (((c0 + (Zero + c2)) - c3) - c16)
|
- Code(Expression(22, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
||||||
- Code(Zero) at (prev + 0, 66) to (start + 0, 67)
|
= ((c18 - c8) - c9)
|
||||||
- Code(Expression(18, Sub)) at (prev + 0, 68) to (start + 0, 97)
|
- Code(Expression(33, Sub)) at (prev + 4, 17) to (start + 0, 20)
|
||||||
= ((((c0 + (Zero + c2)) - c3) - c16) - Zero)
|
= (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
|
||||||
- Code(Zero) at (prev + 0, 97) to (start + 0, 98)
|
- Code(Expression(35, Sub)) at (prev + 0, 23) to (start + 0, 66)
|
||||||
- Code(Expression(17, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
= (((c0 + (c1 + c2)) - c3) - c16)
|
||||||
= (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
|
- Code(Counter(10)) at (prev + 0, 66) to (start + 0, 67)
|
||||||
- Code(Expression(24, Sub)) at (prev + 1, 17) to (start + 0, 20)
|
- Code(Expression(34, Sub)) at (prev + 0, 68) to (start + 0, 97)
|
||||||
= ((c17 - Zero) - Zero)
|
= ((((c0 + (c1 + c2)) - c3) - c16) - c10)
|
||||||
|
- Code(Counter(11)) at (prev + 0, 97) to (start + 0, 98)
|
||||||
|
- Code(Expression(33, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
||||||
|
= (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
|
||||||
|
- Code(Expression(40, Sub)) at (prev + 1, 17) to (start + 0, 20)
|
||||||
|
= ((c17 - c12) - c13)
|
||||||
- Code(Counter(17)) at (prev + 0, 23) to (start + 1, 54)
|
- Code(Counter(17)) at (prev + 0, 23) to (start + 1, 54)
|
||||||
- Code(Zero) at (prev + 1, 54) to (start + 0, 55)
|
- Code(Counter(12)) at (prev + 1, 54) to (start + 0, 55)
|
||||||
- Code(Expression(25, Sub)) at (prev + 1, 18) to (start + 0, 47)
|
- Code(Expression(41, Sub)) at (prev + 1, 18) to (start + 0, 47)
|
||||||
= (c17 - Zero)
|
= (c17 - c12)
|
||||||
- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
|
- Code(Counter(13)) at (prev + 0, 47) to (start + 0, 48)
|
||||||
- Code(Expression(24, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
- Code(Expression(40, Sub)) at (prev + 1, 13) to (start + 0, 32)
|
||||||
= ((c17 - Zero) - Zero)
|
= ((c17 - c12) - c13)
|
||||||
- Code(Expression(29, Sub)) at (prev + 1, 17) to (start + 0, 20)
|
- Code(Expression(45, Sub)) at (prev + 1, 17) to (start + 0, 20)
|
||||||
= ((c19 - Zero) - Zero)
|
= ((c19 - c14) - c15)
|
||||||
- Code(Counter(19)) at (prev + 0, 23) to (start + 1, 54)
|
- Code(Counter(19)) at (prev + 0, 23) to (start + 1, 54)
|
||||||
- Code(Zero) at (prev + 2, 17) to (start + 0, 18)
|
- Code(Counter(14)) at (prev + 2, 17) to (start + 0, 18)
|
||||||
- Code(Expression(30, Sub)) at (prev + 1, 18) to (start + 0, 47)
|
- Code(Expression(46, Sub)) at (prev + 1, 18) to (start + 0, 47)
|
||||||
= (c19 - Zero)
|
= (c19 - c14)
|
||||||
- Code(Zero) at (prev + 1, 17) to (start + 0, 18)
|
- Code(Counter(15)) at (prev + 1, 17) to (start + 0, 18)
|
||||||
- Code(Expression(29, Sub)) at (prev + 2, 13) to (start + 0, 32)
|
- Code(Expression(45, Sub)) at (prev + 2, 13) to (start + 0, 32)
|
||||||
= ((c19 - Zero) - Zero)
|
= ((c19 - c14) - c15)
|
||||||
- Code(Counter(3)) at (prev + 3, 5) to (start + 0, 11)
|
- Code(Counter(3)) at (prev + 3, 5) to (start + 0, 11)
|
||||||
- Code(Expression(31, Add)) at (prev + 1, 1) to (start + 0, 2)
|
- Code(Expression(47, Add)) at (prev + 1, 1) to (start + 0, 2)
|
||||||
= (((Zero + ((c6 + Zero) + Zero)) + Zero) + c3)
|
= ((((c4 + c5) + ((c6 + c7) + (c8 + c9))) + ((c10 + c11) + ((c12 + c13) + (c14 + c15)))) + c3)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
- // MIR for `foo` before DataflowConstProp
|
||||||
|
+ // MIR for `foo` after DataflowConstProp
|
||||||
|
|
||||||
|
fn foo() -> u32 {
|
||||||
|
let mut _0: u32;
|
||||||
|
let _1: (u32, u32);
|
||||||
|
let mut _4: bool;
|
||||||
|
let mut _5: u32;
|
||||||
|
scope 1 {
|
||||||
|
debug a => _1;
|
||||||
|
let _2: (u32, u32);
|
||||||
|
scope 2 {
|
||||||
|
debug b => _2;
|
||||||
|
let _3: u32;
|
||||||
|
scope 3 {
|
||||||
|
debug c => _3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
_1 = const Foo;
|
||||||
|
StorageLive(_2);
|
||||||
|
- _2 = _1;
|
||||||
|
+ _2 = const (5_u32, 3_u32);
|
||||||
|
StorageLive(_3);
|
||||||
|
- _3 = (_2.1: u32);
|
||||||
|
+ _3 = const 3_u32;
|
||||||
|
StorageLive(_4);
|
||||||
|
StorageLive(_5);
|
||||||
|
- _5 = _3;
|
||||||
|
- _4 = Ge(move _5, const 2_u32);
|
||||||
|
- switchInt(move _4) -> [0: bb2, otherwise: bb1];
|
||||||
|
+ _5 = const 3_u32;
|
||||||
|
+ _4 = const true;
|
||||||
|
+ switchInt(const true) -> [0: bb2, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_5);
|
||||||
|
- _0 = (_2.0: u32);
|
||||||
|
+ _0 = const 5_u32;
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_5);
|
||||||
|
_0 = const 13_u32;
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
StorageDead(_4);
|
||||||
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ALLOC0 (size: 8, align: 4) {
|
||||||
|
+ 05 00 00 00 03 00 00 00 │ ........
|
||||||
|
}
|
||||||
|
|
42
tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
Normal file
42
tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//! Verify that we manage to propagate the value of aggregate `a` even without directly mentioning
|
||||||
|
//! the contained scalars.
|
||||||
|
//@ test-mir-pass: DataflowConstProp
|
||||||
|
|
||||||
|
const Foo: (u32, u32) = (5, 3);
|
||||||
|
|
||||||
|
fn foo() -> u32 {
|
||||||
|
// CHECK-LABEL: fn foo(
|
||||||
|
// CHECK: debug a => [[a:_.*]];
|
||||||
|
// CHECK: debug b => [[b:_.*]];
|
||||||
|
// CHECK: debug c => [[c:_.*]];
|
||||||
|
|
||||||
|
// CHECK:bb0: {
|
||||||
|
// CHECK: [[a]] = const Foo;
|
||||||
|
// CHECK: [[b]] = const (5_u32, 3_u32);
|
||||||
|
// CHECK: [[c]] = const 3_u32;
|
||||||
|
// CHECK: {{_.*}} = const 3_u32;
|
||||||
|
// CHECK: {{_.*}} = const true;
|
||||||
|
// CHECK: switchInt(const true) -> [0: bb2, otherwise: bb1];
|
||||||
|
|
||||||
|
// CHECK:bb1: {
|
||||||
|
// CHECK: _0 = const 5_u32;
|
||||||
|
// CHECK: goto -> bb3;
|
||||||
|
|
||||||
|
// CHECK:bb2: {
|
||||||
|
// CHECK: _0 = const 13_u32;
|
||||||
|
// CHECK: goto -> bb3;
|
||||||
|
|
||||||
|
let a = Foo;
|
||||||
|
// This copies the struct in `a`. We want to ensure that we do track the contents of `a`
|
||||||
|
// because we need to read `b` later.
|
||||||
|
let b = a;
|
||||||
|
let c = b.1;
|
||||||
|
if c >= 2 { b.0 } else { 13 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// CHECK-LABEL: fn main(
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR aggregate_copy.foo.DataflowConstProp.diff
|
|
@ -106,8 +106,7 @@
|
||||||
- _7 = (_10.0: f32);
|
- _7 = (_10.0: f32);
|
||||||
+ _7 = const 4f32;
|
+ _7 = const 4f32;
|
||||||
StorageLive(_8);
|
StorageLive(_8);
|
||||||
- _8 = (_10.1: std::option::Option<S>);
|
_8 = (_10.1: std::option::Option<S>);
|
||||||
+ _8 = const Option::<S>::Some(S(1_i32));
|
|
||||||
StorageLive(_9);
|
StorageLive(_9);
|
||||||
_9 = (_10.2: &[f32]);
|
_9 = (_10.2: &[f32]);
|
||||||
StorageDead(_10);
|
StorageDead(_10);
|
||||||
|
@ -157,8 +156,7 @@
|
||||||
+ _23 = const 82f32;
|
+ _23 = const 82f32;
|
||||||
StorageLive(_24);
|
StorageLive(_24);
|
||||||
_37 = deref_copy (*_26);
|
_37 = deref_copy (*_26);
|
||||||
- _24 = ((*_37).1: std::option::Option<S>);
|
_24 = ((*_37).1: std::option::Option<S>);
|
||||||
+ _24 = const Option::<S>::Some(S(35_i32));
|
|
||||||
StorageLive(_25);
|
StorageLive(_25);
|
||||||
_38 = deref_copy (*_26);
|
_38 = deref_copy (*_26);
|
||||||
_25 = ((*_38).2: &[f32]);
|
_25 = ((*_38).2: &[f32]);
|
||||||
|
@ -168,12 +166,11 @@
|
||||||
- _28 = _23;
|
- _28 = _23;
|
||||||
+ _28 = const 82f32;
|
+ _28 = const 82f32;
|
||||||
StorageLive(_29);
|
StorageLive(_29);
|
||||||
- _29 = _24;
|
_29 = _24;
|
||||||
+ _29 = const Option::<S>::Some(S(35_i32));
|
|
||||||
StorageLive(_30);
|
StorageLive(_30);
|
||||||
_30 = _25;
|
_30 = _25;
|
||||||
- _27 = BigStruct(move _28, move _29, move _30);
|
- _27 = BigStruct(move _28, move _29, move _30);
|
||||||
+ _27 = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move _30);
|
+ _27 = BigStruct(const 82f32, move _29, move _30);
|
||||||
StorageDead(_30);
|
StorageDead(_30);
|
||||||
StorageDead(_29);
|
StorageDead(_29);
|
||||||
StorageDead(_28);
|
StorageDead(_28);
|
||||||
|
@ -199,29 +196,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ ALLOC2 (size: 8, align: 4) { .. }
|
+ ALLOC2 (size: 4, align: 4) { .. }
|
||||||
+
|
|
||||||
+ ALLOC3 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC4 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC5 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC6 (size: 4, align: 4) { .. }
|
|
||||||
+
|
+
|
||||||
ALLOC1 (static: BIG_STAT, size: 4, align: 4) { .. }
|
ALLOC1 (static: BIG_STAT, size: 4, align: 4) { .. }
|
||||||
|
|
||||||
- ALLOC2 (size: 20, align: 4) { .. }
|
- ALLOC2 (size: 20, align: 4) { .. }
|
||||||
+ ALLOC7 (size: 20, align: 4) { .. }
|
+ ALLOC3 (size: 20, align: 4) { .. }
|
||||||
|
|
||||||
- ALLOC3 (size: 8, align: 4) { .. }
|
- ALLOC3 (size: 8, align: 4) { .. }
|
||||||
+ ALLOC8 (size: 8, align: 4) { .. }
|
+ ALLOC4 (size: 8, align: 4) { .. }
|
||||||
|
|
||||||
ALLOC0 (static: SMALL_STAT, size: 4, align: 4) { .. }
|
ALLOC0 (static: SMALL_STAT, size: 4, align: 4) { .. }
|
||||||
|
|
||||||
- ALLOC4 (size: 20, align: 4) { .. }
|
- ALLOC4 (size: 20, align: 4) { .. }
|
||||||
+ ALLOC9 (size: 20, align: 4) { .. }
|
+ ALLOC5 (size: 20, align: 4) { .. }
|
||||||
|
|
||||||
- ALLOC5 (size: 4, align: 4) { .. }
|
- ALLOC5 (size: 4, align: 4) { .. }
|
||||||
+ ALLOC10 (size: 4, align: 4) { .. }
|
+ ALLOC6 (size: 4, align: 4) { .. }
|
||||||
|
|
||||||
|
|
|
@ -106,8 +106,7 @@
|
||||||
- _7 = (_10.0: f32);
|
- _7 = (_10.0: f32);
|
||||||
+ _7 = const 4f32;
|
+ _7 = const 4f32;
|
||||||
StorageLive(_8);
|
StorageLive(_8);
|
||||||
- _8 = (_10.1: std::option::Option<S>);
|
_8 = (_10.1: std::option::Option<S>);
|
||||||
+ _8 = const Option::<S>::Some(S(1_i32));
|
|
||||||
StorageLive(_9);
|
StorageLive(_9);
|
||||||
_9 = (_10.2: &[f32]);
|
_9 = (_10.2: &[f32]);
|
||||||
StorageDead(_10);
|
StorageDead(_10);
|
||||||
|
@ -157,8 +156,7 @@
|
||||||
+ _23 = const 82f32;
|
+ _23 = const 82f32;
|
||||||
StorageLive(_24);
|
StorageLive(_24);
|
||||||
_37 = deref_copy (*_26);
|
_37 = deref_copy (*_26);
|
||||||
- _24 = ((*_37).1: std::option::Option<S>);
|
_24 = ((*_37).1: std::option::Option<S>);
|
||||||
+ _24 = const Option::<S>::Some(S(35_i32));
|
|
||||||
StorageLive(_25);
|
StorageLive(_25);
|
||||||
_38 = deref_copy (*_26);
|
_38 = deref_copy (*_26);
|
||||||
_25 = ((*_38).2: &[f32]);
|
_25 = ((*_38).2: &[f32]);
|
||||||
|
@ -168,12 +166,11 @@
|
||||||
- _28 = _23;
|
- _28 = _23;
|
||||||
+ _28 = const 82f32;
|
+ _28 = const 82f32;
|
||||||
StorageLive(_29);
|
StorageLive(_29);
|
||||||
- _29 = _24;
|
_29 = _24;
|
||||||
+ _29 = const Option::<S>::Some(S(35_i32));
|
|
||||||
StorageLive(_30);
|
StorageLive(_30);
|
||||||
_30 = _25;
|
_30 = _25;
|
||||||
- _27 = BigStruct(move _28, move _29, move _30);
|
- _27 = BigStruct(move _28, move _29, move _30);
|
||||||
+ _27 = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move _30);
|
+ _27 = BigStruct(const 82f32, move _29, move _30);
|
||||||
StorageDead(_30);
|
StorageDead(_30);
|
||||||
StorageDead(_29);
|
StorageDead(_29);
|
||||||
StorageDead(_28);
|
StorageDead(_28);
|
||||||
|
@ -199,29 +196,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ ALLOC2 (size: 8, align: 4) { .. }
|
+ ALLOC2 (size: 4, align: 4) { .. }
|
||||||
+
|
|
||||||
+ ALLOC3 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC4 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC5 (size: 8, align: 4) { .. }
|
|
||||||
+
|
|
||||||
+ ALLOC6 (size: 4, align: 4) { .. }
|
|
||||||
+
|
+
|
||||||
ALLOC1 (static: BIG_STAT, size: 8, align: 8) { .. }
|
ALLOC1 (static: BIG_STAT, size: 8, align: 8) { .. }
|
||||||
|
|
||||||
- ALLOC2 (size: 32, align: 8) { .. }
|
- ALLOC2 (size: 32, align: 8) { .. }
|
||||||
+ ALLOC7 (size: 32, align: 8) { .. }
|
+ ALLOC3 (size: 32, align: 8) { .. }
|
||||||
|
|
||||||
- ALLOC3 (size: 8, align: 4) { .. }
|
- ALLOC3 (size: 8, align: 4) { .. }
|
||||||
+ ALLOC8 (size: 8, align: 4) { .. }
|
+ ALLOC4 (size: 8, align: 4) { .. }
|
||||||
|
|
||||||
ALLOC0 (static: SMALL_STAT, size: 8, align: 8) { .. }
|
ALLOC0 (static: SMALL_STAT, size: 8, align: 8) { .. }
|
||||||
|
|
||||||
- ALLOC4 (size: 32, align: 8) { .. }
|
- ALLOC4 (size: 32, align: 8) { .. }
|
||||||
+ ALLOC9 (size: 32, align: 8) { .. }
|
+ ALLOC5 (size: 32, align: 8) { .. }
|
||||||
|
|
||||||
- ALLOC5 (size: 4, align: 4) { .. }
|
- ALLOC5 (size: 4, align: 4) { .. }
|
||||||
+ ALLOC10 (size: 4, align: 4) { .. }
|
+ ALLOC6 (size: 4, align: 4) { .. }
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ fn main() {
|
||||||
const SMALL_VAL: SmallStruct = SmallStruct(4., Some(S(1)), &[]);
|
const SMALL_VAL: SmallStruct = SmallStruct(4., Some(S(1)), &[]);
|
||||||
|
|
||||||
// CHECK: [[a1]] = const 4f32;
|
// CHECK: [[a1]] = const 4f32;
|
||||||
// CHECK: [[b1]] = const Option::<S>::Some(S(1_i32));
|
// CHECK: [[b1]] = ({{_.*}}.1: std::option::Option<S>);
|
||||||
// CHECK: [[c1]] = ({{_.*}}.2: &[f32]);
|
// CHECK: [[c1]] = ({{_.*}}.2: &[f32]);
|
||||||
let SmallStruct(a1, b1, c1) = SMALL_VAL;
|
let SmallStruct(a1, b1, c1) = SMALL_VAL;
|
||||||
|
|
||||||
|
@ -69,12 +69,12 @@ fn main() {
|
||||||
|
|
||||||
static BIG_STAT: &BigStruct = &BigStruct(82., Some(S(35)), &[45., 72.]);
|
static BIG_STAT: &BigStruct = &BigStruct(82., Some(S(35)), &[45., 72.]);
|
||||||
// CHECK: [[a4]] = const 82f32;
|
// CHECK: [[a4]] = const 82f32;
|
||||||
// CHECK: [[b4]] = const Option::<S>::Some(S(35_i32));
|
// CHECK: [[b4]] = ((*{{_.*}}).1: std::option::Option<S>);
|
||||||
// CHECK: [[c4]] = ((*{{_.*}}).2: &[f32]);
|
// CHECK: [[c4]] = ((*{{_.*}}).2: &[f32]);
|
||||||
let BigStruct(a4, b4, c4) = *BIG_STAT;
|
let BigStruct(a4, b4, c4) = *BIG_STAT;
|
||||||
|
|
||||||
// We arbitrarily limit the size of synthetized values to 4 pointers.
|
// We arbitrarily limit the size of synthetized values to 4 pointers.
|
||||||
// `BigStruct` can be read, but we will keep a MIR aggregate for this.
|
// `BigStruct` can be read, but we will keep a MIR aggregate for this.
|
||||||
// CHECK: [[bs]] = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move {{_.*}});
|
// CHECK: [[bs]] = BigStruct(const 82f32, move {{.*}}, move {{_.*}});
|
||||||
let bs = BigStruct(a4, b4, c4);
|
let bs = BigStruct(a4, b4, c4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,20 @@
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_1);
|
StorageLive(_1);
|
||||||
_1 = Less;
|
- _1 = Less;
|
||||||
_0 = move _1 as i8 (Transmute);
|
- _0 = move _1 as i8 (Transmute);
|
||||||
|
+ _1 = const Less;
|
||||||
|
+ _0 = const std::cmp::Ordering::Less as i8 (Transmute);
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ALLOC0 (size: 1, align: 1) {
|
||||||
|
+ ff │ .
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ALLOC1 (size: 1, align: 1) {
|
||||||
|
+ ff │ .
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,20 @@
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_1);
|
StorageLive(_1);
|
||||||
_1 = Less;
|
- _1 = Less;
|
||||||
_0 = move _1 as i8 (Transmute);
|
- _0 = move _1 as i8 (Transmute);
|
||||||
|
+ _1 = const Less;
|
||||||
|
+ _0 = const std::cmp::Ordering::Less as i8 (Transmute);
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ALLOC0 (size: 1, align: 1) {
|
||||||
|
+ ff │ .
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ALLOC1 (size: 1, align: 1) {
|
||||||
|
+ ff │ .
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
- // MIR for `aggregate_copy` before JumpThreading
|
||||||
|
+ // MIR for `aggregate_copy` after JumpThreading
|
||||||
|
|
||||||
|
fn aggregate_copy() -> u32 {
|
||||||
|
let mut _0: u32;
|
||||||
|
let _1: (u32, u32);
|
||||||
|
let mut _4: bool;
|
||||||
|
let mut _5: u32;
|
||||||
|
scope 1 {
|
||||||
|
debug a => _1;
|
||||||
|
let _2: (u32, u32);
|
||||||
|
scope 2 {
|
||||||
|
debug b => _2;
|
||||||
|
let _3: u32;
|
||||||
|
scope 3 {
|
||||||
|
debug c => _3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
_1 = const aggregate_copy::Foo;
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = _1;
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_2.1: u32);
|
||||||
|
StorageLive(_4);
|
||||||
|
StorageLive(_5);
|
||||||
|
_5 = _3;
|
||||||
|
_4 = Eq(move _5, const 2_u32);
|
||||||
|
- switchInt(move _4) -> [0: bb2, otherwise: bb1];
|
||||||
|
+ goto -> bb2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_5);
|
||||||
|
_0 = (_2.0: u32);
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_5);
|
||||||
|
_0 = const 13_u32;
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
StorageDead(_4);
|
||||||
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
- // MIR for `aggregate_copy` before JumpThreading
|
||||||
|
+ // MIR for `aggregate_copy` after JumpThreading
|
||||||
|
|
||||||
|
fn aggregate_copy() -> u32 {
|
||||||
|
let mut _0: u32;
|
||||||
|
let _1: (u32, u32);
|
||||||
|
let mut _4: bool;
|
||||||
|
let mut _5: u32;
|
||||||
|
scope 1 {
|
||||||
|
debug a => _1;
|
||||||
|
let _2: (u32, u32);
|
||||||
|
scope 2 {
|
||||||
|
debug b => _2;
|
||||||
|
let _3: u32;
|
||||||
|
scope 3 {
|
||||||
|
debug c => _3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
_1 = const aggregate_copy::Foo;
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = _1;
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_2.1: u32);
|
||||||
|
StorageLive(_4);
|
||||||
|
StorageLive(_5);
|
||||||
|
_5 = _3;
|
||||||
|
_4 = Eq(move _5, const 2_u32);
|
||||||
|
- switchInt(move _4) -> [0: bb2, otherwise: bb1];
|
||||||
|
+ goto -> bb2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_5);
|
||||||
|
_0 = (_2.0: u32);
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_5);
|
||||||
|
_0 = const 13_u32;
|
||||||
|
goto -> bb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
StorageDead(_4);
|
||||||
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -506,6 +506,21 @@ fn assume(a: u8, b: bool) -> u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify that jump threading succeeds seeing through copies of aggregates.
|
||||||
|
fn aggregate_copy() -> u32 {
|
||||||
|
// CHECK-LABEL: fn aggregate_copy(
|
||||||
|
// CHECK-NOT: switchInt(
|
||||||
|
|
||||||
|
const Foo: (u32, u32) = (5, 3);
|
||||||
|
|
||||||
|
let a = Foo;
|
||||||
|
// This copies a tuple, we want to ensure that the threading condition on `b.1` propagates to a
|
||||||
|
// condition on `a.1`.
|
||||||
|
let b = a;
|
||||||
|
let c = b.1;
|
||||||
|
if c == 2 { b.0 } else { 13 }
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// CHECK-LABEL: fn main(
|
// CHECK-LABEL: fn main(
|
||||||
too_complex(Ok(0));
|
too_complex(Ok(0));
|
||||||
|
@ -534,3 +549,4 @@ fn main() {
|
||||||
// EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff
|
// EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff
|
||||||
// EMIT_MIR jump_threading.aggregate.JumpThreading.diff
|
// EMIT_MIR jump_threading.aggregate.JumpThreading.diff
|
||||||
// EMIT_MIR jump_threading.assume.JumpThreading.diff
|
// EMIT_MIR jump_threading.assume.JumpThreading.diff
|
||||||
|
// EMIT_MIR jump_threading.aggregate_copy.JumpThreading.diff
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue