diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index daf304c1bf9df..ac94ee5c8104e 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -1,3 +1,4 @@ +use std::assert_matches::debug_assert_matches; use std::fmt::{Debug, Formatter}; use std::ops::Range; @@ -350,41 +351,56 @@ pub struct Map<'tcx> { projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>, places: IndexVec>, value_count: usize, + mode: PlaceCollectionMode, // The Range corresponds to a slice into `inner_values_buffer`. inner_values: IndexVec>, inner_values_buffer: Vec, } +#[derive(Copy, Clone, Debug)] +pub enum PlaceCollectionMode { + Full { value_limit: Option }, + OnDemand, +} + impl<'tcx> Map<'tcx> { /// 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 /// chosen is an implementation detail and may not be relied upon (other than that their type /// are scalars). - pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option) -> Self { + #[tracing::instrument(level = "trace", skip(tcx, body))] + pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, mode: PlaceCollectionMode) -> Self { + tracing::trace!(def_id=?body.source.def_id()); + let capacity = 4 * body.local_decls.len(); let mut map = Self { locals: IndexVec::from_elem(None, &body.local_decls), projections: FxHashMap::default(), - places: IndexVec::new(), + places: IndexVec::with_capacity(capacity), value_count: 0, + mode, inner_values: IndexVec::new(), inner_values_buffer: Vec::new(), }; - let exclude = excluded_locals(body); - map.register(tcx, body, exclude, value_limit); + map.register_locals(tcx, body); + match mode { + PlaceCollectionMode::Full { value_limit } => { + map.collect_places(tcx, body); + map.propagate_assignments(tcx, body); + map.create_values(tcx, body, value_limit); + map.trim_useless_places(); + } + PlaceCollectionMode::OnDemand => {} + } debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len()); map } /// Register all non-excluded places that have scalar layout. #[tracing::instrument(level = "trace", skip(self, tcx, body))] - fn register( - &mut self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - exclude: DenseBitSet, - value_limit: Option, - ) { + fn register_locals(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let exclude = excluded_locals(body); + // Start by constructing the places for each bare local. for (local, decl) in body.local_decls.iter_enumerated() { if exclude.contains(local) { @@ -399,23 +415,85 @@ impl<'tcx> Map<'tcx> { let place = self.places.push(PlaceInfo::new(decl.ty, None)); self.locals[local] = Some(place); } + } - // Collect syntactic places and assignments between them. - let mut collector = - PlaceCollector { tcx, body, map: self, assignments: Default::default() }; + /// Collect syntactic places from body, and create `PlaceIndex` for them. + #[tracing::instrument(level = "trace", skip(self, tcx, body))] + fn collect_places(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut collector = PlaceCollector { tcx, body, map: self }; 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 + } + + /// 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. + #[tracing::instrument(level = "trace", skip(self, tcx, body))] + fn propagate_assignments(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + // Collect syntactic places and assignments between them. + let mut assignments = FxIndexSet::default(); + + for bbdata in body.basic_blocks.iter() { + for stmt in bbdata.statements.iter() { + let Some((lhs, rhs)) = stmt.kind.as_assign() else { continue }; + match rhs { + Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs)) + | Rvalue::CopyForDeref(rhs) => { + let Some(lhs) = self.register_place_and_discr(tcx, body, *lhs) else { + continue; + }; + let Some(rhs) = self.register_place_and_discr(tcx, body, *rhs) else { + continue; + }; + assignments.insert((lhs, rhs)); + } + Rvalue::Aggregate(kind, fields) => { + let Some(mut lhs) = self.register_place_and_discr(tcx, body, *lhs) else { + continue; + }; + match **kind { + // Do not propagate unions. + AggregateKind::Adt(_, _, _, _, Some(_)) => continue, + AggregateKind::Adt(_, variant, _, _, None) => { + let ty = self.places[lhs].ty; + if ty.is_enum() { + lhs = self.register_place_index( + 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_and_discr(tcx, body, rhs) + { + let lhs = self.register_place_index( + self.places[rhs].ty, + lhs, + TrackElem::Field(index), + ); + assignments.insert((lhs, rhs)); + } + } + } + _ => {} + } + } + } + + // This is a 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() { @@ -428,8 +506,11 @@ impl<'tcx> Map<'tcx> { 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")); + let rhs_child = self.register_place_index( + ty, + rhs, + proj_elem.expect("child is not a projection"), + ); assignments.insert((lhs_child, rhs_child)); child = next_sibling; } @@ -438,16 +519,22 @@ impl<'tcx> Map<'tcx> { 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")); + let lhs_child = self.register_place_index( + 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. + /// Create values for places whose type have scalar layout. + #[tracing::instrument(level = "trace", skip(self, tcx, body))] + fn create_values(&mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option) { + debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. }); let typing_env = body.typing_env(tcx); for place_info in self.places.iter_mut() { // The user requires a bound on the number of created values. @@ -481,8 +568,12 @@ impl<'tcx> Map<'tcx> { self.cache_preorder_invoke(place); } } + } - // Trim useless places. + /// Trim useless places. + #[tracing::instrument(level = "trace", skip(self))] + fn trim_useless_places(&mut self) { + debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. }); for opt_place in self.locals.iter_mut() { if let Some(place) = *opt_place && self.inner_values[place].is_empty() @@ -495,7 +586,12 @@ impl<'tcx> Map<'tcx> { } #[tracing::instrument(level = "trace", skip(self), ret)] - fn register_place(&mut self, ty: Ty<'tcx>, base: PlaceIndex, elem: TrackElem) -> PlaceIndex { + pub fn register_place_index( + &mut self, + ty: Ty<'tcx>, + base: PlaceIndex, + elem: TrackElem, + ) -> PlaceIndex { *self.projections.entry((base, elem)).or_insert_with(|| { let next = self.places.push(PlaceInfo::new(ty, Some(elem))); self.places[next].next_sibling = self.places[base].first_child; @@ -504,9 +600,124 @@ impl<'tcx> Map<'tcx> { }) } + #[tracing::instrument(level = "trace", skip(self, tcx, body), ret)] + pub fn register_place( + &mut self, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + place: Place<'tcx>, + tail: Option, + ) -> Option { + // Create a place for this projection. + let mut place_index = self.locals[place.local]?; + let mut ty = PlaceTy::from_ty(body.local_decls[place.local].ty); + tracing::trace!(?place_index, ?ty); + + for proj in place.projection { + let track_elem = proj.try_into().ok()?; + ty = ty.projection_ty(tcx, proj); + place_index = self.register_place_index(ty.ty, place_index, track_elem); + tracing::trace!(?proj, ?place_index, ?ty); + } + + if let Some(tail) = tail { + let ty = match tail { + TrackElem::Discriminant => ty.ty.discriminant_ty(tcx), + TrackElem::Variant(..) | TrackElem::Field(..) => todo!(), + TrackElem::DerefLen => tcx.types.usize, + }; + place_index = self.register_place_index(ty, place_index, tail); + } + + Some(place_index) + } + + #[tracing::instrument(level = "trace", skip(self, tcx, body), ret)] + fn register_place_and_discr( + &mut self, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + place: Place<'tcx>, + ) -> Option { + let place = self.register_place(tcx, body, place, None)?; + let ty = self.places[place].ty; + + if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind() + && let ty::Slice(..) = ref_ty.kind() + { + self.register_place_index(tcx.types.usize, place, TrackElem::DerefLen); + } else if ty.is_enum() { + let discriminant_ty = ty.discriminant_ty(tcx); + self.register_place_index(discriminant_ty, place, TrackElem::Discriminant); + } + + Some(place) + } + + #[tracing::instrument(level = "trace", skip(self, tcx, typing_env), ret)] + pub fn register_value( + &mut self, + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + place: PlaceIndex, + ) -> Option { + let place_info = &mut self.places[place]; + if let Some(value) = place_info.value_index { + return Some(value); + } + + if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, place_info.ty) { + place_info.ty = ty; + } + + // Allocate a value slot if it doesn't have one, and the user requested one. + if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(place_info.ty)) + && layout.backend_repr.is_scalar() + { + place_info.value_index = Some(self.value_count.into()); + self.value_count += 1; + } + + place_info.value_index + } + + #[tracing::instrument(level = "trace", skip(self, f))] + pub fn register_copy_tree( + &mut self, + // Tree to copy. + source: PlaceIndex, + // Tree to build. + target: PlaceIndex, + f: &mut impl FnMut(ValueIndex, ValueIndex), + ) { + if let Some(source_value) = self.places[source].value_index { + let target_value = *self.places[target].value_index.get_or_insert_with(|| { + let value_index = self.value_count.into(); + self.value_count += 1; + value_index + }); + f(source_value, target_value) + } + + // Iterate over `source` children and recurse. + let mut source_child_iter = self.places[source].first_child; + while let Some(source_child) = source_child_iter { + source_child_iter = self.places[source_child].next_sibling; + + // Try to find corresponding child and recurse. Reasoning is similar as above. + let source_info = &self.places[source_child]; + let source_ty = source_info.ty; + let source_elem = source_info.proj_elem.unwrap(); + let target_child = self.register_place_index(source_ty, target, source_elem); + self.register_copy_tree(source_child, target_child, f); + } + } + /// Precompute the list of values inside `root` and store it inside /// as a slice within `inner_values_buffer`. + #[tracing::instrument(level = "trace", skip(self))] fn cache_preorder_invoke(&mut self, root: PlaceIndex) { + debug_assert_matches!(self.mode, PlaceCollectionMode::Full { .. }); let start = self.inner_values_buffer.len(); if let Some(vi) = self.places[root].value_index { self.inner_values_buffer.push(vi); @@ -528,44 +739,6 @@ struct PlaceCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a 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 { - // 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> { @@ -575,51 +748,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> { 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)); - } - } - } - _ => {} - } + self.map.register_place_and_discr(self.tcx, self.body, *place); } } @@ -694,6 +823,7 @@ impl<'tcx> Map<'tcx> { /// /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track /// as such. + #[tracing::instrument(level = "trace", skip(self, f))] pub fn for_each_aliasing_place( &self, place: PlaceRef<'_>, @@ -728,6 +858,7 @@ impl<'tcx> Map<'tcx> { } /// Invoke the given function on all the descendants of the given place, except one branch. + #[tracing::instrument(level = "trace", skip(self, f))] fn for_each_variant_sibling( &self, parent: PlaceIndex, @@ -754,9 +885,22 @@ impl<'tcx> Map<'tcx> { } /// Invoke a function on each value in the given place and all descendants. + #[tracing::instrument(level = "trace", skip(self, f))] fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) { - for &v in self.values_inside(root) { - f(v) + if let Some(range) = self.inner_values.get(root) { + // Optimized path: we have cached the inner values. + let values = &self.inner_values_buffer[range.clone()]; + for &v in values { + f(v) + } + } else { + if let Some(root) = self.places[root].value_index { + f(root) + } + + for child in self.children(root) { + self.for_each_value_inside(child, f); + } } } @@ -769,7 +913,9 @@ impl<'tcx> Map<'tcx> { f: &mut impl FnMut(PlaceIndex, &O), ) { // Fast path is there is nothing to do. - if self.inner_values[root].is_empty() { + if let Some(value_range) = self.inner_values.get(root) + && value_range.is_empty() + { return; } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index bccdd526ab7fd..a76a6eaf9660c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::lattice::{FlatSet, HasBottom}; use rustc_mir_dataflow::value_analysis::{ - Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context, + Map, PlaceCollectionMode, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context, }; use rustc_mir_dataflow::{Analysis, ResultsVisitor, visit_reachable_results}; use rustc_span::DUMMY_SP; @@ -55,10 +55,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { // `O(num_nodes * tracked_places * n)` in terms of time complexity. Since the number of // map nodes is strongly correlated to the number of tracked places, this becomes more or // less `O(n)` if we place a constant limit on the number of tracked places. - let place_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None }; + let value_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None }; // Decide which places to track during the analysis. - let map = Map::new(tcx, body, place_limit); + let map = Map::new(tcx, body, PlaceCollectionMode::Full { value_limit }); // Perform the actual dataflow analysis. let const_ = debug_span!("analyze") diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index c021e7d4c3ae9..fd777a4c4224a 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -62,7 +62,9 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::{self, ScalarInt, TyCtxt}; -use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, TrackElem, ValueIndex}; +use rustc_mir_dataflow::value_analysis::{ + Map, PlaceCollectionMode, PlaceIndex, TrackElem, ValueIndex, +}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -71,7 +73,6 @@ use crate::cost_checker::CostChecker; pub(super) struct JumpThreading; const MAX_COST: u8 = 100; -const MAX_PLACES: usize = 100; impl<'tcx> crate::MirPass<'tcx> for JumpThreading { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { @@ -95,7 +96,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { typing_env, ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine), body, - map: Map::new(tcx, body, Some(MAX_PLACES)), + map: Map::new(tcx, body, PlaceCollectionMode::OnDemand), maybe_loop_headers: loops::maybe_loop_headers(body), entry_states: IndexVec::from_elem(ConditionSet::default(), &body.basic_blocks), }; @@ -273,6 +274,19 @@ impl ConditionSet { } impl<'a, 'tcx> TOFinder<'a, 'tcx> { + fn place(&mut self, place: Place<'tcx>, tail: Option) -> Option { + self.map.register_place(self.tcx, self.body, place, tail) + } + + fn value(&mut self, place: PlaceIndex) -> Option { + self.map.register_value(self.tcx, self.typing_env, place) + } + + fn place_value(&mut self, place: Place<'tcx>, tail: Option) -> Option { + let place = self.place(place, tail)?; + self.value(place) + } + /// 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 { @@ -397,7 +411,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { #[instrument(level = "trace", skip(self, state))] fn process_immediate(&mut self, lhs: PlaceIndex, rhs: ImmTy<'tcx>, state: &mut ConditionSet) { - if let Some(lhs) = self.map.value(lhs) + if let Some(lhs) = self.value(lhs) && let Immediate::Scalar(Scalar::Int(int)) = *rhs { state.fulfill_matches(lhs, int) @@ -412,10 +426,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { constant: OpTy<'tcx>, state: &mut ConditionSet, ) { - let values_inside = self.map.values_inside(lhs); - if !state.active.iter().any(|&(_, cond)| values_inside.contains(&cond.place)) { - return; - } self.map.for_each_projection_value( lhs, constant, @@ -450,9 +460,13 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { #[instrument(level = "trace", skip(self, state))] fn process_copy(&mut self, lhs: PlaceIndex, rhs: PlaceIndex, state: &mut ConditionSet) { let mut renames = FxHashMap::default(); - self.map.for_each_value_pair(rhs, lhs, &mut |rhs, lhs| { - renames.insert(lhs, rhs); - }); + self.map.register_copy_tree( + lhs, // tree to copy + rhs, // tree to build + &mut |lhs, rhs| { + renames.insert(lhs, rhs); + }, + ); state.for_each_mut(|c| { if let Some(rhs) = renames.get(&c.place) { c.place = *rhs @@ -474,7 +488,7 @@ 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 }; + let Some(rhs) = self.place(*rhs, None) else { return }; self.process_copy(lhs, rhs, state) } } @@ -487,12 +501,12 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { rvalue: &Rvalue<'tcx>, state: &mut ConditionSet, ) { - let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return }; + let Some(lhs) = self.place(*lhs_place, None) else { return }; match rvalue { Rvalue::Use(operand) => self.process_operand(lhs, operand, state), // Transfer the conditions on the copy rhs. Rvalue::Discriminant(rhs) => { - let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; + let Some(rhs) = self.place(*rhs, Some(TrackElem::Discriminant)) else { return }; self.process_copy(lhs, rhs, state) } // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. @@ -502,33 +516,37 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // Do not support unions. AggregateKind::Adt(.., Some(_)) => return, AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { - if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) - && let Some(discr_value) = self - .ecx - .discriminant_for_variant(agg_ty, *variant_index) - .discard_err() + let discr_ty = agg_ty.discriminant_ty(self.tcx); + let discr_target = + self.map.register_place_index(discr_ty, lhs, TrackElem::Discriminant); + if let Some(discr_value) = + self.ecx.discriminant_for_variant(agg_ty, *variant_index).discard_err() { self.process_immediate(discr_target, discr_value, state); } - if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) { - idx - } else { - return; - } + self.map.register_place_index( + agg_ty, + lhs, + TrackElem::Variant(*variant_index), + ) } _ => lhs, }; for (field_index, operand) in operands.iter_enumerated() { - if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) { - self.process_operand(field, operand, state); - } + let operand_ty = operand.ty(self.body, self.tcx); + let field = self.map.register_place_index( + operand_ty, + lhs, + TrackElem::Field(field_index), + ); + self.process_operand(field, operand, state); } } // Transfer the conditions on the copy rhs, after inverting the value of the condition. 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 }; + let Some(lhs) = self.value(lhs) else { return }; + let Some(operand) = self.place_value(*operand, None) else { return }; state.retain_mut(|mut c| { if c.place == lhs { let value = self @@ -562,8 +580,8 @@ 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(lhs) = self.value(lhs) else { return }; + let Some(operand) = self.place_value(*operand, None) else { return }; let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env) else { return; @@ -592,7 +610,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // If we expect `discriminant(place) ?= A`, // we have an opportunity if `variant_index ?= A`. StatementKind::SetDiscriminant { box place, variant_index } => { - let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return }; + let Some(discr_target) = self.place(*place, Some(TrackElem::Discriminant)) else { + return; + }; let enum_ty = place.ty(self.body, self.tcx).ty; // `SetDiscriminant` guarantees that the discriminant is now `variant_index`. // Even if the discriminant write does nothing due to niches, it is UB to set the @@ -608,7 +628,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { - let Some(place) = self.map.find_value(place.as_ref()) else { return }; + let Some(place) = self.place_value(*place, None) else { return }; state.fulfill_matches(place, ScalarInt::TRUE); } StatementKind::Assign(box (lhs_place, rhs)) => { @@ -665,7 +685,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { state: &mut ConditionSet, ) { let Some(discr) = discr.place() else { return }; - let Some(discr_idx) = self.map.find_value(discr.as_ref()) else { return }; + let Some(discr_idx) = self.place_value(discr, None) else { return }; let discr_ty = discr.ty(self.body, self.tcx).ty; let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; diff --git a/tests/coverage/closure.cov-map b/tests/coverage/closure.cov-map index d713145d86129..d21be004594d8 100644 --- a/tests/coverage/closure.cov-map +++ b/tests/coverage/closure.cov-map @@ -69,24 +69,22 @@ Number of file 0 mappings: 61 - Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Highest counter ID seen: c1 -Function name: closure::main::{closure#0} -Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 28, 05, 00, 06, 01, 01, 0d, 00, 1a, 01, 00, 1d, 00, 1e, 01, 01, 0c, 00, 14, 05, 00, 15, 02, 0a, 02, 02, 09, 00, 0a, 01, 01, 09, 00, 17, 01, 00, 18, 00, 20, 01, 01, 05, 00, 06] +Function name: closure::main::{closure#0} (unused) +Raw bytes (49): 0x[01, 01, 00, 09, 00, 28, 05, 00, 06, 00, 01, 0d, 00, 1a, 00, 00, 1d, 00, 1e, 00, 01, 0c, 00, 14, 00, 00, 15, 02, 0a, 00, 02, 09, 00, 0a, 00, 01, 09, 00, 17, 00, 00, 18, 00, 20, 00, 01, 05, 00, 06] Number of files: 1 - file 0 => $DIR/closure.rs -Number of expressions: 1 -- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of expressions: 0 Number of file 0 mappings: 9 -- Code(Counter(0)) at (prev + 40, 5) to (start + 0, 6) -- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 26) -- Code(Counter(0)) at (prev + 0, 29) to (start + 0, 30) -- Code(Counter(0)) at (prev + 1, 12) to (start + 0, 20) -- Code(Counter(1)) at (prev + 0, 21) to (start + 2, 10) -- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10) - = (c0 - c1) -- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 23) -- Code(Counter(0)) at (prev + 0, 24) to (start + 0, 32) -- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6) -Highest counter ID seen: c1 +- Code(Zero) at (prev + 40, 5) to (start + 0, 6) +- Code(Zero) at (prev + 1, 13) to (start + 0, 26) +- Code(Zero) at (prev + 0, 29) to (start + 0, 30) +- Code(Zero) at (prev + 1, 12) to (start + 0, 20) +- Code(Zero) at (prev + 0, 21) to (start + 2, 10) +- Code(Zero) at (prev + 2, 9) to (start + 0, 10) +- Code(Zero) at (prev + 1, 9) to (start + 0, 23) +- Code(Zero) at (prev + 0, 24) to (start + 0, 32) +- Code(Zero) at (prev + 1, 5) to (start + 0, 6) +Highest counter ID seen: (none) Function name: closure::main::{closure#10} (unused) Raw bytes (20): 0x[01, 01, 00, 03, 00, 9b, 01, 07, 00, 08, 00, 00, 09, 00, 11, 00, 00, 20, 00, 21] @@ -186,24 +184,22 @@ Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) Highest counter ID seen: c1 -Function name: closure::main::{closure#18} -Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 19, 0d, 00, 0e, 01, 01, 15, 00, 22, 01, 00, 25, 00, 26, 01, 01, 14, 00, 1c, 05, 00, 1d, 02, 12, 02, 02, 11, 00, 12, 01, 01, 11, 00, 1f, 01, 00, 20, 00, 28, 01, 01, 0d, 00, 0e] +Function name: closure::main::{closure#18} (unused) +Raw bytes (49): 0x[01, 01, 00, 09, 00, 19, 0d, 00, 0e, 00, 01, 15, 00, 22, 00, 00, 25, 00, 26, 00, 01, 14, 00, 1c, 00, 00, 1d, 02, 12, 00, 02, 11, 00, 12, 00, 01, 11, 00, 1f, 00, 00, 20, 00, 28, 00, 01, 0d, 00, 0e] Number of files: 1 - file 0 => $DIR/closure.rs -Number of expressions: 1 -- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of expressions: 0 Number of file 0 mappings: 9 -- Code(Counter(0)) at (prev + 25, 13) to (start + 0, 14) -- Code(Counter(0)) at (prev + 1, 21) to (start + 0, 34) -- Code(Counter(0)) at (prev + 0, 37) to (start + 0, 38) -- Code(Counter(0)) at (prev + 1, 20) to (start + 0, 28) -- Code(Counter(1)) at (prev + 0, 29) to (start + 2, 18) -- Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 18) - = (c0 - c1) -- Code(Counter(0)) at (prev + 1, 17) to (start + 0, 31) -- Code(Counter(0)) at (prev + 0, 32) to (start + 0, 40) -- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14) -Highest counter ID seen: c1 +- Code(Zero) at (prev + 25, 13) to (start + 0, 14) +- Code(Zero) at (prev + 1, 21) to (start + 0, 34) +- Code(Zero) at (prev + 0, 37) to (start + 0, 38) +- Code(Zero) at (prev + 1, 20) to (start + 0, 28) +- Code(Zero) at (prev + 0, 29) to (start + 2, 18) +- Code(Zero) at (prev + 2, 17) to (start + 0, 18) +- Code(Zero) at (prev + 1, 17) to (start + 0, 31) +- Code(Zero) at (prev + 0, 32) to (start + 0, 40) +- Code(Zero) at (prev + 1, 13) to (start + 0, 14) +Highest counter ID seen: (none) Function name: closure::main::{closure#19} Raw bytes (51): 0x[01, 01, 01, 01, 05, 09, 01, 43, 0d, 00, 0e, 01, 01, 15, 00, 22, 01, 00, 25, 00, 26, 01, 01, 14, 00, 1c, 05, 00, 1d, 02, 12, 02, 02, 11, 00, 12, 01, 01, 11, 00, 1f, 01, 00, 20, 00, 28, 01, 01, 0d, 00, 0e] diff --git a/tests/coverage/try_error_result.cov-map b/tests/coverage/try_error_result.cov-map index 53757ef8a9bd6..fa937cfb7755c 100644 --- a/tests/coverage/try_error_result.cov-map +++ b/tests/coverage/try_error_result.cov-map @@ -92,71 +92,28 @@ Number of file 0 mappings: 14 Highest counter ID seen: c1 Function name: try_error_result::test2 -Raw bytes (443): 0x[01, 01, 3d, 09, 0d, 09, 57, 0d, 15, 09, 57, 0d, 15, 09, 57, 0d, 15, 09, 4f, 53, 1d, 57, 19, 0d, 15, 09, 57, 0d, 15, 09, 57, 0d, 15, 09, 53, 57, 19, 0d, 15, 09, 4f, 53, 1d, 57, 19, 0d, 15, 41, 6b, 21, 25, 41, 21, 41, 6b, 21, 25, 05, 8f, 01, 93, 01, 2d, 09, 29, 05, 09, 05, 09, 05, 93, 01, 09, 29, 05, 8f, 01, 93, 01, 2d, 09, 29, 45, a7, 01, 31, 35, 45, 31, 45, a7, 01, 31, 35, 49, bb, 01, 39, 3d, 49, 39, 49, bb, 01, 39, 3d, 11, 05, c7, 01, 05, cb, 01, 3d, cf, 01, 39, d3, 01, 35, d7, 01, 31, db, 01, 2d, df, 01, 29, e3, 01, 25, e7, 01, 21, eb, 01, 1d, ef, 01, 19, f3, 01, 15, 11, 0d, 39, 01, 3d, 01, 00, 1d, 01, 01, 09, 00, 0f, 01, 00, 12, 00, 1a, 01, 01, 09, 01, 12, 01, 01, 15, 00, 17, 01, 05, 09, 00, 0e, 05, 02, 09, 01, 11, 05, 04, 0d, 00, 1a, 09, 02, 0d, 00, 13, 09, 00, 14, 00, 1f, 0d, 00, 2f, 00, 30, 02, 00, 31, 00, 35, 02, 00, 45, 00, 4f, 02, 00, 50, 00, 62, 02, 01, 0d, 00, 13, 02, 02, 11, 00, 1c, 15, 01, 11, 00, 12, 36, 02, 11, 00, 15, 36, 02, 11, 00, 1b, 36, 01, 15, 00, 27, 4a, 02, 11, 00, 14, 36, 00, 17, 00, 1d, 36, 00, 1e, 00, 29, 19, 00, 41, 00, 42, 3e, 00, 43, 00, 47, 1d, 00, 5f, 00, 60, 4a, 01, 0d, 00, 17, 66, 01, 11, 00, 14, 41, 00, 17, 00, 1d, 41, 00, 1e, 00, 29, 21, 00, 41, 00, 42, 62, 00, 43, 00, 47, 25, 00, 60, 00, 61, 66, 01, 0d, 00, 17, 8a, 01, 04, 11, 00, 14, 7e, 00, 17, 00, 1d, 7e, 00, 1e, 00, 29, 29, 00, 42, 00, 43, 82, 01, 00, 44, 00, 48, 2d, 00, 61, 00, 62, 8a, 01, 01, 0d, 00, 17, a2, 01, 01, 11, 00, 14, 45, 00, 17, 00, 1d, 45, 01, 12, 00, 1d, 31, 00, 36, 00, 37, 9e, 01, 01, 12, 00, 16, 35, 00, 2f, 00, 30, a2, 01, 01, 0d, 00, 17, b6, 01, 01, 11, 00, 14, 49, 00, 17, 00, 1d, 49, 01, 12, 00, 1d, 39, 01, 11, 00, 12, b2, 01, 01, 12, 00, 16, 3d, 01, 11, 00, 12, b6, 01, 02, 0d, 00, 17, be, 01, 03, 05, 00, 0b, c2, 01, 01, 01, 00, 02] +Raw bytes (325): 0x[01, 01, 12, 07, 09, 01, 05, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 01, 09, 05, 39, 01, 3d, 01, 00, 1d, 01, 01, 09, 00, 0f, 01, 00, 12, 00, 1a, 01, 01, 09, 01, 12, 01, 01, 15, 00, 17, 01, 05, 09, 00, 0e, 05, 02, 09, 01, 11, 05, 04, 0d, 00, 1a, 02, 02, 0d, 00, 13, 02, 00, 14, 00, 1f, 00, 00, 2f, 00, 30, 02, 00, 31, 00, 35, 02, 00, 45, 00, 4f, 02, 00, 50, 00, 62, 02, 01, 0d, 00, 13, 02, 02, 11, 00, 1c, 00, 01, 11, 00, 12, 02, 02, 11, 00, 15, 02, 02, 11, 00, 1b, 02, 01, 15, 00, 27, 00, 02, 11, 00, 14, 02, 00, 17, 00, 1d, 02, 00, 1e, 00, 29, 02, 00, 41, 00, 42, 00, 00, 43, 00, 47, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 17, 00, 01, 11, 00, 14, 00, 00, 17, 00, 1d, 00, 00, 1e, 00, 29, 00, 00, 41, 00, 42, 00, 00, 43, 00, 47, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 17, 42, 04, 11, 00, 14, 42, 00, 17, 00, 1d, 42, 00, 1e, 00, 29, 00, 00, 42, 00, 43, 42, 00, 44, 00, 48, 00, 00, 61, 00, 62, 42, 01, 0d, 00, 17, 42, 01, 11, 00, 14, 42, 00, 17, 00, 1d, 42, 01, 12, 00, 1d, 00, 00, 36, 00, 37, 42, 01, 12, 00, 16, 00, 00, 2f, 00, 30, 42, 01, 0d, 00, 17, 42, 01, 11, 00, 14, 42, 00, 17, 00, 1d, 42, 01, 12, 00, 1d, 00, 01, 11, 00, 12, 42, 01, 12, 00, 16, 00, 01, 11, 00, 12, 42, 02, 0d, 00, 17, 46, 03, 05, 00, 0b, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => $DIR/try_error_result.rs -Number of expressions: 61 -- expression 0 operands: lhs = Counter(2), rhs = Counter(3) -- expression 1 operands: lhs = Counter(2), rhs = Expression(21, Add) -- expression 2 operands: lhs = Counter(3), rhs = Counter(5) -- expression 3 operands: lhs = Counter(2), rhs = Expression(21, Add) -- expression 4 operands: lhs = Counter(3), rhs = Counter(5) -- expression 5 operands: lhs = Counter(2), rhs = Expression(21, Add) -- expression 6 operands: lhs = Counter(3), rhs = Counter(5) -- expression 7 operands: lhs = Counter(2), rhs = Expression(19, Add) -- expression 8 operands: lhs = Expression(20, Add), rhs = Counter(7) -- expression 9 operands: lhs = Expression(21, Add), rhs = Counter(6) -- expression 10 operands: lhs = Counter(3), rhs = Counter(5) -- expression 11 operands: lhs = Counter(2), rhs = Expression(21, Add) -- expression 12 operands: lhs = Counter(3), rhs = Counter(5) -- expression 13 operands: lhs = Counter(2), rhs = Expression(21, Add) -- expression 14 operands: lhs = Counter(3), rhs = Counter(5) -- expression 15 operands: lhs = Counter(2), rhs = Expression(20, Add) -- expression 16 operands: lhs = Expression(21, Add), rhs = Counter(6) -- expression 17 operands: lhs = Counter(3), rhs = Counter(5) -- expression 18 operands: lhs = Counter(2), rhs = Expression(19, Add) -- expression 19 operands: lhs = Expression(20, Add), rhs = Counter(7) -- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(6) -- expression 21 operands: lhs = Counter(3), rhs = Counter(5) -- expression 22 operands: lhs = Counter(16), rhs = Expression(26, Add) -- expression 23 operands: lhs = Counter(8), rhs = Counter(9) -- expression 24 operands: lhs = Counter(16), rhs = Counter(8) -- expression 25 operands: lhs = Counter(16), rhs = Expression(26, Add) -- expression 26 operands: lhs = Counter(8), rhs = Counter(9) -- expression 27 operands: lhs = Counter(1), rhs = Expression(35, Add) -- expression 28 operands: lhs = Expression(36, Add), rhs = Counter(11) -- expression 29 operands: lhs = Counter(2), rhs = Counter(10) -- expression 30 operands: lhs = Counter(1), rhs = Counter(2) -- expression 31 operands: lhs = Counter(1), rhs = Counter(2) -- expression 32 operands: lhs = Counter(1), rhs = Expression(36, Add) -- expression 33 operands: lhs = Counter(2), rhs = Counter(10) -- expression 34 operands: lhs = Counter(1), rhs = Expression(35, Add) -- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(11) -- expression 36 operands: lhs = Counter(2), rhs = Counter(10) -- expression 37 operands: lhs = Counter(17), rhs = Expression(41, Add) -- expression 38 operands: lhs = Counter(12), rhs = Counter(13) -- expression 39 operands: lhs = Counter(17), rhs = Counter(12) -- expression 40 operands: lhs = Counter(17), rhs = Expression(41, Add) -- expression 41 operands: lhs = Counter(12), rhs = Counter(13) -- expression 42 operands: lhs = Counter(18), rhs = Expression(46, Add) -- expression 43 operands: lhs = Counter(14), rhs = Counter(15) -- expression 44 operands: lhs = Counter(18), rhs = Counter(14) -- expression 45 operands: lhs = Counter(18), rhs = Expression(46, Add) -- expression 46 operands: lhs = Counter(14), rhs = Counter(15) -- expression 47 operands: lhs = Counter(4), rhs = Counter(1) -- expression 48 operands: lhs = Expression(49, Add), rhs = Counter(1) -- expression 49 operands: lhs = Expression(50, Add), rhs = Counter(15) -- expression 50 operands: lhs = Expression(51, Add), rhs = Counter(14) -- expression 51 operands: lhs = Expression(52, Add), rhs = Counter(13) -- expression 52 operands: lhs = Expression(53, Add), rhs = Counter(12) -- expression 53 operands: lhs = Expression(54, Add), rhs = Counter(11) -- expression 54 operands: lhs = Expression(55, Add), rhs = Counter(10) -- expression 55 operands: lhs = Expression(56, Add), rhs = Counter(9) -- expression 56 operands: lhs = Expression(57, Add), rhs = Counter(8) -- expression 57 operands: lhs = Expression(58, Add), rhs = Counter(7) -- expression 58 operands: lhs = Expression(59, Add), rhs = Counter(6) -- expression 59 operands: lhs = Expression(60, Add), rhs = Counter(5) -- expression 60 operands: lhs = Counter(4), rhs = Counter(3) +Number of expressions: 18 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(2) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(2), rhs = Counter(0) +- expression 3 operands: lhs = Counter(2), rhs = Counter(0) +- expression 4 operands: lhs = Counter(2), rhs = Counter(0) +- expression 5 operands: lhs = Counter(2), rhs = Counter(0) +- expression 6 operands: lhs = Counter(2), rhs = Counter(0) +- expression 7 operands: lhs = Counter(2), rhs = Counter(0) +- expression 8 operands: lhs = Counter(2), rhs = Counter(0) +- expression 9 operands: lhs = Counter(2), rhs = Counter(0) +- expression 10 operands: lhs = Counter(2), rhs = Counter(0) +- expression 11 operands: lhs = Counter(2), rhs = Counter(0) +- expression 12 operands: lhs = Counter(2), rhs = Counter(0) +- expression 13 operands: lhs = Counter(2), rhs = Counter(0) +- expression 14 operands: lhs = Counter(2), rhs = Counter(0) +- expression 15 operands: lhs = Counter(2), rhs = Counter(0) +- expression 16 operands: lhs = Counter(2), rhs = Counter(0) +- expression 17 operands: lhs = Counter(2), rhs = Counter(1) Number of file 0 mappings: 57 - Code(Counter(0)) at (prev + 61, 1) to (start + 0, 29) - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 15) @@ -166,83 +123,83 @@ Number of file 0 mappings: 57 - Code(Counter(0)) at (prev + 5, 9) to (start + 0, 14) - Code(Counter(1)) at (prev + 2, 9) to (start + 1, 17) - Code(Counter(1)) at (prev + 4, 13) to (start + 0, 26) -- Code(Counter(2)) at (prev + 2, 13) to (start + 0, 19) -- Code(Counter(2)) at (prev + 0, 20) to (start + 0, 31) -- Code(Counter(3)) at (prev + 0, 47) to (start + 0, 48) +- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 19) + = ((c0 + c1) - c2) +- Code(Expression(0, Sub)) at (prev + 0, 20) to (start + 0, 31) + = ((c0 + c1) - c2) +- Code(Zero) at (prev + 0, 47) to (start + 0, 48) - Code(Expression(0, Sub)) at (prev + 0, 49) to (start + 0, 53) - = (c2 - c3) + = ((c0 + c1) - c2) - Code(Expression(0, Sub)) at (prev + 0, 69) to (start + 0, 79) - = (c2 - c3) + = ((c0 + c1) - c2) - Code(Expression(0, Sub)) at (prev + 0, 80) to (start + 0, 98) - = (c2 - c3) + = ((c0 + c1) - c2) - Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 19) - = (c2 - c3) + = ((c0 + c1) - c2) - Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 28) - = (c2 - c3) -- Code(Counter(5)) at (prev + 1, 17) to (start + 0, 18) -- Code(Expression(13, Sub)) at (prev + 2, 17) to (start + 0, 21) - = (c2 - (c3 + c5)) -- Code(Expression(13, Sub)) at (prev + 2, 17) to (start + 0, 27) - = (c2 - (c3 + c5)) -- Code(Expression(13, Sub)) at (prev + 1, 21) to (start + 0, 39) - = (c2 - (c3 + c5)) -- Code(Expression(18, Sub)) at (prev + 2, 17) to (start + 0, 20) - = (c2 - (((c3 + c5) + c6) + c7)) -- Code(Expression(13, Sub)) at (prev + 0, 23) to (start + 0, 29) - = (c2 - (c3 + c5)) -- Code(Expression(13, Sub)) at (prev + 0, 30) to (start + 0, 41) - = (c2 - (c3 + c5)) -- Code(Counter(6)) at (prev + 0, 65) to (start + 0, 66) -- Code(Expression(15, Sub)) at (prev + 0, 67) to (start + 0, 71) - = (c2 - ((c3 + c5) + c6)) -- Code(Counter(7)) at (prev + 0, 95) to (start + 0, 96) -- Code(Expression(18, Sub)) at (prev + 1, 13) to (start + 0, 23) - = (c2 - (((c3 + c5) + c6) + c7)) -- Code(Expression(25, Sub)) at (prev + 1, 17) to (start + 0, 20) - = (c16 - (c8 + c9)) -- Code(Counter(16)) at (prev + 0, 23) to (start + 0, 29) -- Code(Counter(16)) at (prev + 0, 30) to (start + 0, 41) -- Code(Counter(8)) at (prev + 0, 65) to (start + 0, 66) -- Code(Expression(24, Sub)) at (prev + 0, 67) to (start + 0, 71) - = (c16 - c8) -- Code(Counter(9)) at (prev + 0, 96) to (start + 0, 97) -- Code(Expression(25, Sub)) at (prev + 1, 13) to (start + 0, 23) - = (c16 - (c8 + c9)) -- Code(Expression(34, Sub)) at (prev + 4, 17) to (start + 0, 20) - = (c1 - ((c2 + c10) + c11)) -- Code(Expression(31, Sub)) at (prev + 0, 23) to (start + 0, 29) - = (c1 - c2) -- Code(Expression(31, Sub)) at (prev + 0, 30) to (start + 0, 41) - = (c1 - c2) -- Code(Counter(10)) at (prev + 0, 66) to (start + 0, 67) -- Code(Expression(32, Sub)) at (prev + 0, 68) to (start + 0, 72) - = (c1 - (c2 + c10)) -- Code(Counter(11)) at (prev + 0, 97) to (start + 0, 98) -- Code(Expression(34, Sub)) at (prev + 1, 13) to (start + 0, 23) - = (c1 - ((c2 + 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 + 0, 29) -- Code(Counter(17)) at (prev + 1, 18) to (start + 0, 29) -- Code(Counter(12)) at (prev + 0, 54) to (start + 0, 55) -- Code(Expression(39, Sub)) at (prev + 1, 18) to (start + 0, 22) - = (c17 - c12) -- Code(Counter(13)) at (prev + 0, 47) to (start + 0, 48) -- Code(Expression(40, Sub)) at (prev + 1, 13) to (start + 0, 23) - = (c17 - (c12 + c13)) -- Code(Expression(45, Sub)) at (prev + 1, 17) to (start + 0, 20) - = (c18 - (c14 + c15)) -- Code(Counter(18)) at (prev + 0, 23) to (start + 0, 29) -- Code(Counter(18)) at (prev + 1, 18) to (start + 0, 29) -- Code(Counter(14)) at (prev + 1, 17) to (start + 0, 18) -- Code(Expression(44, Sub)) at (prev + 1, 18) to (start + 0, 22) - = (c18 - c14) -- Code(Counter(15)) at (prev + 1, 17) to (start + 0, 18) -- Code(Expression(45, Sub)) at (prev + 2, 13) to (start + 0, 23) - = (c18 - (c14 + c15)) -- Code(Expression(47, Sub)) at (prev + 3, 5) to (start + 0, 11) - = (c4 - c1) -- Code(Expression(48, Sub)) at (prev + 1, 1) to (start + 0, 2) - = (((((((((((((c4 + c3) + c5) + c6) + c7) + c8) + c9) + c10) + c11) + c12) + c13) + c14) + c15) - c1) -Highest counter ID seen: c18 + = ((c0 + c1) - c2) +- Code(Zero) at (prev + 1, 17) to (start + 0, 18) +- Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 21) + = ((c0 + c1) - c2) +- Code(Expression(0, Sub)) at (prev + 2, 17) to (start + 0, 27) + = ((c0 + c1) - c2) +- Code(Expression(0, Sub)) at (prev + 1, 21) to (start + 0, 39) + = ((c0 + c1) - c2) +- Code(Zero) at (prev + 2, 17) to (start + 0, 20) +- Code(Expression(0, Sub)) at (prev + 0, 23) to (start + 0, 29) + = ((c0 + c1) - c2) +- Code(Expression(0, Sub)) at (prev + 0, 30) to (start + 0, 41) + = ((c0 + c1) - c2) +- Code(Expression(0, Sub)) at (prev + 0, 65) to (start + 0, 66) + = ((c0 + c1) - c2) +- Code(Zero) at (prev + 0, 67) to (start + 0, 71) +- Code(Zero) at (prev + 0, 95) to (start + 0, 96) +- Code(Zero) at (prev + 1, 13) to (start + 0, 23) +- Code(Zero) at (prev + 1, 17) to (start + 0, 20) +- Code(Zero) at (prev + 0, 23) to (start + 0, 29) +- Code(Zero) at (prev + 0, 30) to (start + 0, 41) +- Code(Zero) at (prev + 0, 65) to (start + 0, 66) +- Code(Zero) at (prev + 0, 67) to (start + 0, 71) +- Code(Zero) at (prev + 0, 96) to (start + 0, 97) +- Code(Zero) at (prev + 1, 13) to (start + 0, 23) +- Code(Expression(16, Sub)) at (prev + 4, 17) to (start + 0, 20) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 0, 23) to (start + 0, 29) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 0, 30) to (start + 0, 41) + = (c2 - c0) +- Code(Zero) at (prev + 0, 66) to (start + 0, 67) +- Code(Expression(16, Sub)) at (prev + 0, 68) to (start + 0, 72) + = (c2 - c0) +- Code(Zero) at (prev + 0, 97) to (start + 0, 98) +- Code(Expression(16, Sub)) at (prev + 1, 13) to (start + 0, 23) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 1, 17) to (start + 0, 20) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 0, 23) to (start + 0, 29) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 1, 18) to (start + 0, 29) + = (c2 - c0) +- Code(Zero) at (prev + 0, 54) to (start + 0, 55) +- Code(Expression(16, Sub)) at (prev + 1, 18) to (start + 0, 22) + = (c2 - c0) +- Code(Zero) at (prev + 0, 47) to (start + 0, 48) +- Code(Expression(16, Sub)) at (prev + 1, 13) to (start + 0, 23) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 1, 17) to (start + 0, 20) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 0, 23) to (start + 0, 29) + = (c2 - c0) +- Code(Expression(16, Sub)) at (prev + 1, 18) to (start + 0, 29) + = (c2 - c0) +- Code(Zero) at (prev + 1, 17) to (start + 0, 18) +- Code(Expression(16, Sub)) at (prev + 1, 18) to (start + 0, 22) + = (c2 - c0) +- Code(Zero) at (prev + 1, 17) to (start + 0, 18) +- Code(Expression(16, Sub)) at (prev + 2, 13) to (start + 0, 23) + = (c2 - c0) +- Code(Expression(17, Sub)) at (prev + 3, 5) to (start + 0, 11) + = (c2 - c1) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c1