Skip to content

Commit 9de451c

Browse files
committed
Only include generator saved locals in the variants that need them
1 parent 7868758 commit 9de451c

File tree

1 file changed

+48
-34
lines changed

1 file changed

+48
-34
lines changed

src/librustc_mir/transform/generator.rs

+48-34
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,9 @@ fn locals_live_across_suspend_points(
401401
movable: bool,
402402
) -> (
403403
liveness::LiveVarSet,
404+
Vec<BitSet<GeneratorSavedLocal>>,
404405
IndexVec<GeneratorSavedLocal, BitSet<GeneratorSavedLocal>>,
405406
FxHashMap<BasicBlock, liveness::LiveVarSet>,
406-
BitSet<BasicBlock>,
407407
) {
408408
let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
409409
let def_id = source.def_id();
@@ -447,13 +447,10 @@ fn locals_live_across_suspend_points(
447447
);
448448

449449
let mut storage_liveness_map = FxHashMap::default();
450-
451-
let mut suspending_blocks = BitSet::new_empty(body.basic_blocks().len());
450+
let mut live_locals_at_suspension_points = Vec::new();
452451

453452
for (block, data) in body.basic_blocks().iter_enumerated() {
454453
if let TerminatorKind::Yield { .. } = data.terminator().kind {
455-
suspending_blocks.insert(block);
456-
457454
let loc = Location {
458455
block: block,
459456
statement_index: data.statements.len(),
@@ -494,16 +491,25 @@ fn locals_live_across_suspend_points(
494491
// and their storage is live (the `storage_liveness` variable)
495492
storage_liveness.intersect(&liveness.outs[block]);
496493

494+
// The generator argument is ignored
495+
storage_liveness.remove(self_arg());
496+
497497
let live_locals = storage_liveness;
498498

499-
// Add the locals life at this suspension point to the set of locals which live across
499+
// Add the locals live at this suspension point to the set of locals which live across
500500
// any suspension points
501501
set.union(&live_locals);
502+
503+
live_locals_at_suspension_points.push(live_locals);
502504
}
503505
}
504506

505-
// The generator argument is ignored
506-
set.remove(self_arg());
507+
// Renumber our liveness_map bitsets to include only the locals we are
508+
// saving.
509+
let live_locals_at_suspension_points = live_locals_at_suspension_points
510+
.iter()
511+
.map(|live_locals| renumber_bitset(&live_locals, &set))
512+
.collect();
507513

508514
let storage_conflicts = compute_storage_conflicts(
509515
body,
@@ -512,7 +518,7 @@ fn locals_live_across_suspend_points(
512518
storage_live,
513519
storage_live_analysis);
514520

515-
(set, storage_conflicts, storage_liveness_map, suspending_blocks)
521+
(set, live_locals_at_suspension_points, storage_conflicts, storage_liveness_map)
516522
}
517523

518524
/// For every saved local, looks for which locals are StorageLive at the same
@@ -611,7 +617,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
611617
FxHashMap<BasicBlock, liveness::LiveVarSet>)
612618
{
613619
// Use a liveness analysis to compute locals which are live across a suspension point
614-
let (live_locals, storage_conflicts, storage_liveness, suspending_blocks) =
620+
let (live_locals, live_locals_at_suspension_points, storage_conflicts, storage_liveness) =
615621
locals_live_across_suspend_points(tcx, body, source, movable);
616622

617623
// Erase regions from the types passed in from typeck so we can compare them with
@@ -641,38 +647,46 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
641647

642648
let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), body.span);
643649

644-
// Gather live locals and their indices replacing values in body.local_decls with a dummy
645-
// to avoid changing local indices
646-
let live_decls = live_locals.iter().map(|local| {
650+
// Gather live locals and their indices replacing values in body.local_decls
651+
// with a dummy to avoid changing local indices.
652+
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
653+
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
654+
let mut decls = IndexVec::<GeneratorSavedLocal, _>::new();
655+
for (idx, local) in live_locals.iter().enumerate() {
647656
let var = mem::replace(&mut body.local_decls[local], dummy_local.clone());
648-
(local, var)
649-
});
657+
locals.push(local);
658+
tys.push(var.ty);
659+
decls.push(var);
660+
debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
661+
}
650662

651-
// For now we will access everything via variant #3, leaving empty variants
652-
// for the UNRESUMED, RETURNED, and POISONED states.
653-
// If there were a yield-less generator without a variant #3, it would not
654-
// have any vars to remap, so we would never use this.
655-
let variant_index = VariantIdx::new(3);
663+
// Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
664+
const RESERVED_VARIANTS: usize = 3;
656665

666+
// Build the generator variant field list.
657667
// Create a map from local indices to generator struct indices.
658-
// We also create a vector of the LocalDecls of these locals.
668+
let mut variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>> =
669+
iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect();
659670
let mut remap = FxHashMap::default();
660-
let mut decls = IndexVec::new();
661-
for (idx, (local, var)) in live_decls.enumerate() {
662-
remap.insert(local, (var.ty, variant_index, idx));
663-
decls.push(var);
671+
for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
672+
let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx);
673+
let mut fields = IndexVec::new();
674+
for (idx, saved_local) in live_locals.iter().enumerate() {
675+
fields.push(saved_local);
676+
// Note that if a field is included in multiple variants, it will be
677+
// added overwritten here. That's fine; fields do not move around
678+
// inside generators, so it doesn't matter which variant index we
679+
// access them by.
680+
remap.insert(locals[saved_local], (tys[saved_local], variant_index, idx));
681+
}
682+
variant_fields.push(fields);
664683
}
665-
debug!("generator saved local mappings: {:?}", decls);
666-
let field_tys = decls.iter().map(|field| field.ty).collect::<IndexVec<_, _>>();
667-
668-
// Put every var in each variant, for now.
669-
let all_vars = (0..field_tys.len()).map(GeneratorSavedLocal::from).collect();
670-
let empty_variants = iter::repeat(IndexVec::new()).take(3);
671-
let state_variants = iter::repeat(all_vars).take(suspending_blocks.count());
684+
debug!("generator variant_fields = {:?}", variant_fields);
685+
debug!("generator storage_conflicts = {:?}", storage_conflicts);
672686

673687
let layout = GeneratorLayout {
674-
field_tys,
675-
variant_fields: empty_variants.chain(state_variants).collect(),
688+
field_tys: tys,
689+
variant_fields,
676690
storage_conflicts,
677691
__local_debuginfo_codegen_only_do_not_use: decls,
678692
};

0 commit comments

Comments
 (0)