Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 56 additions & 38 deletions compiler/rustc_borrowck/src/type_check/liveness/trace.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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(),
};

Expand Down Expand Up @@ -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<BasicBlock, Option<MaybeReachable<MixedBitSet<MovePathIndex>>>>,
exit_states: IndexVec<BasicBlock, Option<MaybeReachable<MixedBitSet<MovePathIndex>>>>,
}

struct DropData<'tcx> {
Expand Down Expand Up @@ -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");
Comment on lines +308 to +312

@jackh726 jackh726 Sep 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically completely unrelated - but sticking it in as a drive-by pass. I had a though that we might be visiting drop points twice (and thus could switch these conditions for a perf benefit) - but the iteration here is unique.

View changes since the review

self.drop_locations.push(location);
self.stack.push(drop_point);
}
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading