Skip to content

Commit ec28ae9

Browse files
committed
Auto merge of #141667 - lqd:lazy-maybe-init, r=matthewjasper
Add fast path for maybe-initializedness in liveness r? `@matthewjasper` Correct me if I'm wrong Matthew, but my understanding is that 1. `MaybeInitializedPlaces` is currently eagerly computed, in `do_mir_borrowck` 2. but this data is only used in liveness 3. and `liveness::trace` actually only uses it for drop-liveness This PR moves the computation to `liveness::trace` which looks to be its only use-site. We also add a fast path there, so that it's only computed by drop-liveness. This is interesting because 1) liveness is only computed for relevant live locals, 2) drop-liveness is only computed for relevant live locals with >0 drop points; 0 is the common case from our benchmarks, as far as I can tell, so even just computing the entire data lazily helps. It seems possible to also reduce the domain here, and speed up the analysis for the cases where it has to be computed -- so I've left a fixme for that, and may look into it soon. (I've come upon this while doing implementation work for polonius, so don't be too enamored with possible wins: the goal is to reduce the eventual polonius overhead and make it more palatable 😓)
2 parents 7a7bcbb + ddeae14 commit ec28ae9

File tree

8 files changed

+67
-48
lines changed

8 files changed

+67
-48
lines changed

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ use rustc_middle::ty::{
4040
self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions,
4141
};
4242
use rustc_middle::{bug, span_bug};
43-
use rustc_mir_dataflow::impls::{
44-
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
45-
};
43+
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
4644
use rustc_mir_dataflow::move_paths::{
4745
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
4846
};
@@ -324,10 +322,6 @@ fn do_mir_borrowck<'tcx>(
324322

325323
let move_data = MoveData::gather_moves(body, tcx, |_| true);
326324

327-
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
328-
.iterate_to_fixpoint(tcx, body, Some("borrowck"))
329-
.into_results_cursor(body);
330-
331325
let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
332326
let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
333327

@@ -346,7 +340,6 @@ fn do_mir_borrowck<'tcx>(
346340
body,
347341
&promoted,
348342
&location_table,
349-
flow_inits,
350343
&move_data,
351344
&borrow_set,
352345
consumer_options,

compiler/rustc_borrowck/src/nll.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
1111
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
1212
use rustc_middle::ty::print::with_no_trimmed_paths;
1313
use rustc_middle::ty::{self, TyCtxt};
14-
use rustc_mir_dataflow::ResultsCursor;
15-
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
1614
use rustc_mir_dataflow::move_paths::MoveData;
1715
use rustc_mir_dataflow::points::DenseLocationMap;
1816
use rustc_session::config::MirIncludeSpans;
@@ -75,14 +73,13 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
7573
/// Computes the (non-lexical) regions from the input MIR.
7674
///
7775
/// This may result in errors being reported.
78-
pub(crate) fn compute_regions<'a, 'tcx>(
76+
pub(crate) fn compute_regions<'tcx>(
7977
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
8078
infcx: &BorrowckInferCtxt<'tcx>,
8179
universal_regions: UniversalRegions<'tcx>,
8280
body: &Body<'tcx>,
8381
promoted: &IndexSlice<Promoted, Body<'tcx>>,
8482
location_table: &PoloniusLocationTable,
85-
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
8683
move_data: &MoveData<'tcx>,
8784
borrow_set: &BorrowSet<'tcx>,
8885
consumer_options: Option<ConsumerOptions>,
@@ -112,7 +109,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
112109
location_table,
113110
borrow_set,
114111
&mut polonius_facts,
115-
flow_inits,
116112
move_data,
117113
Rc::clone(&location_map),
118114
);

