@@ -7,10 +7,10 @@ use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Loc
77use rustc_middle:: traits:: query:: DropckOutlivesResult ;
88use rustc_middle:: ty:: relate:: Relate ;
99use rustc_middle:: ty:: { Ty , TyCtxt , TypeVisitable , TypeVisitableExt } ;
10- use rustc_mir_dataflow:: ResultsCursor ;
1110use rustc_mir_dataflow:: impls:: MaybeInitializedPlaces ;
1211use rustc_mir_dataflow:: move_paths:: { HasMoveData , MoveData , MovePathIndex } ;
1312use rustc_mir_dataflow:: points:: { DenseLocationMap , PointIndex } ;
13+ use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
1414use rustc_span:: { DUMMY_SP , ErrorGuaranteed , Span } ;
1515use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
1616use 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
0 commit comments