Skip to content

Commit e27b6fd

Browse files
committed
Overlap locals that never have storage live at the same time
1 parent 140a5ac commit e27b6fd

File tree

3 files changed

+209
-23
lines changed

3 files changed

+209
-23
lines changed

src/librustc_mir/dataflow/mod.rs

+39
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,45 @@ pub fn state_for_location<'tcx, T: BitDenotation<'tcx>>(loc: Location,
381381
gen_set.to_dense()
382382
}
383383

384+
pub fn for_each_location<'tcx, T: BitDenotation<'tcx>>(
385+
block: BasicBlock,
386+
analysis: &T,
387+
result: &DataflowResults<'tcx, T>,
388+
mir: &Mir<'tcx>,
389+
mut f: impl FnMut(&HybridBitSet<T::Idx>, Location)
390+
) {
391+
let statements = &mir[block].statements;
392+
393+
let mut on_entry = result.sets().on_entry_set_for(block.index()).to_owned();
394+
let mut kill_set = on_entry.to_hybrid();
395+
let mut gen_set = kill_set.clone();
396+
397+
{
398+
let mut sets = BlockSets {
399+
on_entry: &mut on_entry,
400+
kill_set: &mut kill_set,
401+
gen_set: &mut gen_set,
402+
};
403+
// FIXME: This location is technically wrong, but there isn't a way to
404+
// denote the start of a block.
405+
f(sets.gen_set, Location { block, statement_index: 0 });
406+
407+
for statement_index in 0..statements.len() {
408+
let loc = Location { block, statement_index };
409+
analysis.before_statement_effect(&mut sets, loc);
410+
f(sets.gen_set, loc);
411+
analysis.statement_effect(&mut sets, loc);
412+
f(sets.gen_set, loc);
413+
}
414+
415+
let term_loc = Location { block, statement_index: mir[block].statements.len() };
416+
analysis.before_terminator_effect(&mut sets, term_loc);
417+
f(sets.gen_set, term_loc);
418+
analysis.before_statement_effect(&mut sets, term_loc);
419+
f(sets.gen_set, term_loc);
420+
}
421+
}
422+
384423
pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation<'tcx>
385424
{
386425
flow_state: DataflowState<'tcx, O>,

src/librustc_mir/transform/generator.rs

+153-23
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ use rustc::ty::GeneratorSubsts;
5858
use rustc::ty::layout::VariantIdx;
5959
use rustc::ty::subst::SubstsRef;
6060
use rustc_data_structures::fx::FxHashMap;
61-
use rustc_data_structures::indexed_vec::Idx;
61+
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
6262
use rustc_data_structures::bit_set::BitSet;
6363
use std::borrow::Cow;
64+
use std::collections::hash_map;
6465
use std::iter;
6566
use std::mem;
6667
use crate::transform::{MirPass, MirSource};
6768
use crate::transform::simplify;
6869
use crate::transform::no_landing_pads::no_landing_pads;
69-
use crate::dataflow::{do_dataflow, DebugFormatted, state_for_location};
70+
use crate::dataflow::{DataflowResults};
71+
//use crate::dataflow::{DataflowResults, DataflowResultsConsumer};
72+
//use crate::dataflow::FlowAtLocation;
73+
use crate::dataflow::{do_dataflow, DebugFormatted, state_for_location, for_each_location};
7074
use crate::dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
7175
use crate::util::dump_mir;
7276
use crate::util::liveness;
@@ -392,6 +396,8 @@ fn locals_live_across_suspend_points(
392396
source: MirSource<'tcx>,
393397
movable: bool,
394398
) -> (
399+
liveness::LiveVarSet,
400+
FxHashMap<BasicBlock, liveness::LiveVarSet>,
395401
liveness::LiveVarSet,
396402
FxHashMap<BasicBlock, liveness::LiveVarSet>,
397403
BitSet<BasicBlock>,
@@ -496,7 +502,123 @@ fn locals_live_across_suspend_points(
496502
// The generator argument is ignored
497503
set.remove(self_arg());
498504

499-
(set, storage_liveness_map, suspending_blocks)
505+
let (variant_assignments, prefix_locals) = locals_eligible_for_overlap(
506+
mir,
507+
&set,
508+
&storage_liveness_map,
509+
&ignored,
510+
storage_live,
511+
storage_live_analysis);
512+
513+
(set, variant_assignments, prefix_locals, storage_liveness_map, suspending_blocks)
514+
}
515+
516+
fn locals_eligible_for_overlap(
517+
mir: &'mir Mir<'tcx>,
518+
stored_locals: &liveness::LiveVarSet,
519+
storage_liveness_map: &FxHashMap<BasicBlock, liveness::LiveVarSet>,
520+
ignored: &StorageIgnored,
521+
storage_live: DataflowResults<'tcx, MaybeStorageLive<'mir, 'tcx>>,
522+
storage_live_analysis: MaybeStorageLive<'mir, 'tcx>,
523+
) -> (FxHashMap<BasicBlock, liveness::LiveVarSet>, liveness::LiveVarSet) {
524+
debug!("locals_eligible_for_overlap({:?})", mir.span);
525+
debug!("ignored = {:?}", ignored.0);
526+
527+
let mut candidate_locals = stored_locals.clone();
528+
// Storage ignored locals are not candidates, since their storage is always
529+
// live. Remove these.
530+
candidate_locals.subtract(&ignored.0);
531+
532+
let mut eligible_locals = candidate_locals.clone();
533+
debug!("eligible_locals = {:?}", eligible_locals);
534+
535+
// Figure out which of our candidate locals are storage live across only one
536+
// suspension point. There will be an entry for each candidate (`None` if it
537+
// is live across more than one).
538+
let mut variants_map: FxHashMap<Local, Option<BasicBlock>> = FxHashMap::default();
539+
for (block, locals) in storage_liveness_map {
540+
let mut live_locals = candidate_locals.clone();
541+
live_locals.intersect(&locals);
542+
for local in live_locals.iter() {
543+
match variants_map.entry(local) {
544+
hash_map::Entry::Occupied(mut entry) => {
545+
// We've already seen this local at another suspension
546+
// point, so it is no longer a candidate.
547+
debug!("removing local {:?} with live storage across >1 yield ({:?}, {:?})",
548+
local, block, entry.get());
549+
*entry.get_mut() = None;
550+
eligible_locals.remove(local);
551+
}
552+
hash_map::Entry::Vacant(entry) => {
553+
entry.insert(Some(*block));
554+
}
555+
}
556+
}
557+
}
558+
559+
// Of our remaining candidates, find out if any have overlapping storage
560+
// liveness. Those that do must be in the same variant to remain candidates.
561+
// TODO revisit data structures here. should this be a sparse map? what
562+
// about the bitset?
563+
let mut local_overlaps: IndexVec<Local, _> =
564+
iter::repeat(liveness::LiveVarSet::new_empty(mir.local_decls.len()))
565+
.take(mir.local_decls.len())
566+
.collect();
567+
568+
for block in mir.basic_blocks().indices() {
569+
for_each_location(block, &storage_live_analysis, &storage_live, mir, |state, loc| {
570+
let mut eligible_storage_live = state.clone().to_dense();
571+
eligible_storage_live.intersect(&eligible_locals);
572+
573+
for local in eligible_storage_live.iter() {
574+
let mut overlaps = eligible_storage_live.clone();
575+
overlaps.remove(local);
576+
local_overlaps[local].union(&overlaps);
577+
578+
if !overlaps.is_empty() {
579+
trace!("local {:?} overlaps with these locals at {:?}: {:?}",
580+
local, loc, overlaps);
581+
}
582+
}
583+
});
584+
}
585+
586+
for (local, overlaps) in local_overlaps.iter_enumerated() {
587+
// This local may have been deemed ineligible already.
588+
if !eligible_locals.contains(local) {
589+
continue;
590+
}
591+
592+
for other_local in overlaps.iter() {
593+
// local and other_local have overlapping storage, therefore they
594+
// cannot overlap in the generator layout. The only way to guarantee
595+
// this is if they are in the same variant, or one is ineligible
596+
// (which means it is stored in every variant).
597+
if eligible_locals.contains(other_local) &&
598+
variants_map[&local] != variants_map[&other_local] {
599+
// We arbitrarily choose other_local, which will be the higher
600+
// indexed of the two locals, to remove.
601+
eligible_locals.remove(other_local);
602+
debug!("removing local {:?} due to overlap", other_local);
603+
}
604+
}
605+
}
606+
607+
let mut assignments = FxHashMap::default();
608+
for local in eligible_locals.iter() {
609+
assignments
610+
.entry(variants_map[&local].unwrap())
611+
.or_insert(liveness::LiveVarSet::new_empty(mir.local_decls.len()))
612+
.insert(local);
613+
}
614+
615+
let mut ineligible_locals = stored_locals.clone();
616+
ineligible_locals.subtract(&eligible_locals);
617+
618+
debug!("locals_eligible_for_overlap() => (assignments: {:?}, eligible_locals: {:?}",
619+
assignments, eligible_locals);
620+
621+
(assignments, ineligible_locals)
500622
}
501623

502624
fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -510,7 +632,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
510632
FxHashMap<BasicBlock, liveness::LiveVarSet>)
511633
{
512634
// Use a liveness analysis to compute locals which are live across a suspension point
513-
let (live_locals, storage_liveness, suspending_blocks) =
635+
let (live_locals, variant_assignments, prefix_locals, storage_liveness, suspending_blocks) =
514636
locals_live_across_suspend_points(tcx, mir, source, movable);
515637

516638
// Erase regions from the types passed in from typeck so we can compare them with
@@ -539,29 +661,37 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
539661
}
540662

541663
let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), mir.span);
542-
543-
// Gather live locals and their indices replacing values in mir.local_decls with a dummy
544-
// to avoid changing local indices
545-
let live_decls = live_locals.iter().map(|local| {
546-
let var = mem::replace(&mut mir.local_decls[local], dummy_local.clone());
547-
(local, var)
664+
let mut remap = FxHashMap::default();
665+
666+
const INITIAL_VARIANTS: usize = 3;
667+
let prefix_variant = VariantIdx::new(INITIAL_VARIANTS);
668+
let prefix_fields = prefix_locals.iter().enumerate().map(|(idx, local)| {
669+
// Gather LocalDecls of live locals replacing values in mir.local_decls
670+
// with a dummy to avoid changing local indices.
671+
let field = mem::replace(&mut mir.local_decls[local], dummy_local.clone());
672+
remap.insert(local, (field.ty, prefix_variant, idx));
673+
field
674+
})
675+
.collect::<Vec<_>>();
676+
677+
let state_variants = suspending_blocks.iter().enumerate().map(|(variant, block)| {
678+
let variant_index = VariantIdx::new(INITIAL_VARIANTS + variant);
679+
let no_assignments = &liveness::LiveVarSet::new_empty(0);
680+
let variant_locals = variant_assignments.get(&block)
681+
.unwrap_or(no_assignments)
682+
.iter().enumerate().map(|(idx, local)| {
683+
// Gather LocalDecls of live locals replacing values in
684+
// mir.local_decls with a dummy to avoid changing local indices.
685+
let field = mem::replace(&mut mir.local_decls[local], dummy_local.clone());
686+
// These fields follow the prefix fields.
687+
remap.insert(local, (field.ty, variant_index, prefix_fields.len() + idx));
688+
field
689+
});
690+
prefix_fields.iter().cloned().chain(variant_locals).collect::<Vec<_>>()
548691
});
549692

550-
// For now we will access everything via variant #3, leaving empty variants
551-
// for the UNRESUMED, RETURNED, and POISONED states.
552-
// If there were a yield-less generator without a variant #3, it would not
553-
// have any vars to remap, so we would never use this.
554-
let variant_index = VariantIdx::new(3);
555-
556-
// Create a map from local indices to generator struct indices.
557-
// We also create a vector of the LocalDecls of these locals.
558-
let (remap, vars) = live_decls.enumerate().map(|(idx, (local, var))| {
559-
((local, (var.ty, variant_index, idx)), var)
560-
}).unzip();
561-
562693
// Put every var in each variant, for now.
563694
let empty_variants = iter::repeat(vec![]).take(3);
564-
let state_variants = iter::repeat(vars).take(suspending_blocks.count());
565695
let layout = GeneratorLayout {
566696
variant_fields: empty_variants.chain(state_variants).collect()
567697
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(generators)]
2+
3+
fn main() {
4+
let a = || {
5+
{
6+
let x: i32 = 5;
7+
yield;
8+
println!("{:?}", x);
9+
}
10+
{
11+
let y: i32 = 6;
12+
yield;
13+
println!("{:?}", y);
14+
}
15+
};
16+
assert_eq!(8, std::mem::size_of_val(&a));
17+
}

0 commit comments

Comments
 (0)