compiler/rustc_borrowck/src/type_check/liveness/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ use rustc_middle::mir::{Body, Local, Location, SourceInfo};
55
use rustc_middle::span_bug;
66
use rustc_middle::ty::relate::Relate;
77
use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeVisitable};
8-
use rustc_mir_dataflow::ResultsCursor;
9-
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
108
use rustc_mir_dataflow::move_paths::MoveData;
119
use rustc_mir_dataflow::points::DenseLocationMap;
1210
use tracing::debug;
@@ -28,10 +26,9 @@ mod trace;
2826
///
2927
/// N.B., this computation requires normalization; therefore, it must be
3028
/// performed before
31-
pub(super) fn generate<'a, 'tcx>(
29+
pub(super) fn generate<'tcx>(
3230
typeck: &mut TypeChecker<'_, 'tcx>,
3331
location_map: &DenseLocationMap,
34-
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
3532
move_data: &MoveData<'tcx>,
3633
) {
3734
debug!("liveness::generate");
@@ -58,7 +55,7 @@ pub(super) fn generate<'a, 'tcx>(
5855
let (relevant_live_locals, boring_locals) =
5956
compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
6057

61-
trace::trace(typeck, location_map, flow_inits, move_data, relevant_live_locals, boring_locals);
58+
trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals);
6259

6360
// Mark regions that should be live where they appear within rvalues or within a call: like
6461
// args, regions, and types.

compiler/rustc_borrowck/src/type_check/liveness/trace.rs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Loc
77
use rustc_middle::traits::query::DropckOutlivesResult;
88
use rustc_middle::ty::relate::Relate;
99
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
10-
use rustc_mir_dataflow::ResultsCursor;
1110
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
1211
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
1312
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
13+
use rustc_mir_dataflow::{Analysis, ResultsCursor};
1414
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
1515
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
1616
use rustc_trait_selection::traits::ObligationCtxt;
@@ -37,18 +37,17 @@ use crate::type_check::{NormalizeLocation, TypeChecker};
3737
/// DROP-LIVE set are to the liveness sets for regions found in the
3838
/// `dropck_outlives` result of the variable's type (in particular,
3939
/// this respects `#[may_dangle]` annotations).
40-
pub(super) fn trace<'a, 'tcx>(
40+
pub(super) fn trace<'tcx>(
4141
typeck: &mut TypeChecker<'_, 'tcx>,
4242
location_map: &DenseLocationMap,
43-
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
4443
move_data: &MoveData<'tcx>,
4544
relevant_live_locals: Vec<Local>,
4645
boring_locals: Vec<Local>,
4746
) {
4847
let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
4948
let cx = LivenessContext {
5049
typeck,
51-
flow_inits,
50+
flow_inits: None,
5251
location_map,
5352
local_use_map,
5453
move_data,
@@ -65,7 +64,7 @@ pub(super) fn trace<'a, 'tcx>(
6564
}
6665

6766
/// Contextual state for the type-liveness coroutine.
68-
struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
67+
struct LivenessContext<'a, 'typeck, 'tcx> {
6968
/// Current type-checker, giving us our inference context etc.
7069
///
7170
/// This also stores the body we're currently analyzing.
@@ -81,8 +80,8 @@ struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
8180
drop_data: FxIndexMap<Ty<'tcx>, DropData<'tcx>>,
8281

8382
/// Results of dataflow tracking which variables (and paths) have been
84-
/// initialized.
85-
flow_inits: ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>,
83+
/// initialized. Computed lazily when needed by drop-liveness.
84+
flow_inits: Option<ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>>,
8685

8786
/// Index indicating where each variable is assigned, used, or
8887
/// dropped.
@@ -94,8 +93,8 @@ struct DropData<'tcx> {
9493
region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
9594
}
9695

97-
struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
98-
cx: LivenessContext<'a, 'typeck, 'b, 'tcx>,
96+
struct LivenessResults<'a, 'typeck, 'tcx> {
97+
cx: LivenessContext<'a, 'typeck, 'tcx>,
9998

10099
/// Set of points that define the current local.
101100
defs: DenseBitSet<PointIndex>,
@@ -116,8 +115,8 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
116115
stack: Vec<PointIndex>,
117116
}
118117

119-
impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
120-
fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self {
118+
impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> {
119+
fn new(cx: LivenessContext<'a, 'typeck, 'tcx>) -> Self {
121120
let num_points = cx.location_map.num_points();
122121
LivenessResults {
123122
cx,
@@ -459,20 +458,56 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
459458
}
460459
}
461460

462-
impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
461+
impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> {
462+
/// Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already.
463+
///
464+
/// In practice, the results of this dataflow analysis are rarely needed but can be expensive to
465+
/// compute on big functions, so we compute them lazily as a fast path when:
466+
/// - there are relevant live locals
467+
/// - there are drop points for these relevant live locals.
468+
///
469+
/// This happens as part of the drop-liveness computation: it's the only place checking for
470+
/// maybe-initializedness of `MovePathIndex`es.
471+
fn flow_inits(&mut self) -> &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>> {
472+
self.flow_inits.get_or_insert_with(|| {
473+
let tcx = self.typeck.tcx();
474+
let body = self.typeck.body;
475+
// FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s.
476+
//
477+
// This dataflow analysis computes maybe-initializedness of all move paths, which
478+
// explains why it can be expensive on big functions. But this data is only used in
479+
// drop-liveness. Therefore, most of the move paths computed here are ultimately unused,
480+
// even if the results are computed lazily and "no relevant live locals with drop
481+
// points" is the common case.
482+
//
483+
// So we only need the ones for 1) relevant live locals 2) that have drop points. That's
484+
// a much, much smaller domain: in our benchmarks, when it's not zero (the most likely
485+
// case), there are a few dozens compared to e.g. thousands or tens of thousands of
486+
// locals and move paths.
487+
let flow_inits = MaybeInitializedPlaces::new(tcx, body, self.move_data)
488+
.iterate_to_fixpoint(tcx, body, Some("borrowck"))
489+
.into_results_cursor(body);
490+
flow_inits
491+
})
492+
}
493+
}
494+
495+
impl<'tcx> LivenessContext<'_, '_, 'tcx> {
463496
fn body(&self) -> &Body<'tcx> {
464497
self.typeck.body
465498
}
499+
466500
/// Returns `true` if the local variable (or some part of it) is initialized at the current
467501
/// cursor position. Callers should call one of the `seek` methods immediately before to point
468502
/// the cursor to the desired location.
469-
fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
470-
let state = self.flow_inits.get();
503+
fn initialized_at_curr_loc(&mut self, mpi: MovePathIndex) -> bool {
504+
let flow_inits = self.flow_inits();
505+
let state = flow_inits.get();
471506
if state.contains(mpi) {
472507
return true;
473508
}
474509

475-
let move_paths = &self.flow_inits.analysis().move_data().move_paths;
510+
let move_paths = &flow_inits.analysis().move_data().move_paths;
476511
move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some()
477512
}
478513

@@ -481,7 +516,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
481516
/// DROP of some local variable will have an effect -- note that
482517
/// drops, as they may unwind, are always terminators.
483518
fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
484-
self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block));
519+
let terminator_location = self.body().terminator_loc(block);
520+
self.flow_inits().seek_before_primary_effect(terminator_location);
485521
self.initialized_at_curr_loc(mpi)
486522
}
487523

