diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 83fd8ccba60e5..87322a878e0b0 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -26,7 +26,7 @@ rustc_index::newtype_index!( /// This index uniquely identifies a tracked place and therefore a slot in [`State`]. /// /// It is an implementation detail of this module. - struct ValueIndex {} + pub struct ValueIndex {} ); /// See [`State`]. @@ -211,22 +211,9 @@ impl State { /// The target place must have been flooded before calling this method. pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map<'_>) { let State::Reachable(values) = self else { return }; - - // If both places are tracked, we copy the value to the target. - // If the target is tracked, but the source is not, we do nothing, as invalidation has - // already been performed. - if let Some(target_value) = map.places[target].value_index { - if let Some(source_value) = map.places[source].value_index { - values.insert(target_value, values.get(source_value).clone()); - } - } - for target_child in map.children(target) { - // Try to find corresponding child and recurse. Reasoning is similar as above. - let projection = map.places[target_child].proj_elem.unwrap(); - if let Some(source_child) = map.projections.get(&(source, projection)) { - self.insert_place_idx(target_child, *source_child, map); - } - } + map.for_each_value_pair(target, source, &mut |target, source| { + values.insert(target, values.get(source).clone()); + }); } /// Helper method to interpret `target = result`. @@ -677,6 +664,26 @@ impl<'tcx> Map<'tcx> { self.find_extra(place, [TrackElem::DerefLen]) } + /// Locates the value corresponding to the given place. + pub fn value(&self, place: PlaceIndex) -> Option { + self.places[place].value_index + } + + /// Locates the value corresponding to the given place. + pub fn find_value(&self, place: PlaceRef<'_>) -> Option { + self.value(self.find(place)?) + } + + /// Locates the value corresponding to the given discriminant. + pub fn find_discr_value(&self, place: PlaceRef<'_>) -> Option { + self.value(self.find_discr(place)?) + } + + /// Locates the value corresponding to the given length. + pub fn find_len_value(&self, place: PlaceRef<'_>) -> Option { + self.value(self.find_len(place)?) + } + /// Iterate over all direct children. fn children(&self, parent: PlaceIndex) -> impl Iterator { Children::new(self, parent) @@ -689,7 +696,7 @@ impl<'tcx> Map<'tcx> { /// /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track /// as such. - fn for_each_aliasing_place( + pub fn for_each_aliasing_place( &self, place: PlaceRef<'_>, tail_elem: Option, @@ -778,6 +785,31 @@ impl<'tcx> Map<'tcx> { } } } + + /// Recursively iterates on each value contained in `target`, paired with matching projection + /// inside `source`. + pub fn for_each_value_pair( + &self, + target: PlaceIndex, + source: PlaceIndex, + f: &mut impl FnMut(ValueIndex, ValueIndex), + ) { + // If both places are tracked, we copy the value to the target. + // If the target is tracked, but the source is not, we do nothing, as invalidation has + // already been performed. + if let Some(target_value) = self.places[target].value_index { + if let Some(source_value) = self.places[source].value_index { + f(target_value, source_value) + } + } + for target_child in self.children(target) { + // Try to find corresponding child and recurse. Reasoning is similar as above. + let projection = self.places[target_child].proj_elem.unwrap(); + if let Some(source_child) = self.projections.get(&(source, projection)) { + self.for_each_value_pair(target_child, *source_child, f); + } + } + } } /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`]. diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index b45bff2af4478..59e7c82fdcd0a 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -21,24 +21,21 @@ //! on `place` with given `value`, irrespective of the polarity and target of that //! replacement condition. //! -//! We then walk the CFG backwards transforming the set of conditions. +//! We then walk the CFG in post-order transforming the set of conditions. //! When we find a fulfilling assignment, we record a `ThreadingOpportunity`. //! All `ThreadingOpportunity`s are applied to the body, by duplicating blocks if required. //! -//! The optimization search can be very heavy, as it performs a DFS on MIR starting from -//! each `SwitchInt` terminator. To manage the complexity, we: -//! - bound the maximum depth by a constant `MAX_BACKTRACK`; -//! - we only traverse `Goto` terminators. -//! //! We try to avoid creating irreducible control-flow by not threading through a loop header. //! -//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction +//! Applying the optimisation can create a lot of new MIR, so we bound the instruction //! cost by `MAX_COST`. +use std::cell::OnceCell; + use rustc_arena::DroplessArena; use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; @@ -48,7 +45,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ScalarInt, TyCtxt}; use rustc_mir_dataflow::lattice::HasBottom; -use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem}; +use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, TrackElem, ValueIndex}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -56,7 +53,6 @@ use crate::cost_checker::CostChecker; pub(super) struct JumpThreading; -const MAX_BACKTRACK: usize = 5; const MAX_COST: usize = 100; const MAX_PLACES: usize = 100; @@ -86,11 +82,40 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { arena, map: Map::new(tcx, body, Some(MAX_PLACES)), loop_headers: loop_headers(body), + entry_states: IndexVec::from_elem(ConditionSet::BOTTOM, &body.basic_blocks), opportunities: Vec::new(), + costs: IndexVec::from_elem(OnceCell::new(), &body.basic_blocks), }; - for (bb, _) in traversal::preorder(body) { - finder.start_from_switch(bb); + for (bb, bbdata) in traversal::postorder(body) { + if bbdata.is_cleanup { + continue; + } + + let mut state = finder.populate_from_outgoing_edges(bb); + trace!("output_states[{bb:?}] = {state:?}"); + + finder.process_terminator(bb, &mut state); + trace!("pre_terminator_states[{bb:?}] = {state:?}"); + + for stmt in bbdata.statements.iter().rev() { + if state.is_empty() { + break; + } + + finder.process_statement(bb, stmt, &mut state); + + // When a statement mutates a place, assignments to that place that happen + // above the mutation cannot fulfill a condition. + // _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`. + // _1 = 6 + if let Some((lhs, tail)) = finder.mutated_statement(stmt) { + finder.flood_state(lhs, tail, &mut state); + } + } + + trace!("entry_states[{bb:?}] = {state:?}"); + finder.entry_states[bb] = state; } let opportunities = finder.opportunities; @@ -101,7 +126,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { // Verify that we do not thread through a loop header. for to in opportunities.iter() { - assert!(to.chain.iter().all(|&block| !finder.loop_headers.contains(block))); + assert!(to.chain.iter().skip(1).all(|&block| !finder.loop_headers.contains(block))); } OpportunitySet::new(body, opportunities).apply(body); } @@ -126,18 +151,79 @@ struct TOFinder<'a, 'tcx> { body: &'a Body<'tcx>, map: Map<'tcx>, loop_headers: DenseBitSet, + /// This stores the state of each visited block on entry, + /// and the current state of the block being visited. + // Invariant: for each `bb`, each condition in `entry_states[bb]` has a `chain` that + // starts with `bb`. + entry_states: IndexVec>, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec, + /// Pre-computed cost of duplicating each block. + costs: IndexVec>, +} + +/// Singly-linked list to represent chains of blocks. This is cheap to copy, and is converted to +/// plain vecs when creating TOs. +#[derive(Copy, Clone)] +struct BBChain<'a> { + head: BasicBlock, + tail: Option<&'a BBChain<'a>>, +} + +impl std::fmt::Debug for BBChain<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl<'a> BBChain<'a> { + fn single(head: BasicBlock) -> BBChain<'a> { + BBChain { head, tail: None } + } + + fn tail_head(self) -> Option { + let tail = self.tail?; + Some(tail.head) + } + + fn cons(arena: &'a DroplessArena, head: BasicBlock, tail: BBChain<'a>) -> BBChain<'a> { + BBChain { head, tail: Some(arena.alloc(tail)) } + } + + fn to_vec(self) -> Vec { + Vec::from_iter(self.iter()) + } + + fn iter(&self) -> impl Iterator { + return BBChainIter(Some(self)); + + struct BBChainIter<'a, 'h>(Option<&'h BBChain<'a>>); + + impl<'a> Iterator for BBChainIter<'a, '_> { + type Item = BasicBlock; + + fn next(&mut self) -> Option { + let BBChain { head, tail } = self.0?; + self.0 = *tail; + Some(*head) + } + } + } } /// Represent the following statement. If we can prove that the current local is equal/not-equal /// to `value`, jump to `target`. #[derive(Copy, Clone, Debug)] -struct Condition { +struct Condition<'a> { + place: ValueIndex, value: ScalarInt, polarity: Polarity, + /// Chain of basic-blocks to traverse from condition fulfilment to target. + chain: BBChain<'a>, target: BasicBlock, + /// Cumulated cost of duplicating this chain. + cost: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -146,17 +232,22 @@ enum Polarity { Eq, } -impl Condition { - fn matches(&self, value: ScalarInt) -> bool { - (self.value == value) == (self.polarity == Polarity::Eq) +impl<'a> Condition<'a> { + fn matches(&self, place: ValueIndex, value: ScalarInt) -> bool { + self.place == place && (self.value == value) == (self.polarity == Polarity::Eq) + } + + fn into_opportunity(self) -> ThreadingOpportunity { + trace!(?self, "registering"); + ThreadingOpportunity { chain: self.chain.to_vec(), target: self.target } } } -#[derive(Copy, Clone, Debug)] -struct ConditionSet<'a>(&'a [Condition]); +#[derive(Clone, Debug)] +struct ConditionSet<'a>(Vec>); -impl HasBottom for ConditionSet<'_> { - const BOTTOM: Self = ConditionSet(&[]); +impl<'a> HasBottom for ConditionSet<'a> { + const BOTTOM: Self = ConditionSet(Vec::new()); fn is_bottom(&self) -> bool { self.0.is_empty() @@ -164,154 +255,113 @@ impl HasBottom for ConditionSet<'_> { } impl<'a> ConditionSet<'a> { - fn iter(self) -> impl Iterator { - self.0.iter().copied() + fn is_empty(&self) -> bool { + self.0.is_empty() } - fn iter_matches(self, value: ScalarInt) -> impl Iterator { - self.iter().filter(move |c| c.matches(value)) + fn iter(&self) -> impl Iterator> { + self.0.iter().copied() } - fn map( - self, - arena: &'a DroplessArena, - f: impl Fn(Condition) -> Option, - ) -> Option> { - let set = arena.try_alloc_from_iter(self.iter().map(|c| f(c).ok_or(()))).ok()?; - Some(ConditionSet(set)) + fn iter_matches( + &self, + place: ValueIndex, + value: ScalarInt, + ) -> impl Iterator> { + self.iter().filter(move |c| c.matches(place, value)) } -} -impl<'a, 'tcx> TOFinder<'a, 'tcx> { - fn is_empty(&self, state: &State>) -> bool { - state.all_bottom() + fn register_matches( + &self, + place: ValueIndex, + value: ScalarInt, + opportunities: &mut Vec, + ) { + self.iter_matches(place, value).for_each(|cond| opportunities.push(cond.into_opportunity())) } - /// Recursion entry point to find threading opportunities. - #[instrument(level = "trace", skip(self))] - fn start_from_switch(&mut self, bb: BasicBlock) { - let bbdata = &self.body[bb]; - if bbdata.is_cleanup || self.loop_headers.contains(bb) { - return; - } - let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return }; - let Some(discr) = discr.place() else { return }; - debug!(?discr, ?bb); - - let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; - - let Some(discr) = self.map.find(discr.as_ref()) else { return }; - debug!(?discr); - - let cost = CostChecker::new(self.tcx, self.typing_env, None, self.body); - let mut state = State::new_reachable(); - - let conds = if let Some((value, then, else_)) = targets.as_static_if() { - let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; - self.arena.alloc_from_iter([ - Condition { value, polarity: Polarity::Eq, target: then }, - Condition { value, polarity: Polarity::Ne, target: else_ }, - ]) - } else { - self.arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| { - let value = ScalarInt::try_from_uint(value, discr_layout.size)?; - Some(Condition { value, polarity: Polarity::Eq, target }) - })) - }; - let conds = ConditionSet(conds); - state.insert_value_idx(discr, conds, &self.map); - - self.find_opportunity(bb, state, cost, 0) + fn retain(&mut self, f: impl Fn(Condition<'a>) -> bool) { + self.0.retain(|&c| f(c)) } - /// Recursively walk statements backwards from this bb's terminator to find threading - /// opportunities. - #[instrument(level = "trace", skip(self, cost), ret)] - fn find_opportunity( - &mut self, - bb: BasicBlock, - mut state: State>, - mut cost: CostChecker<'_, 'tcx>, - depth: usize, - ) { - // Do not thread through loop headers. - if self.loop_headers.contains(bb) { - return; - } - - debug!(cost = ?cost.cost()); - for (statement_index, stmt) in - self.body.basic_blocks[bb].statements.iter().enumerate().rev() - { - if self.is_empty(&state) { - return; + fn retain_mut(&mut self, f: impl Fn(Condition<'a>) -> Option>) { + self.0.retain_mut(|c| { + if let Some(n) = f(*c) { + *c = n; + true + } else { + false } + }) + } - cost.visit_statement(stmt, Location { block: bb, statement_index }); - if cost.cost() > MAX_COST { - return; - } - - // Attempt to turn the `current_condition` on `lhs` into a condition on another place. - self.process_statement(bb, stmt, &mut state); - - // When a statement mutates a place, assignments to that place that happen - // above the mutation cannot fulfill a condition. - // _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`. - // _1 = 6 - if let Some((lhs, tail)) = self.mutated_statement(stmt) { - state.flood_with_tail_elem(lhs.as_ref(), tail, &self.map, ConditionSet::BOTTOM); - } - } + fn for_each_mut(&mut self, f: impl Fn(&mut Condition<'a>)) { + self.0.iter_mut().for_each(f) + } +} - if self.is_empty(&state) || depth >= MAX_BACKTRACK { - return; - } +impl<'a, 'tcx> TOFinder<'a, 'tcx> { + /// Construct the condition set for `bb` from the terminator, without executing its effect. + #[instrument(level = "trace", skip(self))] + fn populate_from_outgoing_edges(&mut self, bb: BasicBlock) -> ConditionSet<'a> { + let bbdata = &self.body[bb]; - let last_non_rec = self.opportunities.len(); + // This should be the first time we populate `entry_states[bb]`. + debug_assert!(self.entry_states[bb].is_empty()); - let predecessors = &self.body.basic_blocks.predecessors()[bb]; - if let &[pred] = &predecessors[..] - && bb != START_BLOCK - { - let term = self.body.basic_blocks[pred].terminator(); - match term.kind { - TerminatorKind::SwitchInt { ref discr, ref targets } => { - self.process_switch_int(discr, targets, bb, &mut state); - self.find_opportunity(pred, state, cost, depth + 1); - } - _ => self.recurse_through_terminator(pred, || state, &cost, depth), - } - } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] { - for &pred in predecessors { - self.recurse_through_terminator(pred, || state.clone(), &cost, depth); + let state_len = + bbdata.terminator().successors().map(|succ| self.entry_states[succ].0.len()).sum(); + let mut state = Vec::with_capacity(state_len); + for succ in bbdata.terminator().successors() { + // Do not thread through loop headers. + if self.loop_headers.contains(succ) { + continue; } - self.recurse_through_terminator(last_pred, || state, &cost, depth); + state.extend_from_slice(&self.entry_states[succ].0); + } + if state.is_empty() { + return ConditionSet::BOTTOM; } + // Prepend current block to propagated state. + state.retain_mut(|cond| { + let head = cond.chain.head; + cond.chain = BBChain::cons(self.arena, bb, cond.chain); + // Remove conditions for which the duplication cost is too high. + // This is required to keep the size of the `ConditionSet` tractable. + let cost = cond.cost + self.cost(head); + cond.cost = cost; + cost <= MAX_COST + }); + ConditionSet(state) + } - let new_tos = &mut self.opportunities[last_non_rec..]; - debug!(?new_tos); + fn cost(&self, bb: BasicBlock) -> usize { + *self.costs[bb].get_or_init(|| { + let bbdata = &self.body[bb]; + let mut cost = CostChecker::new(self.tcx, self.typing_env, None, self.body); + cost.visit_basic_block_data(bb, bbdata); + cost.cost() + }) + } - // Try to deduplicate threading opportunities. - if new_tos.len() > 1 - && new_tos.len() == predecessors.len() - && predecessors - .iter() - .zip(new_tos.iter()) - .all(|(&pred, to)| to.chain == &[pred] && to.target == new_tos[0].target) - { - // All predecessors have a threading opportunity, and they all point to the same block. - debug!(?new_tos, "dedup"); - let first = &mut new_tos[0]; - *first = ThreadingOpportunity { chain: vec![bb], target: first.target }; - self.opportunities.truncate(last_non_rec + 1); + /// Remove all conditions in the state that alias given place. + fn flood_state( + &self, + place: Place<'tcx>, + extra_elem: Option, + state: &mut ConditionSet<'a>, + ) { + if state.is_empty() { return; } - - for op in self.opportunities[last_non_rec..].iter_mut() { - op.chain.push(bb); + let mut places_to_exclude = FxHashSet::default(); + self.map.for_each_aliasing_place(place.as_ref(), extra_elem, &mut |vi| { + places_to_exclude.insert(vi); + }); + if places_to_exclude.is_empty() { + return; } + state.retain(|c| !places_to_exclude.contains(&c.place)); } /// Extract the mutated place from a statement. @@ -355,34 +405,29 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } } - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self, state))] fn process_immediate( &mut self, bb: BasicBlock, lhs: PlaceIndex, rhs: ImmTy<'tcx>, - state: &mut State>, + state: &mut ConditionSet<'a>, ) { - let register_opportunity = |c: Condition| { - debug!(?bb, ?c.target, "register"); - self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) - }; - - if let Some(conditions) = state.try_get_idx(lhs, &self.map) + if let Some(lhs) = self.map.value(lhs) && let Immediate::Scalar(Scalar::Int(int)) = *rhs { - conditions.iter_matches(int).for_each(register_opportunity); + state.register_matches(lhs, int, &mut self.opportunities) } } /// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self, state))] fn process_constant( &mut self, bb: BasicBlock, lhs: PlaceIndex, constant: OpTy<'tcx>, - state: &mut State>, + state: &mut ConditionSet<'a>, ) { self.map.for_each_projection_value( lhs, @@ -404,27 +449,37 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } }, &mut |place, op| { - if let Some(conditions) = state.try_get_idx(place, &self.map) + if let Some(place) = self.map.value(place) && let Some(imm) = self.ecx.read_immediate_raw(op).discard_err() && let Some(imm) = imm.right() && let Immediate::Scalar(Scalar::Int(int)) = *imm { - conditions.iter_matches(int).for_each(|c: Condition| { - self.opportunities - .push(ThreadingOpportunity { chain: vec![bb], target: c.target }) - }) + state.register_matches(place, int, &mut self.opportunities) } }, ); } - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self, state))] + fn process_copy(&mut self, lhs: PlaceIndex, rhs: PlaceIndex, state: &mut ConditionSet<'a>) { + let mut renames = FxHashMap::default(); + self.map.for_each_value_pair(rhs, lhs, &mut |rhs, lhs| { + renames.insert(lhs, rhs); + }); + state.for_each_mut(|c| { + if let Some(rhs) = renames.get(&c.place) { + c.place = *rhs + } + }); + } + + #[instrument(level = "trace", skip(self, state))] fn process_operand( &mut self, bb: BasicBlock, lhs: PlaceIndex, rhs: &Operand<'tcx>, - state: &mut State>, + state: &mut ConditionSet<'a>, ) { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. @@ -439,27 +494,30 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // Transfer the conditions on the copied rhs. Operand::Move(rhs) | Operand::Copy(rhs) => { let Some(rhs) = self.map.find(rhs.as_ref()) else { return }; - state.insert_place_idx(rhs, lhs, &self.map); + self.process_copy(lhs, rhs, state) } } } - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self, state))] fn process_assign( &mut self, bb: BasicBlock, lhs_place: &Place<'tcx>, - rhs: &Rvalue<'tcx>, - state: &mut State>, + rvalue: &Rvalue<'tcx>, + state: &mut ConditionSet<'a>, ) { let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return }; - match rhs { + match rvalue { Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state), // Transfer the conditions on the copy rhs. - Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state), + Rvalue::CopyForDeref(rhs) => { + let Some(rhs) = self.map.find(rhs.as_ref()) else { return }; + self.process_copy(lhs, rhs, state) + } Rvalue::Discriminant(rhs) => { let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; - state.insert_place_idx(rhs, lhs, &self.map); + self.process_copy(lhs, rhs, state) } // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Rvalue::Aggregate(box kind, operands) => { @@ -491,32 +549,31 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } } // Transfer the conditions on the copy rhs, after inverting the value of the condition. - Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { - let layout = self.ecx.layout_of(place.ty(self.body, self.tcx).ty).unwrap(); - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; - let Some(place) = self.map.find(place.as_ref()) else { return }; - let Some(conds) = conditions.map(self.arena, |mut cond| { - cond.value = self - .ecx - .unary_op(UnOp::Not, &ImmTy::from_scalar_int(cond.value, layout)) - .discard_err()? - .to_scalar_int() - .discard_err()?; - Some(cond) - }) else { - return; - }; - state.insert_value_idx(place, conds, &self.map); + Rvalue::UnaryOp(UnOp::Not, Operand::Move(operand) | Operand::Copy(operand)) => { + let layout = self.ecx.layout_of(operand.ty(self.body, self.tcx).ty).unwrap(); + let Some(lhs) = self.map.value(lhs) else { return }; + let Some(operand) = self.map.find_value(operand.as_ref()) else { return }; + state.retain_mut(|mut c| { + if c.place == lhs { + let value = self + .ecx + .unary_op(UnOp::Not, &ImmTy::from_scalar_int(c.value, layout)) + .discard_err()? + .to_scalar_int() + .discard_err()?; + c.place = operand; + c.value = value; + } + Some(c) + }); } // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. // Create a condition on `rhs ?= B`. Rvalue::BinaryOp( op, - box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value)) - | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)), + box (Operand::Move(operand) | Operand::Copy(operand), Operand::Constant(value)) + | box (Operand::Constant(value), Operand::Move(operand) | Operand::Copy(operand)), ) => { - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; - let Some(place) = self.map.find(place.as_ref()) else { return }; let equals = match op { BinOp::Eq => ScalarInt::TRUE, BinOp::Ne => ScalarInt::FALSE, @@ -529,38 +586,34 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // Avoid handling them, though this could be extended in the future. return; } + let Some(lhs) = self.map.value(lhs) else { return }; + let Some(operand) = self.map.find_value(operand.as_ref()) else { return }; let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env) else { return; }; - let Some(conds) = conditions.map(self.arena, |c| { - Some(Condition { - value, - polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, - ..c - }) - }) else { - return; - }; - state.insert_value_idx(place, conds, &self.map); + state.for_each_mut(|c| { + if c.place == lhs { + let polarity = + if c.matches(lhs, equals) { Polarity::Eq } else { Polarity::Ne }; + c.place = operand; + c.value = value; + c.polarity = polarity; + } + }); } _ => {} } } - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self, state))] fn process_statement( &mut self, bb: BasicBlock, stmt: &Statement<'tcx>, - state: &mut State>, + state: &mut ConditionSet<'a>, ) { - let register_opportunity = |c: Condition| { - debug!(?bb, ?c.target, "register"); - self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) - }; - // Below, `lhs` is the return value of `mutated_statement`, // the place to which `conditions` apply. @@ -584,8 +637,8 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { - let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return }; - conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity) + let Some(place) = self.map.find_value(place.as_ref()) else { return }; + state.register_matches(place, ScalarInt::TRUE, &mut self.opportunities); } StatementKind::Assign(box (lhs_place, rhs)) => { self.process_assign(bb, lhs_place, rhs, state) @@ -594,95 +647,119 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } } - #[instrument(level = "trace", skip(self, state, cost))] - fn recurse_through_terminator( - &mut self, - bb: BasicBlock, - // Pass a closure that may clone the state, as we don't want to do it each time. - state: impl FnOnce() -> State>, - cost: &CostChecker<'_, 'tcx>, - depth: usize, - ) { + /// Execute the terminator for block `bb` into state `entry_states[bb]`. + #[instrument(level = "trace", skip(self, state))] + fn process_terminator(&mut self, bb: BasicBlock, state: &mut ConditionSet<'a>) { let term = self.body.basic_blocks[bb].terminator(); let place_to_flood = match term.kind { - // We come from a target, so those are not possible. - TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::TailCall { .. } - | TerminatorKind::Unreachable - | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"), // Disallowed during optimizations. TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"), // Cannot reason about inline asm. - TerminatorKind::InlineAsm { .. } => return, + TerminatorKind::InlineAsm { .. } => { + *state = ConditionSet::BOTTOM; + return; + } // `SwitchInt` is handled specially. - TerminatorKind::SwitchInt { .. } => return, - // We can recurse, no thing particular to do. - TerminatorKind::Goto { .. } => None, + TerminatorKind::SwitchInt { ref discr, ref targets } => { + return self.process_switch_int(bb, discr, targets, state); + } + // These do not modify memory. + TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::CoroutineDrop + // Assertions can be no-op at codegen time, so treat them as such. + | TerminatorKind::Assert { .. } + | TerminatorKind::Goto { .. } => None, // Flood the overwritten place, and progress through. TerminatorKind::Drop { place: destination, .. } | TerminatorKind::Call { destination, .. } => Some(destination), - // Ignore, as this can be a no-op at codegen time. - TerminatorKind::Assert { .. } => None, + TerminatorKind::TailCall { .. } => Some(RETURN_PLACE.into()), }; - // We can recurse through this terminator. - let mut state = state(); + // This terminator modifies `place_to_flood`, cleanup the associated conditions. if let Some(place_to_flood) = place_to_flood { - state.flood_with(place_to_flood.as_ref(), &self.map, ConditionSet::BOTTOM); + self.flood_state(place_to_flood, None, state); } - self.find_opportunity(bb, state, cost.clone(), depth + 1) } #[instrument(level = "trace", skip(self))] fn process_switch_int( &mut self, + bb: BasicBlock, discr: &Operand<'tcx>, targets: &SwitchTargets, - target_bb: BasicBlock, - state: &mut State>, + state: &mut ConditionSet<'a>, ) { - debug_assert_ne!(target_bb, START_BLOCK); - debug_assert_eq!(self.body.basic_blocks.predecessors()[target_bb].len(), 1); - let Some(discr) = discr.place() else { return }; + let Some(discr_idx) = self.map.find_value(discr.as_ref()) else { return }; + let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { - return; - }; - let Some(conditions) = state.try_get(discr.as_ref(), &self.map) else { return }; + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; - if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) { - let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; - debug_assert_eq!(targets.iter().filter(|&(_, target)| target == target_bb).count(), 1); - - // We are inside `target_bb`. Since we have a single predecessor, we know we passed - // through the `SwitchInt` before arriving here. Therefore, we know that - // `discr == value`. If one condition can be fulfilled by `discr == value`, - // that's an opportunity. - for c in conditions.iter_matches(value) { - debug!(?target_bb, ?c.target, "register"); - self.opportunities.push(ThreadingOpportunity { chain: vec![], target: c.target }); - } - } else if let Some((value, _, else_bb)) = targets.as_static_if() - && target_bb == else_bb - { - let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; + // Attempt to fulfill a condition using an outgoing branch's condition. + // Only support the case where there are no duplicated outgoing edges. + if targets.is_distinct() { + for c in state.iter() { + if c.place != discr_idx { + continue; + } - // We only know that `discr != value`. That's much weaker information than - // the equality we had in the previous arm. All we can conclude is that - // the replacement condition `discr != value` can be threaded, and nothing else. - for c in conditions.iter() { - if c.value == value && c.polarity == Polarity::Ne { - debug!(?target_bb, ?c.target, "register"); - self.opportunities - .push(ThreadingOpportunity { chain: vec![], target: c.target }); + // Invariant from `populate_from_outgoing_edges`. + debug_assert_eq!(c.chain.head, bb); + let Some(target_bb) = c.chain.tail_head() else { continue }; + + let may_thread = if let Some((branch, _)) = + targets.iter().find(|&(_, bb)| bb == target_bb) + && let Some(branch) = ScalarInt::try_from_uint(branch, discr_layout.size) + { + // The switch contains a branch `bb -> target_bb` if `discr == branch`. + c.matches(discr_idx, branch) + } else if target_bb == targets.otherwise() + && let Ok(value) = c.value.try_to_bits(discr_layout.size) + { + // We only know that `discr` is different from all the constants in the switch. + // That's much weaker information than the equality we had in the previous arm. + // All we can conclude is that the replacement condition `discr != value` can + // be threaded, and nothing else. + c.polarity == Polarity::Ne && targets.all_values().contains(&value.into()) + } else { + false + }; + if may_thread { + self.opportunities.push(c.into_opportunity()); } } } + + // Introduce additional conditions of the form `discr ?= value` for each value in targets. + let chain = BBChain::single(bb); + let mk_condition = |value, polarity, target| Condition { + place: discr_idx, + value, + polarity, + chain, + target, + cost: 0, + }; + if let Some((value, then_, else_)) = targets.as_static_if() { + // We have an `if`, generate both `discr == value` and `discr != value`. + let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; + state.0.extend_from_slice(&[ + mk_condition(value, Polarity::Eq, then_), + mk_condition(value, Polarity::Ne, else_), + ]); + } else { + // We have a general switch and we cannot express `discr != value0 && discr != value1`, + // so we only generate equality predicates. + state.0.extend(targets.iter().filter_map(|(value, target)| { + let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + Some(mk_condition(value, Polarity::Eq, target)) + })) + } } } @@ -838,11 +915,12 @@ enum Update { /// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. /// But if the CFG is already irreducible, there is no point in trying much harder. /// is already irreducible. +#[instrument(level = "trace", skip(body), ret)] fn loop_headers(body: &Body<'_>) -> DenseBitSet { let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); let dominators = body.basic_blocks.dominators(); // Only visit reachable blocks. - for (bb, bbdata) in traversal::preorder(body) { + for (bb, bbdata) in traversal::postorder(body) { for succ in bbdata.terminator().successors() { if dominators.dominates(succ, bb) { loop_headers.insert(succ); diff --git a/tests/coverage/conditions.cov-map b/tests/coverage/conditions.cov-map index 29d9604085ede..0047d52c89fab 100644 --- a/tests/coverage/conditions.cov-map +++ b/tests/coverage/conditions.cov-map @@ -1,95 +1,75 @@ Function name: conditions::main -Raw bytes (656): 0x[01, 01, 57, 05, 09, 01, 05, 09, 5d, 09, 27, 5d, 61, 27, 65, 5d, 61, 09, 23, 27, 65, 5d, 61, 01, 03, 03, 0d, 11, 51, 11, 4f, 51, 55, 4f, 59, 51, 55, 11, 4b, 4f, 59, 51, 55, 03, 9f, 01, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 9f, 01, 15, 0d, 11, 19, 45, 19, 97, 01, 45, 49, 97, 01, 4d, 45, 49, 19, 93, 01, 97, 01, 4d, 45, 49, 9f, 01, 9b, 02, 0d, 11, 15, 19, 15, 19, 15, 19, 15, 19, 15, 19, 1d, 21, 15, 19, 9b, 02, 1d, 15, 19, 21, 39, 21, e3, 01, 39, 3d, e3, 01, 41, 39, 3d, 21, df, 01, e3, 01, 41, 39, 3d, 9b, 02, d7, 02, 15, 19, 1d, 21, 9b, 02, d7, 02, 15, 19, 1d, 21, 9b, 02, d7, 02, 15, 19, 1d, 21, 9b, 02, d7, 02, 15, 19, 1d, 21, 9b, 02, d7, 02, 15, 19, 1d, 21, 25, 29, 1d, 21, d7, 02, 25, 1d, 21, 29, 2d, 29, cf, 02, 2d, 31, cf, 02, 35, 2d, 31, 29, cb, 02, cf, 02, 35, 2d, 31, d7, 02, db, 02, 1d, 21, 25, 29, 53, 01, 03, 01, 00, 0a, 01, 01, 09, 00, 16, 01, 00, 19, 00, 1a, 01, 01, 08, 00, 0c, 01, 00, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 03, 09, 00, 0a, 01, 00, 10, 00, 1d, 05, 01, 09, 00, 17, 05, 01, 09, 00, 0a, 06, 01, 0f, 00, 1c, 09, 01, 0c, 00, 19, 0a, 00, 1d, 00, 2a, 0e, 00, 2e, 00, 3c, 23, 00, 3d, 02, 0a, 1e, 02, 09, 00, 0a, 09, 01, 09, 00, 17, 09, 01, 09, 00, 12, 2a, 02, 09, 00, 0f, 03, 03, 09, 00, 16, 03, 00, 19, 00, 1a, 03, 01, 08, 00, 0c, 03, 00, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 02, 08, 00, 15, 0d, 00, 16, 02, 06, 2e, 02, 0f, 00, 1c, 11, 01, 0c, 00, 19, 32, 00, 1d, 00, 2a, 36, 00, 2e, 00, 3c, 4b, 00, 3d, 02, 0a, 46, 02, 09, 00, 0a, 11, 01, 09, 00, 17, 52, 02, 09, 00, 0f, 9f, 01, 03, 08, 00, 0c, 9f, 01, 01, 0d, 00, 1a, 9f, 01, 00, 1d, 00, 1e, 9f, 01, 01, 0c, 00, 10, 9f, 01, 00, 11, 02, 0a, 00, 02, 09, 00, 0a, 9f, 01, 02, 0c, 00, 19, 15, 00, 1a, 02, 0a, 72, 04, 11, 00, 1e, 19, 01, 10, 00, 1d, 7a, 00, 21, 00, 2e, 7e, 00, 32, 00, 40, 93, 01, 00, 41, 02, 0e, 8e, 01, 02, 0d, 00, 0e, 19, 01, 0d, 00, 1b, 9a, 01, 02, 0d, 00, 13, 00, 02, 05, 00, 06, 9b, 02, 02, 09, 00, 16, 9b, 02, 00, 19, 00, 1a, 9b, 02, 01, 08, 00, 0c, 9b, 02, 00, 0d, 02, 06, 00, 02, 05, 00, 06, d7, 02, 02, 09, 00, 0a, 9b, 02, 00, 10, 00, 1d, 1d, 00, 1e, 02, 06, be, 01, 02, 0f, 00, 1c, 21, 01, 0c, 00, 19, c6, 01, 00, 1d, 00, 2a, ca, 01, 00, 2e, 00, 3c, df, 01, 00, 3d, 02, 0a, da, 01, 02, 09, 00, 0a, 21, 01, 09, 00, 17, 96, 02, 02, 0d, 00, 20, 96, 02, 00, 23, 00, 2c, 96, 02, 01, 09, 00, 11, 96, 02, 00, 12, 00, 1b, 96, 02, 01, 09, 00, 0f, db, 02, 03, 09, 00, 0a, d7, 02, 00, 10, 00, 1d, 25, 00, 1e, 02, 06, aa, 02, 02, 0f, 00, 1c, 29, 01, 0c, 00, 19, b2, 02, 00, 1d, 00, 2a, b6, 02, 00, 2e, 00, 3c, cb, 02, 00, 3d, 02, 0a, c6, 02, 02, 09, 00, 0a, 29, 01, 09, 00, 17, d2, 02, 02, 09, 00, 0f, 01, 02, 01, 00, 02] +Raw bytes (595): 0x[01, 01, 43, 05, 09, 01, 05, 09, 51, 09, 13, 51, 55, 01, 03, 03, 0d, 11, 49, 11, 27, 49, 4d, 03, 63, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 0d, 11, 63, 15, 0d, 11, 19, 41, 19, 5b, 41, 45, 63, cb, 01, 0d, 11, 15, 19, 15, 19, 15, 19, 15, 19, 15, 19, 1d, 21, 15, 19, cb, 01, 1d, 15, 19, 21, 39, 21, 93, 01, 39, 3d, cb, 01, 87, 02, 15, 19, 1d, 21, cb, 01, 87, 02, 15, 19, 1d, 21, cb, 01, 87, 02, 15, 19, 1d, 21, cb, 01, 87, 02, 15, 19, 1d, 21, cb, 01, 87, 02, 15, 19, 1d, 21, 25, 29, 1d, 21, 87, 02, 25, 1d, 21, 29, 2d, 29, ff, 01, 2d, 31, ff, 01, 35, 2d, 31, 29, fb, 01, ff, 01, 35, 2d, 31, 87, 02, 8b, 02, 1d, 21, 25, 29, 53, 01, 03, 01, 00, 0a, 01, 01, 09, 00, 16, 01, 00, 19, 00, 1a, 01, 01, 08, 00, 0c, 01, 00, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 03, 09, 00, 0a, 01, 00, 10, 00, 1d, 05, 01, 09, 00, 17, 05, 01, 09, 00, 0a, 06, 01, 0f, 00, 1c, 09, 01, 0c, 00, 19, 0a, 00, 1d, 00, 2a, 0e, 00, 2e, 00, 3c, 09, 00, 3d, 02, 0a, 00, 02, 09, 00, 0a, 09, 01, 09, 00, 17, 09, 01, 09, 00, 12, 16, 02, 09, 00, 0f, 03, 03, 09, 00, 16, 03, 00, 19, 00, 1a, 03, 01, 08, 00, 0c, 03, 00, 0d, 02, 06, 00, 02, 05, 00, 06, 03, 02, 08, 00, 15, 0d, 00, 16, 02, 06, 1a, 02, 0f, 00, 1c, 11, 01, 0c, 00, 19, 1e, 00, 1d, 00, 2a, 22, 00, 2e, 00, 3c, 11, 00, 3d, 02, 0a, 00, 02, 09, 00, 0a, 11, 01, 09, 00, 17, 2a, 02, 09, 00, 0f, 63, 03, 08, 00, 0c, 63, 01, 0d, 00, 1a, 63, 00, 1d, 00, 1e, 63, 01, 0c, 00, 10, 63, 00, 11, 02, 0a, 00, 02, 09, 00, 0a, 63, 02, 0c, 00, 19, 15, 00, 1a, 02, 0a, 4a, 04, 11, 00, 1e, 19, 01, 10, 00, 1d, 52, 00, 21, 00, 2e, 56, 00, 32, 00, 40, 19, 00, 41, 02, 0e, 00, 02, 0d, 00, 0e, 19, 01, 0d, 00, 1b, 5e, 02, 0d, 00, 13, 00, 02, 05, 00, 06, cb, 01, 02, 09, 00, 16, cb, 01, 00, 19, 00, 1a, cb, 01, 01, 08, 00, 0c, cb, 01, 00, 0d, 02, 06, 00, 02, 05, 00, 06, 87, 02, 02, 09, 00, 0a, cb, 01, 00, 10, 00, 1d, 1d, 00, 1e, 02, 06, 82, 01, 02, 0f, 00, 1c, 21, 01, 0c, 00, 19, 8a, 01, 00, 1d, 00, 2a, 8e, 01, 00, 2e, 00, 3c, 21, 00, 3d, 02, 0a, 00, 02, 09, 00, 0a, 21, 01, 09, 00, 17, c6, 01, 02, 0d, 00, 20, c6, 01, 00, 23, 00, 2c, c6, 01, 01, 09, 00, 11, c6, 01, 00, 12, 00, 1b, c6, 01, 01, 09, 00, 0f, 8b, 02, 03, 09, 00, 0a, 87, 02, 00, 10, 00, 1d, 25, 00, 1e, 02, 06, da, 01, 02, 0f, 00, 1c, 29, 01, 0c, 00, 19, e2, 01, 00, 1d, 00, 2a, e6, 01, 00, 2e, 00, 3c, fb, 01, 00, 3d, 02, 0a, f6, 01, 02, 09, 00, 0a, 29, 01, 09, 00, 17, 82, 02, 02, 09, 00, 0f, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => $DIR/conditions.rs -Number of expressions: 87 +Number of expressions: 67 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Counter(0), rhs = Counter(1) -- expression 2 operands: lhs = Counter(2), rhs = Counter(23) -- expression 3 operands: lhs = Counter(2), rhs = Expression(9, Add) -- expression 4 operands: lhs = Counter(23), rhs = Counter(24) -- expression 5 operands: lhs = Expression(9, Add), rhs = Counter(25) -- expression 6 operands: lhs = Counter(23), rhs = Counter(24) -- expression 7 operands: lhs = Counter(2), rhs = Expression(8, Add) -- expression 8 operands: lhs = Expression(9, Add), rhs = Counter(25) -- expression 9 operands: lhs = Counter(23), rhs = Counter(24) -- expression 10 operands: lhs = Counter(0), rhs = Expression(0, Add) -- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(4), rhs = Counter(20) -- expression 13 operands: lhs = Counter(4), rhs = Expression(19, Add) -- expression 14 operands: lhs = Counter(20), rhs = Counter(21) -- expression 15 operands: lhs = Expression(19, Add), rhs = Counter(22) -- expression 16 operands: lhs = Counter(20), rhs = Counter(21) -- expression 17 operands: lhs = Counter(4), rhs = Expression(18, Add) -- expression 18 operands: lhs = Expression(19, Add), rhs = Counter(22) -- expression 19 operands: lhs = Counter(20), rhs = Counter(21) -- expression 20 operands: lhs = Expression(0, Add), rhs = Expression(39, Add) -- expression 21 operands: lhs = Counter(3), rhs = Counter(4) -- expression 22 operands: lhs = Counter(3), rhs = Counter(4) -- expression 23 operands: lhs = Counter(3), rhs = Counter(4) +- expression 2 operands: lhs = Counter(2), rhs = Counter(20) +- expression 3 operands: lhs = Counter(2), rhs = Expression(4, Add) +- expression 4 operands: lhs = Counter(20), rhs = Counter(21) +- expression 5 operands: lhs = Counter(0), rhs = Expression(0, Add) +- expression 6 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 7 operands: lhs = Counter(4), rhs = Counter(18) +- expression 8 operands: lhs = Counter(4), rhs = Expression(9, Add) +- expression 9 operands: lhs = Counter(18), rhs = Counter(19) +- expression 10 operands: lhs = Expression(0, Add), rhs = Expression(24, Add) +- expression 11 operands: lhs = Counter(3), rhs = Counter(4) +- expression 12 operands: lhs = Counter(3), rhs = Counter(4) +- expression 13 operands: lhs = Counter(3), rhs = Counter(4) +- expression 14 operands: lhs = Counter(3), rhs = Counter(4) +- expression 15 operands: lhs = Counter(3), rhs = Counter(4) +- expression 16 operands: lhs = Counter(3), rhs = Counter(4) +- expression 17 operands: lhs = Counter(3), rhs = Counter(4) +- expression 18 operands: lhs = Expression(24, Add), rhs = Counter(5) +- expression 19 operands: lhs = Counter(3), rhs = Counter(4) +- expression 20 operands: lhs = Counter(6), rhs = Counter(16) +- expression 21 operands: lhs = Counter(6), rhs = Expression(22, Add) +- expression 22 operands: lhs = Counter(16), rhs = Counter(17) +- expression 23 operands: lhs = Expression(24, Add), rhs = Expression(50, Add) - expression 24 operands: lhs = Counter(3), rhs = Counter(4) -- expression 25 operands: lhs = Counter(3), rhs = Counter(4) -- expression 26 operands: lhs = Counter(3), rhs = Counter(4) -- expression 27 operands: lhs = Counter(3), rhs = Counter(4) -- expression 28 operands: lhs = Expression(39, Add), rhs = Counter(5) -- expression 29 operands: lhs = Counter(3), rhs = Counter(4) -- expression 30 operands: lhs = Counter(6), rhs = Counter(17) -- expression 31 operands: lhs = Counter(6), rhs = Expression(37, Add) -- expression 32 operands: lhs = Counter(17), rhs = Counter(18) -- expression 33 operands: lhs = Expression(37, Add), rhs = Counter(19) -- expression 34 operands: lhs = Counter(17), rhs = Counter(18) -- expression 35 operands: lhs = Counter(6), rhs = Expression(36, Add) -- expression 36 operands: lhs = Expression(37, Add), rhs = Counter(19) -- expression 37 operands: lhs = Counter(17), rhs = Counter(18) -- expression 38 operands: lhs = Expression(39, Add), rhs = Expression(70, Add) -- expression 39 operands: lhs = Counter(3), rhs = Counter(4) -- expression 40 operands: lhs = Counter(5), rhs = Counter(6) +- expression 25 operands: lhs = Counter(5), rhs = Counter(6) +- expression 26 operands: lhs = Counter(5), rhs = Counter(6) +- expression 27 operands: lhs = Counter(5), rhs = Counter(6) +- expression 28 operands: lhs = Counter(5), rhs = Counter(6) +- expression 29 operands: lhs = Counter(5), rhs = Counter(6) +- expression 30 operands: lhs = Counter(7), rhs = Counter(8) +- expression 31 operands: lhs = Counter(5), rhs = Counter(6) +- expression 32 operands: lhs = Expression(50, Add), rhs = Counter(7) +- expression 33 operands: lhs = Counter(5), rhs = Counter(6) +- expression 34 operands: lhs = Counter(8), rhs = Counter(14) +- expression 35 operands: lhs = Counter(8), rhs = Expression(36, Add) +- expression 36 operands: lhs = Counter(14), rhs = Counter(15) +- expression 37 operands: lhs = Expression(50, Add), rhs = Expression(65, Add) +- expression 38 operands: lhs = Counter(5), rhs = Counter(6) +- expression 39 operands: lhs = Counter(7), rhs = Counter(8) +- expression 40 operands: lhs = Expression(50, Add), rhs = Expression(65, Add) - expression 41 operands: lhs = Counter(5), rhs = Counter(6) -- expression 42 operands: lhs = Counter(5), rhs = Counter(6) -- expression 43 operands: lhs = Counter(5), rhs = Counter(6) +- expression 42 operands: lhs = Counter(7), rhs = Counter(8) +- expression 43 operands: lhs = Expression(50, Add), rhs = Expression(65, Add) - expression 44 operands: lhs = Counter(5), rhs = Counter(6) - expression 45 operands: lhs = Counter(7), rhs = Counter(8) -- expression 46 operands: lhs = Counter(5), rhs = Counter(6) -- expression 47 operands: lhs = Expression(70, Add), rhs = Counter(7) -- expression 48 operands: lhs = Counter(5), rhs = Counter(6) -- expression 49 operands: lhs = Counter(8), rhs = Counter(14) -- expression 50 operands: lhs = Counter(8), rhs = Expression(56, Add) -- expression 51 operands: lhs = Counter(14), rhs = Counter(15) -- expression 52 operands: lhs = Expression(56, Add), rhs = Counter(16) -- expression 53 operands: lhs = Counter(14), rhs = Counter(15) -- expression 54 operands: lhs = Counter(8), rhs = Expression(55, Add) -- expression 55 operands: lhs = Expression(56, Add), rhs = Counter(16) -- expression 56 operands: lhs = Counter(14), rhs = Counter(15) -- expression 57 operands: lhs = Expression(70, Add), rhs = Expression(85, Add) -- expression 58 operands: lhs = Counter(5), rhs = Counter(6) -- expression 59 operands: lhs = Counter(7), rhs = Counter(8) -- expression 60 operands: lhs = Expression(70, Add), rhs = Expression(85, Add) -- expression 61 operands: lhs = Counter(5), rhs = Counter(6) -- expression 62 operands: lhs = Counter(7), rhs = Counter(8) -- expression 63 operands: lhs = Expression(70, Add), rhs = Expression(85, Add) -- expression 64 operands: lhs = Counter(5), rhs = Counter(6) +- expression 46 operands: lhs = Expression(50, Add), rhs = Expression(65, Add) +- expression 47 operands: lhs = Counter(5), rhs = Counter(6) +- expression 48 operands: lhs = Counter(7), rhs = Counter(8) +- expression 49 operands: lhs = Expression(50, Add), rhs = Expression(65, Add) +- expression 50 operands: lhs = Counter(5), rhs = Counter(6) +- expression 51 operands: lhs = Counter(7), rhs = Counter(8) +- expression 52 operands: lhs = Counter(9), rhs = Counter(10) +- expression 53 operands: lhs = Counter(7), rhs = Counter(8) +- expression 54 operands: lhs = Expression(65, Add), rhs = Counter(9) +- expression 55 operands: lhs = Counter(7), rhs = Counter(8) +- expression 56 operands: lhs = Counter(10), rhs = Counter(11) +- expression 57 operands: lhs = Counter(10), rhs = Expression(63, Add) +- expression 58 operands: lhs = Counter(11), rhs = Counter(12) +- expression 59 operands: lhs = Expression(63, Add), rhs = Counter(13) +- expression 60 operands: lhs = Counter(11), rhs = Counter(12) +- expression 61 operands: lhs = Counter(10), rhs = Expression(62, Add) +- expression 62 operands: lhs = Expression(63, Add), rhs = Counter(13) +- expression 63 operands: lhs = Counter(11), rhs = Counter(12) +- expression 64 operands: lhs = Expression(65, Add), rhs = Expression(66, Add) - expression 65 operands: lhs = Counter(7), rhs = Counter(8) -- expression 66 operands: lhs = Expression(70, Add), rhs = Expression(85, Add) -- expression 67 operands: lhs = Counter(5), rhs = Counter(6) -- expression 68 operands: lhs = Counter(7), rhs = Counter(8) -- expression 69 operands: lhs = Expression(70, Add), rhs = Expression(85, Add) -- expression 70 operands: lhs = Counter(5), rhs = Counter(6) -- expression 71 operands: lhs = Counter(7), rhs = Counter(8) -- expression 72 operands: lhs = Counter(9), rhs = Counter(10) -- expression 73 operands: lhs = Counter(7), rhs = Counter(8) -- expression 74 operands: lhs = Expression(85, Add), rhs = Counter(9) -- expression 75 operands: lhs = Counter(7), rhs = Counter(8) -- expression 76 operands: lhs = Counter(10), rhs = Counter(11) -- expression 77 operands: lhs = Counter(10), rhs = Expression(83, Add) -- expression 78 operands: lhs = Counter(11), rhs = Counter(12) -- expression 79 operands: lhs = Expression(83, Add), rhs = Counter(13) -- expression 80 operands: lhs = Counter(11), rhs = Counter(12) -- expression 81 operands: lhs = Counter(10), rhs = Expression(82, Add) -- expression 82 operands: lhs = Expression(83, Add), rhs = Counter(13) -- expression 83 operands: lhs = Counter(11), rhs = Counter(12) -- expression 84 operands: lhs = Expression(85, Add), rhs = Expression(86, Add) -- expression 85 operands: lhs = Counter(7), rhs = Counter(8) -- expression 86 operands: lhs = Counter(9), rhs = Counter(10) +- expression 66 operands: lhs = Counter(9), rhs = Counter(10) Number of file 0 mappings: 83 - Code(Counter(0)) at (prev + 3, 1) to (start + 0, 10) - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 22) @@ -106,16 +86,14 @@ Number of file 0 mappings: 83 = (c0 - c1) - Code(Counter(2)) at (prev + 1, 12) to (start + 0, 25) - Code(Expression(2, Sub)) at (prev + 0, 29) to (start + 0, 42) - = (c2 - c23) + = (c2 - c20) - Code(Expression(3, Sub)) at (prev + 0, 46) to (start + 0, 60) - = (c2 - (c23 + c24)) -- Code(Expression(8, Add)) at (prev + 0, 61) to (start + 2, 10) - = ((c23 + c24) + c25) -- Code(Expression(7, Sub)) at (prev + 2, 9) to (start + 0, 10) - = (c2 - ((c23 + c24) + c25)) + = (c2 - (c20 + c21)) +- Code(Counter(2)) at (prev + 0, 61) to (start + 2, 10) +- Code(Zero) at (prev + 2, 9) to (start + 0, 10) - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 23) - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 18) -- Code(Expression(10, Sub)) at (prev + 2, 9) to (start + 0, 15) +- Code(Expression(5, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - (c1 + c2)) - Code(Expression(0, Add)) at (prev + 3, 9) to (start + 0, 22) = (c1 + c2) @@ -129,103 +107,97 @@ Number of file 0 mappings: 83 - Code(Expression(0, Add)) at (prev + 2, 8) to (start + 0, 21) = (c1 + c2) - Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6) -- Code(Expression(11, Sub)) at (prev + 2, 15) to (start + 0, 28) +- Code(Expression(6, Sub)) at (prev + 2, 15) to (start + 0, 28) = ((c1 + c2) - c3) - Code(Counter(4)) at (prev + 1, 12) to (start + 0, 25) -- Code(Expression(12, Sub)) at (prev + 0, 29) to (start + 0, 42) - = (c4 - c20) -- Code(Expression(13, Sub)) at (prev + 0, 46) to (start + 0, 60) - = (c4 - (c20 + c21)) -- Code(Expression(18, Add)) at (prev + 0, 61) to (start + 2, 10) - = ((c20 + c21) + c22) -- Code(Expression(17, Sub)) at (prev + 2, 9) to (start + 0, 10) - = (c4 - ((c20 + c21) + c22)) +- Code(Expression(7, Sub)) at (prev + 0, 29) to (start + 0, 42) + = (c4 - c18) +- Code(Expression(8, Sub)) at (prev + 0, 46) to (start + 0, 60) + = (c4 - (c18 + c19)) +- Code(Counter(4)) at (prev + 0, 61) to (start + 2, 10) +- Code(Zero) at (prev + 2, 9) to (start + 0, 10) - Code(Counter(4)) at (prev + 1, 9) to (start + 0, 23) -- Code(Expression(20, Sub)) at (prev + 2, 9) to (start + 0, 15) +- Code(Expression(10, Sub)) at (prev + 2, 9) to (start + 0, 15) = ((c1 + c2) - (c3 + c4)) -- Code(Expression(39, Add)) at (prev + 3, 8) to (start + 0, 12) +- Code(Expression(24, Add)) at (prev + 3, 8) to (start + 0, 12) = (c3 + c4) -- Code(Expression(39, Add)) at (prev + 1, 13) to (start + 0, 26) +- Code(Expression(24, Add)) at (prev + 1, 13) to (start + 0, 26) = (c3 + c4) -- Code(Expression(39, Add)) at (prev + 0, 29) to (start + 0, 30) +- Code(Expression(24, Add)) at (prev + 0, 29) to (start + 0, 30) = (c3 + c4) -- Code(Expression(39, Add)) at (prev + 1, 12) to (start + 0, 16) +- Code(Expression(24, Add)) at (prev + 1, 12) to (start + 0, 16) = (c3 + c4) -- Code(Expression(39, Add)) at (prev + 0, 17) to (start + 2, 10) +- Code(Expression(24, Add)) at (prev + 0, 17) to (start + 2, 10) = (c3 + c4) - Code(Zero) at (prev + 2, 9) to (start + 0, 10) -- Code(Expression(39, Add)) at (prev + 2, 12) to (start + 0, 25) +- Code(Expression(24, Add)) at (prev + 2, 12) to (start + 0, 25) = (c3 + c4) - Code(Counter(5)) at (prev + 0, 26) to (start + 2, 10) -- Code(Expression(28, Sub)) at (prev + 4, 17) to (start + 0, 30) +- Code(Expression(18, Sub)) at (prev + 4, 17) to (start + 0, 30) = ((c3 + c4) - c5) - Code(Counter(6)) at (prev + 1, 16) to (start + 0, 29) -- Code(Expression(30, Sub)) at (prev + 0, 33) to (start + 0, 46) - = (c6 - c17) -- Code(Expression(31, Sub)) at (prev + 0, 50) to (start + 0, 64) - = (c6 - (c17 + c18)) -- Code(Expression(36, Add)) at (prev + 0, 65) to (start + 2, 14) - = ((c17 + c18) + c19) -- Code(Expression(35, Sub)) at (prev + 2, 13) to (start + 0, 14) - = (c6 - ((c17 + c18) + c19)) +- Code(Expression(20, Sub)) at (prev + 0, 33) to (start + 0, 46) + = (c6 - c16) +- Code(Expression(21, Sub)) at (prev + 0, 50) to (start + 0, 64) + = (c6 - (c16 + c17)) +- Code(Counter(6)) at (prev + 0, 65) to (start + 2, 14) +- Code(Zero) at (prev + 2, 13) to (start + 0, 14) - Code(Counter(6)) at (prev + 1, 13) to (start + 0, 27) -- Code(Expression(38, Sub)) at (prev + 2, 13) to (start + 0, 19) +- Code(Expression(23, Sub)) at (prev + 2, 13) to (start + 0, 19) = ((c3 + c4) - (c5 + c6)) - Code(Zero) at (prev + 2, 5) to (start + 0, 6) -- Code(Expression(70, Add)) at (prev + 2, 9) to (start + 0, 22) +- Code(Expression(50, Add)) at (prev + 2, 9) to (start + 0, 22) = (c5 + c6) -- Code(Expression(70, Add)) at (prev + 0, 25) to (start + 0, 26) +- Code(Expression(50, Add)) at (prev + 0, 25) to (start + 0, 26) = (c5 + c6) -- Code(Expression(70, Add)) at (prev + 1, 8) to (start + 0, 12) +- Code(Expression(50, Add)) at (prev + 1, 8) to (start + 0, 12) = (c5 + c6) -- Code(Expression(70, Add)) at (prev + 0, 13) to (start + 2, 6) +- Code(Expression(50, Add)) at (prev + 0, 13) to (start + 2, 6) = (c5 + c6) - Code(Zero) at (prev + 2, 5) to (start + 0, 6) -- Code(Expression(85, Add)) at (prev + 2, 9) to (start + 0, 10) +- Code(Expression(65, Add)) at (prev + 2, 9) to (start + 0, 10) = (c7 + c8) -- Code(Expression(70, Add)) at (prev + 0, 16) to (start + 0, 29) +- Code(Expression(50, Add)) at (prev + 0, 16) to (start + 0, 29) = (c5 + c6) - Code(Counter(7)) at (prev + 0, 30) to (start + 2, 6) -- Code(Expression(47, Sub)) at (prev + 2, 15) to (start + 0, 28) +- Code(Expression(32, Sub)) at (prev + 2, 15) to (start + 0, 28) = ((c5 + c6) - c7) - Code(Counter(8)) at (prev + 1, 12) to (start + 0, 25) -- Code(Expression(49, Sub)) at (prev + 0, 29) to (start + 0, 42) +- Code(Expression(34, Sub)) at (prev + 0, 29) to (start + 0, 42) = (c8 - c14) -- Code(Expression(50, Sub)) at (prev + 0, 46) to (start + 0, 60) +- Code(Expression(35, Sub)) at (prev + 0, 46) to (start + 0, 60) = (c8 - (c14 + c15)) -- Code(Expression(55, Add)) at (prev + 0, 61) to (start + 2, 10) - = ((c14 + c15) + c16) -- Code(Expression(54, Sub)) at (prev + 2, 9) to (start + 0, 10) - = (c8 - ((c14 + c15) + c16)) +- Code(Counter(8)) at (prev + 0, 61) to (start + 2, 10) +- Code(Zero) at (prev + 2, 9) to (start + 0, 10) - Code(Counter(8)) at (prev + 1, 9) to (start + 0, 23) -- Code(Expression(69, Sub)) at (prev + 2, 13) to (start + 0, 32) +- Code(Expression(49, Sub)) at (prev + 2, 13) to (start + 0, 32) = ((c5 + c6) - (c7 + c8)) -- Code(Expression(69, Sub)) at (prev + 0, 35) to (start + 0, 44) +- Code(Expression(49, Sub)) at (prev + 0, 35) to (start + 0, 44) = ((c5 + c6) - (c7 + c8)) -- Code(Expression(69, Sub)) at (prev + 1, 9) to (start + 0, 17) +- Code(Expression(49, Sub)) at (prev + 1, 9) to (start + 0, 17) = ((c5 + c6) - (c7 + c8)) -- Code(Expression(69, Sub)) at (prev + 0, 18) to (start + 0, 27) +- Code(Expression(49, Sub)) at (prev + 0, 18) to (start + 0, 27) = ((c5 + c6) - (c7 + c8)) -- Code(Expression(69, Sub)) at (prev + 1, 9) to (start + 0, 15) +- Code(Expression(49, Sub)) at (prev + 1, 9) to (start + 0, 15) = ((c5 + c6) - (c7 + c8)) -- Code(Expression(86, Add)) at (prev + 3, 9) to (start + 0, 10) +- Code(Expression(66, Add)) at (prev + 3, 9) to (start + 0, 10) = (c9 + c10) -- Code(Expression(85, Add)) at (prev + 0, 16) to (start + 0, 29) +- Code(Expression(65, Add)) at (prev + 0, 16) to (start + 0, 29) = (c7 + c8) - Code(Counter(9)) at (prev + 0, 30) to (start + 2, 6) -- Code(Expression(74, Sub)) at (prev + 2, 15) to (start + 0, 28) +- Code(Expression(54, Sub)) at (prev + 2, 15) to (start + 0, 28) = ((c7 + c8) - c9) - Code(Counter(10)) at (prev + 1, 12) to (start + 0, 25) -- Code(Expression(76, Sub)) at (prev + 0, 29) to (start + 0, 42) +- Code(Expression(56, Sub)) at (prev + 0, 29) to (start + 0, 42) = (c10 - c11) -- Code(Expression(77, Sub)) at (prev + 0, 46) to (start + 0, 60) +- Code(Expression(57, Sub)) at (prev + 0, 46) to (start + 0, 60) = (c10 - (c11 + c12)) -- Code(Expression(82, Add)) at (prev + 0, 61) to (start + 2, 10) +- Code(Expression(62, Add)) at (prev + 0, 61) to (start + 2, 10) = ((c11 + c12) + c13) -- Code(Expression(81, Sub)) at (prev + 2, 9) to (start + 0, 10) +- Code(Expression(61, Sub)) at (prev + 2, 9) to (start + 0, 10) = (c10 - ((c11 + c12) + c13)) - Code(Counter(10)) at (prev + 1, 9) to (start + 0, 23) -- Code(Expression(84, Sub)) at (prev + 2, 9) to (start + 0, 15) +- Code(Expression(64, Sub)) at (prev + 2, 9) to (start + 0, 15) = ((c7 + c8) - (c9 + c10)) - Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) Highest counter ID seen: c10 diff --git a/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-abort.diff index a7551c3fb5b79..89d04c557f125 100644 --- a/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-abort.diff @@ -1,51 +1,50 @@ - // MIR for `aggregate` before JumpThreading + // MIR for `aggregate` after JumpThreading - fn aggregate(_1: u8) -> u8 { - debug x => _1; + fn aggregate() -> u8 { let mut _0: u8; + let _1: u8; let _2: u8; - let _3: u8; - let mut _4: (u8, u8); - let mut _5: bool; - let mut _6: u8; + let mut _3: (u8, u8); + let mut _4: bool; + let mut _5: u8; scope 1 { - debug a => _2; - debug b => _3; + debug a => _1; + debug b => _2; } bb0: { - StorageLive(_4); - _4 = const aggregate::FOO; - StorageLive(_2); - _2 = copy (_4.0: u8); StorageLive(_3); - _3 = copy (_4.1: u8); - StorageDead(_4); + _3 = const aggregate::FOO; + StorageLive(_1); + _1 = copy (_3.0: u8); + StorageLive(_2); + _2 = copy (_3.1: u8); + StorageDead(_3); + StorageLive(_4); StorageLive(_5); - StorageLive(_6); - _6 = copy _2; - _5 = Eq(move _6, const 7_u8); -- switchInt(move _5) -> [0: bb2, otherwise: bb1]; + _5 = copy _1; + _4 = Eq(move _5, const 7_u8); +- switchInt(move _4) -> [0: bb2, otherwise: bb1]; + goto -> bb2; } bb1: { - StorageDead(_6); - _0 = copy _3; + StorageDead(_5); + _0 = copy _2; goto -> bb3; } bb2: { - StorageDead(_6); - _0 = copy _2; + StorageDead(_5); + _0 = copy _1; goto -> bb3; } bb3: { - StorageDead(_5); - StorageDead(_3); + StorageDead(_4); StorageDead(_2); + StorageDead(_1); return; } } diff --git a/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-unwind.diff index a7551c3fb5b79..89d04c557f125 100644 --- a/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.aggregate.JumpThreading.panic-unwind.diff @@ -1,51 +1,50 @@ - // MIR for `aggregate` before JumpThreading + // MIR for `aggregate` after JumpThreading - fn aggregate(_1: u8) -> u8 { - debug x => _1; + fn aggregate() -> u8 { let mut _0: u8; + let _1: u8; let _2: u8; - let _3: u8; - let mut _4: (u8, u8); - let mut _5: bool; - let mut _6: u8; + let mut _3: (u8, u8); + let mut _4: bool; + let mut _5: u8; scope 1 { - debug a => _2; - debug b => _3; + debug a => _1; + debug b => _2; } bb0: { - StorageLive(_4); - _4 = const aggregate::FOO; - StorageLive(_2); - _2 = copy (_4.0: u8); StorageLive(_3); - _3 = copy (_4.1: u8); - StorageDead(_4); + _3 = const aggregate::FOO; + StorageLive(_1); + _1 = copy (_3.0: u8); + StorageLive(_2); + _2 = copy (_3.1: u8); + StorageDead(_3); + StorageLive(_4); StorageLive(_5); - StorageLive(_6); - _6 = copy _2; - _5 = Eq(move _6, const 7_u8); -- switchInt(move _5) -> [0: bb2, otherwise: bb1]; + _5 = copy _1; + _4 = Eq(move _5, const 7_u8); +- switchInt(move _4) -> [0: bb2, otherwise: bb1]; + goto -> bb2; } bb1: { - StorageDead(_6); - _0 = copy _3; + StorageDead(_5); + _0 = copy _2; goto -> bb3; } bb2: { - StorageDead(_6); - _0 = copy _2; + StorageDead(_5); + _0 = copy _1; goto -> bb3; } bb3: { - StorageDead(_5); - StorageDead(_3); + StorageDead(_4); StorageDead(_2); + StorageDead(_1); return; } } diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff index d17f2752f58ec..62631b9043f8c 100644 --- a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-abort.diff @@ -9,7 +9,8 @@ bb0: { _2 = const true; _3 = const true; - switchInt(copy _1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; +- switchInt(copy _1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; ++ switchInt(copy _1) -> [0: bb14, 1: bb14, 2: bb1, otherwise: bb2]; } bb1: { @@ -28,11 +29,13 @@ } bb4: { - switchInt(copy _3) -> [0: bb5, otherwise: bb7]; +- switchInt(copy _3) -> [0: bb5, otherwise: bb7]; ++ switchInt(copy _3) -> [0: bb10, otherwise: bb7]; } bb5: { - switchInt(copy _2) -> [0: bb6, otherwise: bb8]; +- switchInt(copy _2) -> [0: bb6, otherwise: bb8]; ++ goto -> bb8; } bb6: { @@ -41,11 +44,11 @@ bb7: { - goto -> bb5; -+ goto -> bb10; ++ goto -> bb11; } bb8: { -+ goto -> bb6; + goto -> bb6; + } + + bb9: { @@ -53,7 +56,29 @@ + } + + bb10: { - goto -> bb6; ++ goto -> bb6; ++ } ++ ++ bb11: { ++ goto -> bb6; ++ } ++ ++ bb12: { ++ _2 = const false; ++ goto -> bb13; ++ } ++ ++ bb13: { ++ goto -> bb7; ++ } ++ ++ bb14: { ++ _2 = const false; ++ goto -> bb15; ++ } ++ ++ bb15: { ++ goto -> bb7; } } diff --git a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff index d17f2752f58ec..62631b9043f8c 100644 --- a/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.disappearing_bb.JumpThreading.panic-unwind.diff @@ -9,7 +9,8 @@ bb0: { _2 = const true; _3 = const true; - switchInt(copy _1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; +- switchInt(copy _1) -> [0: bb3, 1: bb3, 2: bb1, otherwise: bb2]; ++ switchInt(copy _1) -> [0: bb14, 1: bb14, 2: bb1, otherwise: bb2]; } bb1: { @@ -28,11 +29,13 @@ } bb4: { - switchInt(copy _3) -> [0: bb5, otherwise: bb7]; +- switchInt(copy _3) -> [0: bb5, otherwise: bb7]; ++ switchInt(copy _3) -> [0: bb10, otherwise: bb7]; } bb5: { - switchInt(copy _2) -> [0: bb6, otherwise: bb8]; +- switchInt(copy _2) -> [0: bb6, otherwise: bb8]; ++ goto -> bb8; } bb6: { @@ -41,11 +44,11 @@ bb7: { - goto -> bb5; -+ goto -> bb10; ++ goto -> bb11; } bb8: { -+ goto -> bb6; + goto -> bb6; + } + + bb9: { @@ -53,7 +56,29 @@ + } + + bb10: { - goto -> bb6; ++ goto -> bb6; ++ } ++ ++ bb11: { ++ goto -> bb6; ++ } ++ ++ bb12: { ++ _2 = const false; ++ goto -> bb13; ++ } ++ ++ bb13: { ++ goto -> bb7; ++ } ++ ++ bb14: { ++ _2 = const false; ++ goto -> bb15; ++ } ++ ++ bb15: { ++ goto -> bb7; } } diff --git a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff index 083a6e7487a05..0f0f24bbaa1f9 100644 --- a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-abort.diff @@ -18,7 +18,8 @@ bb2: { _2 = const 5_u8; - goto -> bb3; +- goto -> bb3; ++ goto -> bb7; } bb3: { @@ -40,6 +41,16 @@ bb6: { _0 = const 9_u8; return; ++ } ++ ++ bb7: { ++ _3 = const 13_i32; ++ goto -> bb8; ++ } ++ ++ bb8: { ++ _4 = const 15_i32; ++ goto -> bb5; } } diff --git a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff index 083a6e7487a05..0f0f24bbaa1f9 100644 --- a/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.duplicate_chain.JumpThreading.panic-unwind.diff @@ -18,7 +18,8 @@ bb2: { _2 = const 5_u8; - goto -> bb3; +- goto -> bb3; ++ goto -> bb7; } bb3: { @@ -40,6 +41,16 @@ bb6: { _0 = const 9_u8; return; ++ } ++ ++ bb7: { ++ _3 = const 13_i32; ++ goto -> bb8; ++ } ++ ++ bb8: { ++ _4 = const 15_i32; ++ goto -> bb5; } } diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 009e1060700c1..73d340f572051 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -244,7 +244,7 @@ fn duplicate_chain(x: bool) -> u8 { bb2 = { // CHECK: bb2: { // CHECK: [[a]] = const 5_u8; - // CHECK: goto -> bb3; + // CHECK: goto -> bb7; a = 5; Goto(bb3) } @@ -277,6 +277,12 @@ fn duplicate_chain(x: bool) -> u8 { RET = 9; Return() } + // CHECK: bb7: { + // CHECK-NEXT: {{_.*}} = const 13_i32; + // CHECK-NEXT: goto -> bb8; + // CHECK: bb8: { + // CHECK-NEXT: {{_.*}} = const 15_i32; + // CHECK-NEXT: goto -> bb5; } } @@ -422,7 +428,7 @@ fn disappearing_bb(x: u8) -> u8 { } bb3 = { // CHECK: bb3: { - // CHECK: goto -> bb10; + // CHECK: goto -> bb4; a = false; Goto(bb4) } @@ -442,16 +448,36 @@ fn disappearing_bb(x: u8) -> u8 { Goto(bb6) } // CHECK: bb9: { - // CHECK: goto -> bb5; + // CHECK-NEXT: goto -> bb5; // CHECK: bb10: { - // CHECK: goto -> bb6; + // CHECK-NEXT: goto -> bb6; + // CHECK: bb11: { + // CHECK-NEXT: goto -> bb6; + // CHECK: bb12: { + // CHECK-NEXT: _2 = const false; + // CHECK-NEXT: goto -> bb13; + // CHECK: bb13: { + // CHECK-NEXT: goto -> bb7; + // CHECK: bb14: { + // CHECK-NEXT: _2 = const false; + // CHECK-NEXT: goto -> bb15; + // CHECK: bb15: { + // CHECK-NEXT: goto -> bb7; } } /// Verify that we can thread jumps when we assign from an aggregate constant. -fn aggregate(x: u8) -> u8 { +fn aggregate() -> u8 { // CHECK-LABEL: fn aggregate( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug b => [[b:_.*]]; // CHECK-NOT: switchInt( + // CHECK: [[a2:_.*]] = copy [[a]]; + // CHECK: {{_.*}} = Eq(move [[a2]], const 7_u8); + // CHECK-NEXT: goto -> [[bb:bb.*]]; + // CHECK: [[bb]]: { + // CHECK-NOT: } + // CHECK: _0 = copy [[a]]; const FOO: (u8, u8) = (5, 13); @@ -508,7 +534,16 @@ 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: debug a => [[a:_.*]]; + // CHECK: debug b => [[b:_.*]]; + // CHECK: debug c => [[c:_.*]]; // CHECK-NOT: switchInt( + // CHECK: [[c2:_.*]] = copy [[c]]; + // CHECK: {{_.*}} = Eq(move [[c2]], const 2_u32); + // CHECK-NEXT: goto -> [[bb:bb.*]]; + // CHECK: [[bb]]: { + // CHECK-NOT: } + // CHECK: _0 = const 13_u32; const Foo: (u32, u32) = (5, 3); @@ -532,6 +567,14 @@ fn floats() -> u32 { pub fn bitwise_not() -> i32 { // CHECK-LABEL: fn bitwise_not( + // CHECK: debug a => [[a:_.*]]; + // CHECK: [[a2:_.*]] = copy [[a]]; + // CHECK: [[not:_.*]] = Not(move [[a2]]); + // CHECK: {{_.*}} = Eq(move [[not]], const 0_i32); + // CHECK-NEXT: goto -> [[bb:bb.*]]; + // CHECK: [[bb]]: { + // CHECK-NOT: } + // CHECK: _0 = const 0_i32; // Test for #131195, which was optimizing `!a == b` into `a != b`. let a = 1; @@ -540,6 +583,14 @@ pub fn bitwise_not() -> i32 { pub fn logical_not() -> i32 { // CHECK-LABEL: fn logical_not( + // CHECK: debug a => [[a:_.*]]; + // CHECK: [[a2:_.*]] = copy [[a]]; + // CHECK: [[not:_.*]] = Not(move [[a2]]); + // CHECK: {{_.*}} = Eq(move [[not]], const true); + // CHECK-NEXT: goto -> [[bb:bb.*]]; + // CHECK: [[bb]]: { + // CHECK-NOT: } + // CHECK: _0 = const 1_i32; let a = false; if !a == true { 1 } else { 0 } @@ -557,7 +608,7 @@ fn main() { mutable_ref(); renumbered_bb(true); disappearing_bb(7); - aggregate(7); + aggregate(); assume(7, false); floats(); bitwise_not();