5050//! Otherwise it drops all the values in scope at the last suspension point.
5151
5252use crate :: dataflow:: impls:: {
53- MaybeBorrowedLocals , MaybeLiveLocals , MaybeRequiresStorage , MaybeStorageLive ,
53+ MaybeBorrowedLocals , MaybeInitializedLocals , MaybeLiveLocals , MaybeStorageLive ,
5454} ;
5555use crate :: dataflow:: { self , Analysis } ;
5656use crate :: transform:: no_landing_pads:: no_landing_pads;
@@ -444,86 +444,74 @@ fn locals_live_across_suspend_points(
444444 movable : bool ,
445445) -> LivenessInfo {
446446 let def_id = source. def_id ( ) ;
447- let body_ref: & Body < ' _ > = & body;
448447
449448 // Calculate when MIR locals have live storage. This gives us an upper bound of their
450449 // lifetimes.
451450 let mut storage_live = MaybeStorageLive :: new ( always_live_locals. clone ( ) )
452- . into_engine ( tcx, body_ref , def_id)
451+ . into_engine ( tcx, body , def_id)
453452 . iterate_to_fixpoint ( )
454- . into_results_cursor ( body_ref) ;
455-
456- // Calculate the MIR locals which have been previously
457- // borrowed (even if they are still active).
458- let borrowed_locals_results =
459- MaybeBorrowedLocals :: all_borrows ( ) . into_engine ( tcx, body_ref, def_id) . iterate_to_fixpoint ( ) ;
460-
461- let mut borrowed_locals_cursor =
462- dataflow:: ResultsCursor :: new ( body_ref, & borrowed_locals_results) ;
463-
464- // Calculate the MIR locals that we actually need to keep storage around
465- // for.
466- let requires_storage_results = MaybeRequiresStorage :: new ( body, & borrowed_locals_results)
467- . into_engine ( tcx, body_ref, def_id)
468- . iterate_to_fixpoint ( ) ;
469- let mut requires_storage_cursor =
470- dataflow:: ResultsCursor :: new ( body_ref, & requires_storage_results) ;
471-
472- // Calculate the liveness of MIR locals ignoring borrows.
473- let mut liveness = MaybeLiveLocals
474- . into_engine ( tcx, body_ref, def_id)
453+ . into_results_cursor ( body) ;
454+
455+ let mut init = MaybeInitializedLocals
456+ . into_engine ( tcx, body, def_id)
475457 . iterate_to_fixpoint ( )
476- . into_results_cursor ( body_ref ) ;
458+ . into_results_cursor ( body ) ;
477459
478- let mut storage_liveness_map = IndexVec :: from_elem ( None , body. basic_blocks ( ) ) ;
479- let mut live_locals_at_suspension_points = Vec :: new ( ) ;
480- let mut live_locals_at_any_suspension_point = BitSet :: new_empty ( body. local_decls . len ( ) ) ;
460+ let mut live = MaybeLiveLocals
461+ . into_engine ( tcx, body, def_id)
462+ . iterate_to_fixpoint ( )
463+ . into_results_cursor ( body) ;
481464
482- for ( block, data) in body. basic_blocks ( ) . iter_enumerated ( ) {
483- if let TerminatorKind :: Yield { .. } = data. terminator ( ) . kind {
484- let loc = Location { block, statement_index : data. statements . len ( ) } ;
485-
486- liveness. seek_to_block_end ( block) ;
487- let mut live_locals = liveness. get ( ) . clone ( ) ;
488-
489- if !movable {
490- // The `liveness` variable contains the liveness of MIR locals ignoring borrows.
491- // This is correct for movable generators since borrows cannot live across
492- // suspension points. However for immovable generators we need to account for
493- // borrows, so we conseratively assume that all borrowed locals are live until
494- // we find a StorageDead statement referencing the locals.
495- // To do this we just union our `liveness` result with `borrowed_locals`, which
496- // contains all the locals which has been borrowed before this suspension point.
497- // If a borrow is converted to a raw reference, we must also assume that it lives
498- // forever. Note that the final liveness is still bounded by the storage liveness
499- // of the local, which happens using the `intersect` operation below.
500- borrowed_locals_cursor. seek_before_primary_effect ( loc) ;
501- live_locals. union ( borrowed_locals_cursor. get ( ) ) ;
502- }
465+ let mut borrowed = MaybeBorrowedLocals :: all_borrows ( )
466+ . into_engine ( tcx, body, def_id)
467+ . iterate_to_fixpoint ( )
468+ . into_results_cursor ( body) ;
503469
504- // Store the storage liveness for later use so we can restore the state
505- // after a suspension point
506- storage_live. seek_before_primary_effect ( loc) ;
507- storage_liveness_map[ block] = Some ( storage_live. get ( ) . clone ( ) ) ;
470+ // Liveness across yield points is determined by the following boolean equation, where `live`,
471+ // `init` and `borrowed` come from dataflow and `movable` is a property of the generator.
472+ // Movable generators do not allow borrows to live across yield points, so they don't need to
473+ // store a local simply because it is borrowed.
474+ //
475+ // live_across_yield := (live & init) | (!movable & borrowed)
476+ //
477+ let mut locals_live_across_yield_point = |block| {
478+ live. seek_to_block_end ( block) ;
479+ let mut live_locals = live. get ( ) . clone ( ) ;
508480
509- // Locals live are live at this point only if they are used across
510- // suspension points (the `liveness` variable)
511- // and their storage is required (the `storage_required` variable)
512- requires_storage_cursor. seek_before_primary_effect ( loc) ;
513- live_locals. intersect ( requires_storage_cursor. get ( ) ) ;
481+ init. seek_to_block_end ( block) ;
482+ live_locals. intersect ( init. get ( ) ) ;
514483
515- // The generator argument is ignored.
516- live_locals. remove ( SELF_ARG ) ;
484+ if !movable {
485+ borrowed. seek_to_block_end ( block) ;
486+ live_locals. union ( borrowed. get ( ) ) ;
487+ }
517488
518- debug ! ( "loc = {:?}, live_locals = {:?}" , loc, live_locals) ;
489+ live_locals
490+ } ;
519491
520- // Add the locals live at this suspension point to the set of locals which live across
521- // any suspension points
522- live_locals_at_any_suspension_point . union ( & live_locals ) ;
492+ let mut storage_liveness_map = IndexVec :: from_elem ( None , body . basic_blocks ( ) ) ;
493+ let mut live_locals_at_suspension_points = Vec :: new ( ) ;
494+ let mut live_locals_at_any_suspension_point = BitSet :: new_empty ( body . local_decls . len ( ) ) ;
523495
524- live_locals_at_suspension_points. push ( live_locals) ;
496+ for ( block, data) in body. basic_blocks ( ) . iter_enumerated ( ) {
497+ if !matches ! ( data. terminator( ) . kind, TerminatorKind :: Yield { .. } ) {
498+ continue ;
525499 }
500+
501+ // Store the storage liveness for later use so we can restore the state
502+ // after a suspension point
503+ storage_live. seek_to_block_end ( block) ;
504+ storage_liveness_map[ block] = Some ( storage_live. get ( ) . clone ( ) ) ;
505+
506+ let mut live_locals = locals_live_across_yield_point ( block) ;
507+
508+ // Ignore the generator's `self` argument since it is handled seperately.
509+ live_locals. remove ( SELF_ARG ) ;
510+ debug ! ( "block = {:?}, live_locals = {:?}" , block, live_locals) ;
511+ live_locals_at_any_suspension_point. union ( & live_locals) ;
512+ live_locals_at_suspension_points. push ( live_locals) ;
526513 }
514+
527515 debug ! ( "live_locals_anywhere = {:?}" , live_locals_at_any_suspension_point) ;
528516
529517 // Renumber our liveness_map bitsets to include only the locals we are
@@ -534,10 +522,11 @@ fn locals_live_across_suspend_points(
534522 . collect ( ) ;
535523
536524 let storage_conflicts = compute_storage_conflicts (
537- body_ref ,
525+ body ,
538526 & live_locals_at_any_suspension_point,
539527 always_live_locals. clone ( ) ,
540- requires_storage_results,
528+ init,
529+ borrowed,
541530 ) ;
542531
543532 LivenessInfo {
@@ -569,6 +558,33 @@ fn renumber_bitset(
569558 out
570559}
571560
561+ /// Record conflicts between locals at the current dataflow cursor positions.
562+ ///
563+ /// You need to seek the cursors before calling this function.
564+ fn record_conflicts_at_curr_loc (
565+ local_conflicts : & mut BitMatrix < Local , Local > ,
566+ init : & dataflow:: ResultsCursor < ' mir , ' tcx , MaybeInitializedLocals > ,
567+ borrowed : & dataflow:: ResultsCursor < ' mir , ' tcx , MaybeBorrowedLocals > ,
568+ ) {
569+ // A local requires storage if it is initialized or borrowed. For now, a local
570+ // becomes uninitialized if it is moved from, but is still considered "borrowed".
571+ //
572+ // requires_storage := init | borrowed
573+ //
574+ // FIXME: This function is called in a loop, so it might be better to pass in a temporary
575+ // bitset rather than cloning here.
576+ let mut requires_storage = init. get ( ) . clone ( ) ;
577+ requires_storage. union ( borrowed. get ( ) ) ;
578+
579+ for local in requires_storage. iter ( ) {
580+ local_conflicts. union_row_with ( & requires_storage, local) ;
581+ }
582+
583+ if requires_storage. count ( ) > 1 {
584+ trace ! ( "requires_storage={:?}" , requires_storage) ;
585+ }
586+ }
587+
572588/// For every saved local, looks for which locals are StorageLive at the same
573589/// time. Generates a bitset for every local of all the other locals that may be
574590/// StorageLive simultaneously with that local. This is used in the layout
@@ -577,30 +593,40 @@ fn compute_storage_conflicts(
577593 body : & ' mir Body < ' tcx > ,
578594 stored_locals : & BitSet < Local > ,
579595 always_live_locals : storage:: AlwaysLiveLocals ,
580- requires_storage : dataflow:: Results < ' tcx , MaybeRequiresStorage < ' mir , ' tcx > > ,
596+ mut init : dataflow:: ResultsCursor < ' mir , ' tcx , MaybeInitializedLocals > ,
597+ mut borrowed : dataflow:: ResultsCursor < ' mir , ' tcx , MaybeBorrowedLocals > ,
581598) -> BitMatrix < GeneratorSavedLocal , GeneratorSavedLocal > {
582- assert_eq ! ( body. local_decls. len( ) , stored_locals. domain_size( ) ) ;
583-
584599 debug ! ( "compute_storage_conflicts({:?})" , body. span) ;
585- debug ! ( "always_live = {:?}" , always_live_locals) ;
586-
587- // Locals that are always live or ones that need to be stored across
588- // suspension points are not eligible for overlap.
589- let mut ineligible_locals = always_live_locals. into_inner ( ) ;
590- ineligible_locals. intersect ( stored_locals) ;
600+ assert_eq ! ( body. local_decls. len( ) , stored_locals. domain_size( ) ) ;
591601
592- // Compute the storage conflicts for all eligible locals.
593- let mut visitor = StorageConflictVisitor {
594- body,
595- stored_locals : & stored_locals,
596- local_conflicts : BitMatrix :: from_row_n ( & ineligible_locals, body. local_decls . len ( ) ) ,
597- } ;
602+ // Locals that are always live conflict with all other locals.
603+ //
604+ // FIXME: Why do we need to handle locals without `Storage{Live,Dead}` specially here?
605+ // Shouldn't it be enough to know whether they are initialized?
606+ let always_live_locals = always_live_locals. into_inner ( ) ;
607+ let mut local_conflicts = BitMatrix :: from_row_n ( & always_live_locals, body. local_decls . len ( ) ) ;
608+
609+ // Visit every reachable statement and terminator. The exact order does not matter. When two
610+ // locals are live at the same point in time, add an entry in the conflict matrix.
611+ for ( block, data) in traversal:: preorder ( body) {
612+ // Ignore unreachable blocks.
613+ if data. terminator ( ) . kind == TerminatorKind :: Unreachable {
614+ continue ;
615+ }
598616
599- // Visit only reachable basic blocks. The exact order is not important.
600- let reachable_blocks = traversal:: preorder ( body) . map ( |( bb, _) | bb) ;
601- requires_storage. visit_with ( body, reachable_blocks, & mut visitor) ;
617+ for ( statement_index, _) in data. statements . iter ( ) . enumerate ( ) {
618+ let loc = Location { block, statement_index } ;
619+ trace ! ( "record conflicts at {:?}" , loc) ;
620+ init. seek_before_primary_effect ( loc) ;
621+ borrowed. seek_before_primary_effect ( loc) ;
622+ record_conflicts_at_curr_loc ( & mut local_conflicts, & init, & borrowed) ;
623+ }
602624
603- let local_conflicts = visitor. local_conflicts ;
625+ trace ! ( "record conflicts at end of {:?}" , block) ;
626+ init. seek_to_block_end ( block) ;
627+ borrowed. seek_to_block_end ( block) ;
628+ record_conflicts_at_curr_loc ( & mut local_conflicts, & init, & borrowed) ;
629+ }
604630
605631 // Compress the matrix using only stored locals (Local -> GeneratorSavedLocal).
606632 //
@@ -612,7 +638,7 @@ fn compute_storage_conflicts(
612638 let mut storage_conflicts = BitMatrix :: new ( stored_locals. count ( ) , stored_locals. count ( ) ) ;
613639 for ( idx_a, local_a) in stored_locals. iter ( ) . enumerate ( ) {
614640 let saved_local_a = GeneratorSavedLocal :: new ( idx_a) ;
615- if ineligible_locals . contains ( local_a) {
641+ if always_live_locals . contains ( local_a) {
616642 // Conflicts with everything.
617643 storage_conflicts. insert_all_into_row ( saved_local_a) ;
618644 } else {
@@ -628,56 +654,6 @@ fn compute_storage_conflicts(
628654 storage_conflicts
629655}
630656
631- struct StorageConflictVisitor < ' mir , ' tcx , ' s > {
632- body : & ' mir Body < ' tcx > ,
633- stored_locals : & ' s BitSet < Local > ,
634- // FIXME(tmandry): Consider using sparse bitsets here once we have good
635- // benchmarks for generators.
636- local_conflicts : BitMatrix < Local , Local > ,
637- }
638-
639- impl dataflow:: ResultsVisitor < ' mir , ' tcx > for StorageConflictVisitor < ' mir , ' tcx , ' _ > {
640- type FlowState = BitSet < Local > ;
641-
642- fn visit_statement_before_primary_effect (
643- & mut self ,
644- state : & Self :: FlowState ,
645- _statement : & ' mir Statement < ' tcx > ,
646- loc : Location ,
647- ) {
648- self . apply_state ( state, loc) ;
649- }
650-
651- fn visit_terminator_before_primary_effect (
652- & mut self ,
653- state : & Self :: FlowState ,
654- _terminator : & ' mir Terminator < ' tcx > ,
655- loc : Location ,
656- ) {
657- self . apply_state ( state, loc) ;
658- }
659- }
660-
661- impl < ' body , ' tcx , ' s > StorageConflictVisitor < ' body , ' tcx , ' s > {
662- fn apply_state ( & mut self , flow_state : & BitSet < Local > , loc : Location ) {
663- // Ignore unreachable blocks.
664- if self . body . basic_blocks ( ) [ loc. block ] . terminator ( ) . kind == TerminatorKind :: Unreachable {
665- return ;
666- }
667-
668- let mut eligible_storage_live = flow_state. clone ( ) ;
669- eligible_storage_live. intersect ( & self . stored_locals ) ;
670-
671- for local in eligible_storage_live. iter ( ) {
672- self . local_conflicts . union_row_with ( & eligible_storage_live, local) ;
673- }
674-
675- if eligible_storage_live. count ( ) > 1 {
676- trace ! ( "at {:?}, eligible_storage_live={:?}" , loc, eligible_storage_live) ;
677- }
678- }
679- }
680-
681657fn compute_layout < ' tcx > (
682658 tcx : TyCtxt < ' tcx > ,
683659 source : MirSource < ' tcx > ,
0 commit comments