Skip to content

Commit 7868758

Browse files
committed
Overlap locals that never have storage live at the same time
...and are only included in a single variant.
1 parent f9f8bfa commit 7868758

File tree

1 file changed

+201
-13
lines changed

1 file changed

+201
-13
lines changed

src/librustc/ty/layout.rs

+201-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use std::ops::Bound;
1414

1515
use crate::hir;
1616
use crate::ich::StableHashingContext;
17+
use crate::mir::GeneratorSavedLocal;
18+
use crate::ty::subst::Subst;
19+
use rustc_data_structures::bit_set::BitSet;
1720
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
1821
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
1922
StableHasherResult};
@@ -612,34 +615,219 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
612615
}
613616

614617
ty::Generator(def_id, ref substs, _) => {
615-
// FIXME(tmandry): For fields that are repeated in multiple
616-
// variants in the GeneratorLayout, we need code to ensure that
617-
// the offset of these fields never change. Right now this is
618-
// not an issue since every variant has every field, but once we
619-
// optimize this we have to be more careful.
618+
// When laying out generators, we divide our saved local fields
619+
// into two categories: overlap-eligible and overlap-ineligible.
620+
//
621+
// Those fields which are ineligible for overlap go in a
622+
// "prefix" at the beginning of the layout, and always have
623+
// space reserved for them.
624+
//
625+
// Overlap-eligible fields are only assigned to one variant, so
626+
// we lay those fields out for each variant and put them right
627+
// after the prefix.
628+
//
629+
// Finally, in the layout details, we point to the fields
630+
// from the variants they are assigned to. It is possible for
631+
// some fields to be included in multiple variants. No field
632+
// ever "moves around" in the layout; its offset is always the
633+
// same.
634+
//
635+
// Also included in the layout are the upvars and the
636+
// discriminant. These are included as fields on the "outer"
637+
// layout; they are not part of any variant.
638+
639+
let info = tcx.generator_layout(def_id);
640+
let subst_field = |ty: Ty<'tcx>| { ty.subst(tcx, substs.substs) };
641+
642+
#[derive(Clone, Debug, PartialEq)]
643+
enum SavedLocalEligibility {
644+
Unassigned,
645+
Assigned(VariantIdx),
646+
// FIXME: Use newtype_index so we aren't wasting bytes
647+
Ineligible(Option<u32>),
648+
}
649+
use SavedLocalEligibility::*;
650+
651+
let mut assignments: IndexVec<GeneratorSavedLocal, SavedLocalEligibility> =
652+
iter::repeat(Unassigned)
653+
.take(info.field_tys.len())
654+
.collect();
655+
656+
// The saved locals not eligible for overlap. These will get
657+
// "promoted" to the prefix of our generator.
658+
let mut eligible_locals = BitSet::new_filled(info.field_tys.len());
659+
660+
// Figure out which of our saved locals are fields in only
661+
// one variant. The rest are deemed ineligible for overlap.
662+
for (variant_index, fields) in info.variant_fields.iter_enumerated() {
663+
for local in fields {
664+
match assignments[*local] {
665+
Unassigned => {
666+
assignments[*local] = Assigned(variant_index);
667+
}
668+
Assigned(idx) => {
669+
// We've already seen this local at another suspension
670+
// point, so it is no longer a candidate.
671+
trace!("removing local {:?} in >1 variant ({:?}, {:?})",
672+
local, variant_index, idx);
673+
eligible_locals.remove(*local);
674+
assignments[*local] = Ineligible(None);
675+
}
676+
Ineligible(_) => {},
677+
}
678+
}
679+
}
680+
681+
// Next, check every pair of eligible locals to see if they
682+
// conflict.
683+
for (local_a, conflicts_a) in info.storage_conflicts.iter_enumerated() {
684+
if !eligible_locals.contains(local_a) {
685+
continue;
686+
}
687+
688+
for local_b in conflicts_a.iter() {
689+
// local_a and local_b have overlapping storage, therefore they
690+
// cannot overlap in the generator layout. The only way to guarantee
691+
// this is if they are in the same variant, or one is ineligible
692+
// (which means it is stored in every variant).
693+
if !eligible_locals.contains(local_b) ||
694+
assignments[local_a] == assignments[local_b]
695+
{
696+
continue;
697+
}
698+
699+
// If they conflict, we will choose one to make ineligible.
700+
let conflicts_b = &info.storage_conflicts[local_b];
701+
let (remove, other) = if conflicts_a.count() > conflicts_b.count() {
702+
(local_a, local_b)
703+
} else {
704+
(local_b, local_a)
705+
};
706+
eligible_locals.remove(remove);
707+
assignments[remove] = Ineligible(None);
708+
trace!("removing local {:?} due to conflict with {:?}", remove, other);
709+
}
710+
}
711+
712+
let mut ineligible_locals = BitSet::new_filled(info.field_tys.len());
713+
ineligible_locals.subtract(&eligible_locals);
620714

715+
// Write down the order of our locals that will be promoted to
716+
// the prefix.
717+
for (idx, local) in ineligible_locals.iter().enumerate() {
718+
assignments[local] = Ineligible(Some(idx as u32));
719+
}
720+
debug!("generator saved local assignments: {:?}", assignments);
721+
722+
// Build a prefix layout, including "promoting" all ineligible
723+
// locals as part of the prefix.
621724
let discr_index = substs.prefix_tys(def_id, tcx).count();
725+
let promoted_tys =
726+
ineligible_locals.iter().map(|local| subst_field(info.field_tys[local]));
622727
let prefix_tys = substs.prefix_tys(def_id, tcx)
623-
.chain(iter::once(substs.discr_ty(tcx)));
728+
.chain(iter::once(substs.discr_ty(tcx)))
729+
.chain(promoted_tys);
624730
let prefix = univariant_uninterned(
625731
&prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
626732
&ReprOptions::default(),
627733
StructKind::AlwaysSized)?;
734+
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
735+
736+
// Split the prefix layout into the "outer" fields (upvars and
737+
// discriminant) and the "promoted" fields. Promoted fields will
738+
// get included in each variant that requested them in
739+
// GeneratorLayout.
740+
let renumber_indices = |mut index: Vec<u32>| -> Vec<u32> {
741+
debug!("renumber_indices({:?})", index);
742+
let mut inverse_index = (0..index.len() as u32).collect::<Vec<_>>();
743+
inverse_index.sort_unstable_by_key(|i| index[*i as usize]);
744+
for i in 0..index.len() {
745+
index[inverse_index[i] as usize] = i as u32;
746+
}
747+
debug!("renumber_indices() => {:?}", index);
748+
index
749+
};
750+
debug!("prefix = {:#?}", prefix);
751+
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
752+
FieldPlacement::Arbitrary { offsets, memory_index } => {
753+
let (offsets_a, offsets_b) =
754+
offsets.split_at(discr_index + 1);
755+
let (memory_index_a, memory_index_b) =
756+
memory_index.split_at(discr_index + 1);
757+
let outer_fields = FieldPlacement::Arbitrary {
758+
offsets: offsets_a.to_vec(),
759+
memory_index: renumber_indices(memory_index_a.to_vec())
760+
};
761+
(outer_fields,
762+
offsets_b.to_vec(),
763+
renumber_indices(memory_index_b.to_vec()))
764+
}
765+
_ => bug!(),
766+
};
628767

