Skip to content

Commit f9f8bfa

Browse files
committed
Collect conflict information in GeneratorLayout
1 parent a73ecb3 commit f9f8bfa

File tree

3 files changed

+179
-4
lines changed

3 files changed

+179
-4
lines changed

src/librustc/mir/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::hir::def_id::DefId;
99
use crate::hir::{self, InlineAsm as HirInlineAsm};
1010
use crate::mir::interpret::{ConstValue, InterpError, Scalar};
1111
use crate::mir::visit::MirVisitable;
12+
use rustc_data_structures::bit_set::BitSet;
1213
use rustc_data_structures::fx::FxHashSet;
1314
use rustc_data_structures::graph::dominators::{dominators, Dominators};
1415
use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
@@ -1500,6 +1501,13 @@ impl<'tcx> BasicBlockData<'tcx> {
15001501
self.terminator.as_mut().expect("invalid terminator state")
15011502
}
15021503

1504+
pub fn is_unreachable(&self) -> bool {
1505+
match self.terminator().kind {
1506+
TerminatorKind::Unreachable => true,
1507+
_ => false,
1508+
}
1509+
}
1510+
15031511
pub fn retain_statements<F>(&mut self, mut f: F)
15041512
where
15051513
F: FnMut(&mut Statement<'_>) -> bool,
@@ -2997,6 +3005,11 @@ pub struct GeneratorLayout<'tcx> {
29973005
/// be stored in multiple variants.
29983006
pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
29993007

3008+
/// Which saved locals are storage-live at the same time. Locals that do not
3009+
/// have conflicts with each other are allowed to overlap in the computed
3010+
/// layout.
3011+
pub storage_conflicts: IndexVec<GeneratorSavedLocal, BitSet<GeneratorSavedLocal>>,
3012+
30003013
/// Names and scopes of all the stored generator locals.
30013014
/// NOTE(tmandry) This is *strictly* a temporary hack for codegen
30023015
/// debuginfo generation, and will be removed at some point.
@@ -3193,6 +3206,7 @@ BraceStructTypeFoldableImpl! {
31933206
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
31943207
field_tys,
31953208
variant_fields,
3209+
storage_conflicts,
31963210
__local_debuginfo_codegen_only_do_not_use,
31973211
}
31983212
}
@@ -3572,6 +3586,15 @@ impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
35723586
}
35733587
}
35743588

3589+
impl<'tcx, T: Idx> TypeFoldable<'tcx> for BitSet<T> {
3590+
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
3591+
self.clone()
3592+
}
3593+
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
3594+
false
3595+
}
3596+
}
3597+
35753598
impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
35763599
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
35773600
Constant {

src/librustc_mir/dataflow/mod.rs

+56
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,62 @@ pub fn state_for_location<'tcx, T: BitDenotation<'tcx>>(loc: Location,
380380
gen_set.to_dense()
381381
}
382382

383+
/// Calls `f` with the dataflow state at every location in `mir`.
384+
/// Ignores blocks that terminate in `unreachable`.
385+
pub fn for_each_location<'tcx, T: BitDenotation<'tcx>>(
386+
mir: &Body<'tcx>,
387+
analysis: &T,
388+
result: &DataflowResults<'tcx, T>,
389+
mut f: impl FnMut(&HybridBitSet<T::Idx>, Location)
390+
) {
391+
for (block, bb_data) in mir.basic_blocks().iter_enumerated() {
392+
if bb_data.is_unreachable() {
393+
continue;
394+
}
395+
for_each_block_location(mir, block, bb_data, analysis, result, &mut f);
396+
}
397+
}
398+
399+
fn for_each_block_location<'tcx, T: BitDenotation<'tcx>>(
400+
mir: &Body<'tcx>,
401+
block: BasicBlock,
402+
bb_data: &BasicBlockData<'tcx>,
403+
analysis: &T,
404+
result: &DataflowResults<'tcx, T>,
405+
f: &mut impl FnMut(&HybridBitSet<T::Idx>, Location)
406+
) {
407+
let statements = &bb_data.statements;
408+
409+
let mut on_entry = result.sets().on_entry_set_for(block.index()).to_owned();
410+
let mut kill_set = on_entry.to_hybrid();
411+
let mut gen_set = kill_set.clone();
412+
413+
{
414+
let mut sets = BlockSets {
415+
on_entry: &mut on_entry,
416+
kill_set: &mut kill_set,
417+
gen_set: &mut gen_set,
418+
};
419+
// FIXME: This location is technically wrong, but there isn't a way to
420+
// denote the start of a block.
421+
f(sets.gen_set, Location { block, statement_index: 0 });
422+
423+
for statement_index in 0..statements.len() {
424+
let loc = Location { block, statement_index };
425+
analysis.before_statement_effect(&mut sets, loc);
426+
f(sets.gen_set, loc);
427+
analysis.statement_effect(&mut sets, loc);
428+
f(sets.gen_set, loc);
429+
}
430+
431+
let term_loc = Location { block, statement_index: mir[block].statements.len() };
432+
analysis.before_terminator_effect(&mut sets, term_loc);
433+
f(sets.gen_set, term_loc);
434+
analysis.before_statement_effect(&mut sets, term_loc);
435+
f(sets.gen_set, term_loc);
436+
}
437+
}
438+
383439
pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation<'tcx>
384440
{
385441
flow_state: DataflowState<'tcx, O>,

src/librustc_mir/transform/generator.rs

+100-4
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ use std::mem;
6666
use crate::transform::{MirPass, MirSource};
6767
use crate::transform::simplify;
6868
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};
7071
use crate::dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
7172
use crate::util::dump_mir;
7273
use crate::util::liveness;
@@ -400,6 +401,7 @@ fn locals_live_across_suspend_points(
400401
movable: bool,
401402
) -> (
402403
liveness::LiveVarSet,
404+
IndexVec<GeneratorSavedLocal, BitSet<GeneratorSavedLocal>>,
403405
FxHashMap<BasicBlock, liveness::LiveVarSet>,
404406
BitSet<BasicBlock>,
405407
) {
@@ -503,7 +505,99 @@ fn locals_live_across_suspend_points(
503505
// The generator argument is ignored
504506
set.remove(self_arg());
505507

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
507601
}
508602

509603
fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -517,7 +611,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
517611
FxHashMap<BasicBlock, liveness::LiveVarSet>)
518612
{
519613
// 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) =
521615
locals_live_across_suspend_points(tcx, body, source, movable);
522616

523617
// 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>,
547641

548642
let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), body.span);
549643

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
551645
// to avoid changing local indices
552646
let live_decls = live_locals.iter().map(|local| {
553647
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>,
568662
remap.insert(local, (var.ty, variant_index, idx));
569663
decls.push(var);
570664
}
665+
debug!("generator saved local mappings: {:?}", decls);
571666
let field_tys = decls.iter().map(|field| field.ty).collect::<IndexVec<_, _>>();
572667

573668
// Put every var in each variant, for now.
@@ -578,6 +673,7 @@ fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
578673
let layout = GeneratorLayout {
579674
field_tys,
580675
variant_fields: empty_variants.chain(state_variants).collect(),
676+
storage_conflicts,
581677
__local_debuginfo_codegen_only_do_not_use: decls,
582678
};
583679

0 commit comments

Comments
 (0)