From bbfef750a9ce4e462ccb7885104e5c1fa141b0d8 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 8 Sep 2026 01:31:40 +0000 Subject: [PATCH] Cache block seeking results for initialized_at_terminator and _exit --- .../src/type_check/liveness/trace.rs | 94 +++++++++++-------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 89a8899a991c9..6b1cb3a0c9eef 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::DenseBitSet; +use rustc_index::IndexVec; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; @@ -10,7 +11,7 @@ use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; @@ -55,6 +56,8 @@ pub(super) fn trace<'tcx>( location_map, local_use_map, move_data, + term_states: IndexVec::new(), + exit_states: IndexVec::new(), drop_data: FxIndexMap::default(), }; @@ -90,6 +93,10 @@ struct LivenessContext<'a, 'typeck, 'tcx> { /// Index indicating where each variable is assigned, used, or /// dropped. local_use_map: &'a LocalUseMap, + + // Caches for the results of `initialized_at_terminator` and `initialized_at_exit`. + term_states: IndexVec>>>, + exit_states: IndexVec>>>, } struct DropData<'tcx> { @@ -298,9 +305,11 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { let location = self.cx.location_map.to_location(drop_point); debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,); - if self.cx.initialized_at_terminator(location.block, mpi) - && self.drop_live_at.insert(drop_point) - { + if self.cx.initialized_at_terminator(location.block, mpi) { + let inserted = self.drop_live_at.insert(drop_point); + // Right now, we should not visit a drop_point twice. + // If we do, this will trigger a debug assert so we know we can optimize. + debug_assert!(inserted, "drop point should not have been visited yet"); self.drop_locations.push(location); self.stack.push(drop_point); } @@ -453,18 +462,31 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } -impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> { - /// Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already. - /// - /// In practice, the results of this dataflow analysis are rarely needed but can be expensive to - /// compute on big functions, so we compute them lazily as a fast path when: - /// - there are relevant live locals - /// - there are drop points for these relevant live locals. - /// - /// This happens as part of the drop-liveness computation: it's the only place checking for - /// maybe-initializedness of `MovePathIndex`es. - fn flow_inits(&mut self) -> &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>> { - self.flow_inits.get_or_insert_with(|| { +enum InitAtLocation { + Terminator, + Exit, +} + +impl<'tcx> LivenessContext<'_, '_, 'tcx> { + fn body(&self) -> &Body<'tcx> { + self.typeck.body + } + + /// Returns `true` if the local variable (or some part of it) is initialized + /// at the location defined by `init_at_location`. + fn initialized_at( + &mut self, + block: BasicBlock, + mpi: MovePathIndex, + init_at_location: InitAtLocation, + ) -> bool { + // Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already. + // + // In practice, the results of this dataflow analysis are rarely needed but can be expensive to + // compute on big functions, so we compute them lazily as a fast path when: + // - there are relevant live locals + // - there are drop points for these relevant live locals. + let flow_inits = self.flow_inits.get_or_insert_with(|| { let tcx = self.typeck.tcx(); let body = self.typeck.body; // FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s. @@ -484,21 +506,21 @@ impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> { .iterate_to_fixpoint(tcx, body, Some("borrowck")) .into_results_cursor(body); flow_inits - }) - } -} - -impl<'tcx> LivenessContext<'_, '_, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.typeck.body - } - - /// Returns `true` if the local variable (or some part of it) is initialized at the current - /// cursor position. Callers should call one of the `seek` methods immediately before to point - /// the cursor to the desired location. - fn initialized_at_curr_loc(&mut self, mpi: MovePathIndex) -> bool { - let flow_inits = self.flow_inits(); - let state = flow_inits.get(); + }); + let states = match init_at_location { + InitAtLocation::Terminator => &mut self.term_states, + InitAtLocation::Exit => &mut self.exit_states, + }; + let state = states.get_or_insert_with(block, || { + let terminator_location = self.typeck.body.terminator_loc(block); + match init_at_location { + InitAtLocation::Terminator => { + flow_inits.seek_before_primary_effect(terminator_location) + } + InitAtLocation::Exit => flow_inits.seek_after_primary_effect(terminator_location), + } + flow_inits.get().clone() + }); if state.contains(mpi) { return true; } @@ -512,9 +534,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// DROP of some local variable will have an effect -- note that /// drops, as they may unwind, are always terminators. fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - let terminator_location = self.body().terminator_loc(block); - self.flow_inits().seek_before_primary_effect(terminator_location); - self.initialized_at_curr_loc(mpi) + self.initialized_at(block, mpi, InitAtLocation::Terminator) } /// Returns `true` if the path `mpi` (or some part of it) is initialized at @@ -523,9 +543,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// **Warning:** Does not account for the result of `Call` /// instructions. fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - let terminator_location = self.body().terminator_loc(block); - self.flow_inits().seek_after_primary_effect(terminator_location); - self.initialized_at_curr_loc(mpi) + self.initialized_at(block, mpi, InitAtLocation::Exit) } /// Stores the result that all regions in `value` are live for the