@@ -491,7 +527,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
491527
/// **Warning:** Does not account for the result of `Call`
492528
/// instructions.
493529
fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
494-
self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block));
530+
let terminator_location = self.body().terminator_loc(block);
531+
self.flow_inits().seek_after_primary_effect(terminator_location);
495532
self.initialized_at_curr_loc(mpi)
496533
}
497534

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ use rustc_middle::ty::{
3030
TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions,
3131
};
3232
use rustc_middle::{bug, span_bug};
33-
use rustc_mir_dataflow::ResultsCursor;
34-
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
3533
use rustc_mir_dataflow::move_paths::MoveData;
3634
use rustc_mir_dataflow::points::DenseLocationMap;
3735
use rustc_span::def_id::CRATE_DEF_ID;
@@ -97,10 +95,9 @@ mod relate_tys;
9795
/// - `location_table` -- for datalog polonius, the map between `Location`s and `RichLocation`s
9896
/// - `borrow_set` -- information about borrows occurring in `body`
9997
/// - `polonius_facts` -- when using Polonius, this is the generated set of Polonius facts
100-
/// - `flow_inits` -- results of a maybe-init dataflow analysis
10198
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
10299
/// - `location_map` -- map between MIR `Location` and `PointIndex`
103-
pub(crate) fn type_check<'a, 'tcx>(
100+
pub(crate) fn type_check<'tcx>(
104101
root_cx: &mut BorrowCheckRootCtxt<'tcx>,
105102
infcx: &BorrowckInferCtxt<'tcx>,
106103
body: &Body<'tcx>,
@@ -109,7 +106,6 @@ pub(crate) fn type_check<'a, 'tcx>(
109106
location_table: &PoloniusLocationTable,
110107
borrow_set: &BorrowSet<'tcx>,
111108
polonius_facts: &mut Option<PoloniusFacts>,
112-
flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
113109
move_data: &MoveData<'tcx>,
114110
location_map: Rc<DenseLocationMap>,
115111
) -> MirTypeckResults<'tcx> {
@@ -167,7 +163,7 @@ pub(crate) fn type_check<'a, 'tcx>(
167163
typeck.equate_inputs_and_outputs(&normalized_inputs_and_output);
168164
typeck.check_signature_annotation();
169165

170-
liveness::generate(&mut typeck, &location_map, flow_inits, move_data);
166+
liveness::generate(&mut typeck, &location_map, move_data);
171167

172168
let opaque_type_values =
173169
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);

tests/mir-opt/dataflow.main.maybe_init.borrowck.dot

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
digraph graph_for_def_id_0_3 {
2+
graph[fontname="Courier, monospace"];
3+
node[fontname="Courier, monospace"];
4+
edge[fontname="Courier, monospace"];
5+
bb_0[label=<<table border="1" cellborder="1" cellspacing="0" cellpadding="3" sides="rb"><tr><td colspan="3" sides="tl">bb0</td></tr><tr><td colspan="2" bgcolor="#a0a0a0" sides="tl">MIR</td><td bgcolor="#a0a0a0" sides="tl">STATE</td></tr><tr><td valign="bottom" sides="tl" align="right"></td><td valign="bottom" sides="tl" align="left">(on start)</td><td colspan="1" valign="bottom" sides="tl" align="left">{_0}</td></tr><tr><td valign="top" sides="tl" bgcolor="#f0f0f0" align="right">0</td><td valign="top" sides="tl" bgcolor="#f0f0f0" align="left">_0 = const ()</td><td valign="top" sides="tl" bgcolor="#f0f0f0" align="left"><font color="red">-_0</font></td></tr><tr><td valign="top" sides="tl" align="right">T</td><td valign="top" sides="tl" align="left">return</td><td valign="top" sides="tl" align="left"></td></tr><tr><td valign="bottom" sides="tl" bgcolor="#f0f0f0" align="right"></td><td valign="bottom" sides="tl" bgcolor="#f0f0f0" align="left">(on end)</td><td colspan="1" valign="bottom" sides="tl" bgcolor="#f0f0f0" align="left">{}</td></tr></table>>][shape="none"];
6+
}

tests/mir-opt/dataflow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
// Test graphviz dataflow output
33
//@ compile-flags: -Z dump-mir=main -Z dump-mir-dataflow
44

5-
// EMIT_MIR dataflow.main.maybe_init.borrowck.dot
5+
// EMIT_MIR dataflow.main.maybe_uninit.borrowck.dot
66
fn main() {}

0 commit comments

Comments
 (0)