629768
let mut size = prefix.size;
630769
let mut align = prefix.align;
631-
let variants_tys = substs.state_tys(def_id, tcx);
632-
let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
770+
let variants = info.variant_fields.iter_enumerated().map(|(index, variant_fields)| {
771+
// Only include overlap-eligible fields when we compute our variant layout.
772+
let variant_only_tys = variant_fields.iter().flat_map(|local| {
773+
let ty = info.field_tys[*local];
774+
match assignments[*local] {
775+
Unassigned => bug!(),
776+
Assigned(v) if v == index => Some(subst_field(ty)),
777+
Assigned(_) => bug!("assignment does not match variant"),
778+
Ineligible(_) => None,
779+
}
780+
});
781+
633782
let mut variant = univariant_uninterned(
634-
&variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
783+
&variant_only_tys
784+
.map(|ty| self.layout_of(ty))
785+
.collect::<Result<Vec<_>, _>>()?,
635786
&ReprOptions::default(),
636-
StructKind::Prefixed(prefix.size, prefix.align.abi))?;
787+
StructKind::Prefixed(prefix_size, prefix_align.abi))?;
788+
variant.variants = Variants::Single { index };
637789

638-
variant.variants = Variants::Single { index: VariantIdx::new(i) };
790+
let (offsets, memory_index) = match variant.fields {
791+
FieldPlacement::Arbitrary { offsets, memory_index } =>
792+
(offsets, memory_index),
793+
_ => bug!(),
794+
};
795+
796+
// Now, stitch the promoted and variant-only fields back
797+
// together in the order they are mentioned by our
798+
// GeneratorLayout.
799+
let mut next_variant_field = 0;
800+
let mut combined_offsets = Vec::new();
801+
let mut combined_memory_index = Vec::new();
802+
for local in variant_fields.iter() {
803+
match assignments[*local] {
804+
Unassigned => bug!(),
805+
Assigned(_) => {
806+
combined_offsets.push(offsets[next_variant_field]);
807+
// Shift memory indices by the number of
808+
// promoted fields, which all come first. We
809+
// may not use all promoted fields in our
810+
// variant but that's okay; we'll renumber them
811+
// below.
812+
combined_memory_index.push(
813+
promoted_memory_index.len() as u32 +
814+
memory_index[next_variant_field]);
815+
next_variant_field += 1;
816+
}
817+
Ineligible(field_idx) => {
818+
let field_idx = field_idx.unwrap() as usize;
819+
combined_offsets.push(promoted_offsets[field_idx]);
820+
combined_memory_index.push(promoted_memory_index[field_idx]);
821+
}
822+
}
823+
}
824+
variant.fields = FieldPlacement::Arbitrary {
825+
offsets: combined_offsets,
826+
memory_index: renumber_indices(combined_memory_index),
827+
};
639828

640829
size = size.max(variant.size);
641830
align = align.max(variant.align);
642-
643831
Ok(variant)
644832
}).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
645833

@@ -661,7 +849,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
661849
discr_index,
662850
variants,
663851
},
664-
fields: prefix.fields,
852+
fields: outer_fields,
665853
abi,
666854
size,
667855
align,

0 commit comments

Comments
 (0)