@@ -66,7 +66,8 @@ use std::mem;
66
66
use crate :: transform:: { MirPass , MirSource } ;
67
67
use crate :: transform:: simplify;
68
68
use crate :: transform:: no_landing_pads:: no_landing_pads;
69
- use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location} ;
69
+ use crate :: dataflow:: { DataflowResults } ;
70
+ use crate :: dataflow:: { do_dataflow, DebugFormatted , state_for_location, for_each_location} ;
70
71
use crate :: dataflow:: { MaybeStorageLive , HaveBeenBorrowedLocals } ;
71
72
use crate :: util:: dump_mir;
72
73
use crate :: util:: liveness;
@@ -400,6 +401,7 @@ fn locals_live_across_suspend_points(
400
401
movable : bool ,
401
402
) -> (
402
403
liveness:: LiveVarSet ,
404
+ IndexVec < GeneratorSavedLocal , BitSet < GeneratorSavedLocal > > ,
403
405
FxHashMap < BasicBlock , liveness:: LiveVarSet > ,
404
406
BitSet < BasicBlock > ,
405
407
) {
@@ -503,7 +505,99 @@ fn locals_live_across_suspend_points(
503
505
// The generator argument is ignored
504
506
set. remove ( self_arg ( ) ) ;
505
507
506
- ( set, storage_liveness_map, suspending_blocks)
508
+ let storage_conflicts = compute_storage_conflicts (
509
+ body,
510
+ & set,
511
+ & ignored,
512
+ storage_live,
513
+ storage_live_analysis) ;
514
+
515
+ ( set, storage_conflicts, storage_liveness_map, suspending_blocks)
516
+ }
517
+
518
+ /// For every saved local, looks for which locals are StorageLive at the same
519
+ /// time. Generates a bitset for every local of all the other locals that may be
520
+ /// StorageLive simultaneously with that local. This is used in the layout
521
+ /// computation; see `GeneratorLayout` for more.
522
+ fn compute_storage_conflicts (
523
+ body : & ' mir Body < ' tcx > ,
524
+ stored_locals : & liveness:: LiveVarSet ,
525
+ ignored : & StorageIgnored ,
526
+ storage_live : DataflowResults < ' tcx , MaybeStorageLive < ' mir , ' tcx > > ,
527
+ storage_live_analysis : MaybeStorageLive < ' mir , ' tcx > ,
528
+ ) -> IndexVec < GeneratorSavedLocal , BitSet < GeneratorSavedLocal > > {
529
+ debug ! ( "compute_storage_conflicts({:?})" , body. span) ;
530
+ debug ! ( "ignored = {:?}" , ignored. 0 ) ;
531
+
532
+ // Storage ignored locals are not eligible for overlap, since their storage
533
+ // is always live.
534
+ let mut ineligible_locals = ignored. 0 . clone ( ) ;
535
+ ineligible_locals. intersect ( & stored_locals) ;
536
+
537
+ // Of our remaining candidates, find out if any have overlapping storage
538
+ // liveness. Those that do must be in the same variant to remain candidates.
539
+ // FIXME(tmandry): Consider using sparse bitsets here once we have good
540
+ // benchmarks for generators.
541
+ let mut local_conflicts: IndexVec < Local , _ > =
542
+ // Add conflicts for every ineligible local.
543
+ iter:: repeat ( ineligible_locals. clone ( ) )
544
+ . take ( body. local_decls . len ( ) )
545
+ . collect ( ) ;
546
+
547
+ for_each_location ( body, & storage_live_analysis, & storage_live, |state, loc| {
548
+ let mut eligible_storage_live = state. clone ( ) . to_dense ( ) ;
549
+ eligible_storage_live. intersect ( & stored_locals) ;
550
+
551
+ for local in eligible_storage_live. iter ( ) {
552
+ let mut overlaps = eligible_storage_live. clone ( ) ;
553
+ overlaps. remove ( local) ;
554
+ local_conflicts[ local] . union ( & overlaps) ;
555
+
556
+ if !overlaps. is_empty ( ) {
557
+ trace ! ( "at {:?}, local {:?} conflicts with {:?}" ,
558
+ loc, local, overlaps) ;
559
+ }
560
+ }
561
+ } ) ;
562
+
563
+ // NOTE: Today we store a full conflict bitset for every local. Technically
564
+ // this is twice as many bits as we need, since the relation is symmetric.
565
+ // However, in practice these bitsets are not usually large. The layout code
566
+ // also needs to keep track of how many conflicts each local has, so it's
567
+ // simpler to keep it this way for now.
568
+ let storage_conflicts: IndexVec < GeneratorSavedLocal , _ > = stored_locals
569
+ . iter ( )
570
+ . map ( |local_a| {
571
+ if ineligible_locals. contains ( local_a) {
572
+ // Conflicts with everything.
573
+ BitSet :: new_filled ( stored_locals. count ( ) )
574
+ } else {
575
+ // Keep overlap information only for stored locals.
576
+ renumber_bitset ( & local_conflicts[ local_a] , stored_locals)
577
+ }
578
+ } )
579
+ . collect ( ) ;
580
+
581
+ storage_conflicts
582
+ }
583
+
584
+ /// Renumbers the items present in `stored_locals` and applies the renumbering
585
+ /// to 'input`.
586
+ ///
587
+ /// For example, if `stored_locals = [1, 3, 5]`, this would be renumbered to
588
+ /// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`.
589
+ fn renumber_bitset ( input : & BitSet < Local > , stored_locals : & liveness:: LiveVarSet )
590
+ -> BitSet < GeneratorSavedLocal > {
591
+ assert ! ( stored_locals. superset( & input) , "{:?} not a superset of {:?}" , stored_locals, input) ;
592
+ let mut out = BitSet :: new_empty ( stored_locals. count ( ) ) ;
593
+ for ( idx, local) in stored_locals. iter ( ) . enumerate ( ) {
594
+ let saved_local = GeneratorSavedLocal :: from ( idx) ;
595
+ if input. contains ( local) {
596
+ out. insert ( saved_local) ;
597
+ }
598
+ }
599
+ debug ! ( "renumber_bitset({:?}, {:?}) => {:?}" , input, stored_locals, out) ;
600
+ out
507
601
}
508
602
509
603
fn compute_layout < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
@@ -517,7 +611,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
517
611
FxHashMap < BasicBlock , liveness:: LiveVarSet > )
518
612
{
519
613
// Use a liveness analysis to compute locals which are live across a suspension point
520
- let ( live_locals, storage_liveness, suspending_blocks) =
614
+ let ( live_locals, storage_conflicts , storage_liveness, suspending_blocks) =
521
615
locals_live_across_suspend_points ( tcx, body, source, movable) ;
522
616
523
617
// Erase regions from the types passed in from typeck so we can compare them with
@@ -547,7 +641,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
547
641
548
642
let dummy_local = LocalDecl :: new_internal ( tcx. mk_unit ( ) , body. span ) ;
549
643
550
- // Gather live locals and their indices replacing values in mir .local_decls with a dummy
644
+ // Gather live locals and their indices replacing values in body .local_decls with a dummy
551
645
// to avoid changing local indices
552
646
let live_decls = live_locals. iter ( ) . map ( |local| {
553
647
let var = mem:: replace ( & mut body. local_decls [ local] , dummy_local. clone ( ) ) ;
@@ -568,6 +662,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
568
662
remap. insert ( local, ( var. ty , variant_index, idx) ) ;
569
663
decls. push ( var) ;
570
664
}
665
+ debug ! ( "generator saved local mappings: {:?}" , decls) ;
571
666
let field_tys = decls. iter ( ) . map ( |field| field. ty ) . collect :: < IndexVec < _ , _ > > ( ) ;
572
667
573
668
// Put every var in each variant, for now.
@@ -578,6 +673,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
578
673
let layout = GeneratorLayout {
579
674
field_tys,
580
675
variant_fields : empty_variants. chain ( state_variants) . collect ( ) ,
676
+ storage_conflicts,
581
677
__local_debuginfo_codegen_only_do_not_use : decls,
582
678
} ;
583
679
0 commit comments