Skip to content

Commit e232636

Browse files
committed
Auto merge of rust-lang#59897 - tmandry:variantful-generators, r=eddyb
Multi-variant layouts for generators This allows generators to overlap fields using variants, but doesn't do any such overlapping yet. It creates one variant for every state of the generator (unresumed, returned, panicked, plus one for every yield), and puts every stored local in each of the yield-point variants. Required for optimizing generator layouts (rust-lang#52924). There was quite a lot of refactoring needed for this change. I've done my best in later commits to eliminate assumptions in the code that only certain kinds of types are multi-variant, and to centralize knowledge of the inner mechanics of generators in as few places as possible. This change also emits debuginfo about the fields contained in each variant, as well as preserving debuginfo about stored locals while running in the generator. Also, fixes rust-lang#59972. Future work: - Use this change for an optimization pass that actually overlaps locals within the generator struct (rust-lang#52924) - In the type layout fields, don't include locals that are uninitialized for a particular variant, so miri and UB sanitizers can check our memory (see rust-lang#59972 (comment)) - Preserve debuginfo scopes across generator yield points
2 parents 13fde05 + 77a6d29 commit e232636

File tree

22 files changed

+804
-331
lines changed

22 files changed

+804
-331
lines changed

src/librustc/mir/mod.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,10 @@ impl<'tcx> Place<'tcx> {
20302030
variant_index))
20312031
}
20322032

2033+
pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> {
2034+
self.elem(ProjectionElem::Downcast(None, variant_index))
2035+
}
2036+
20332037
pub fn index(self, index: Local) -> Place<'tcx> {
20342038
self.elem(ProjectionElem::Index(index))
20352039
}
@@ -2589,11 +2593,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
25892593
let var_name = tcx.hir().name_by_hir_id(freevar.var_id());
25902594
struct_fmt.field(&var_name.as_str(), place);
25912595
}
2592-
struct_fmt.field("$state", &places[freevars.len()]);
2593-
for i in (freevars.len() + 1)..places.len() {
2594-
struct_fmt
2595-
.field(&format!("${}", i - freevars.len() - 1), &places[i]);
2596-
}
25972596
});
25982597

25992598
struct_fmt.finish()
@@ -3031,10 +3030,29 @@ pub struct UnsafetyCheckResult {
30313030
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
30323031
}
30333032

3033+
newtype_index! {
3034+
pub struct GeneratorSavedLocal {
3035+
derive [HashStable]
3036+
DEBUG_FORMAT = "_{}",
3037+
}
3038+
}
3039+
30343040
/// The layout of generator state
30353041
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
30363042
pub struct GeneratorLayout<'tcx> {
3037-
pub fields: Vec<LocalDecl<'tcx>>,
3043+
/// The type of every local stored inside the generator.
3044+
pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
3045+
3046+
/// Which of the above fields are in each variant. Note that one field may
3047+
/// be stored in multiple variants.
3048+
pub variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>>,
3049+
3050+
/// Names and scopes of all the stored generator locals.
3051+
/// NOTE(tmandry) This is *strictly* a temporary hack for codegen
3052+
/// debuginfo generation, and will be removed at some point.
3053+
/// Do **NOT** use it for anything else, local information should not be
3054+
/// in the MIR, please rely on local crate HIR or other side-channels.
3055+
pub __local_debuginfo_codegen_only_do_not_use: IndexVec<GeneratorSavedLocal, LocalDecl<'tcx>>,
30383056
}
30393057

30403058
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@@ -3223,7 +3241,9 @@ BraceStructTypeFoldableImpl! {
32233241

32243242
BraceStructTypeFoldableImpl! {
32253243
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
3226-
fields
3244+
field_tys,
3245+
variant_fields,
3246+
__local_debuginfo_codegen_only_do_not_use,
32273247
}
32283248
}
32293249

@@ -3598,6 +3618,15 @@ impl<'tcx> TypeFoldable<'tcx> for Field {
35983618
}
35993619
}
36003620

3621+
impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
3622+
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
3623+
*self
3624+
}
3625+
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
3626+
false
3627+
}
3628+
}
3629+
36013630
impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
36023631
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
36033632
Constant {

src/librustc/mir/tcx.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,13 @@ impl<'tcx> Rvalue<'tcx> {
177177
}
178178
Rvalue::Discriminant(ref place) => {
179179
let ty = place.ty(local_decls, tcx).ty;
180-
if let ty::Adt(adt_def, _) = ty.sty {
181-
adt_def.repr.discr_type().to_ty(tcx)
182-
} else {
183-
// This can only be `0`, for now, so `u8` will suffice.
184-
tcx.types.u8
180+
match ty.sty {
181+
ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx),
182+
ty::Generator(_, substs, _) => substs.discr_ty(tcx),
183+
_ => {
184+
// This can only be `0`, for now, so `u8` will suffice.
185+
tcx.types.u8
186+
}
185187
}
186188
}
187189
Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t),

src/librustc/ty/layout.rs

+77-10
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,63 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
604604
tcx.intern_layout(unit)
605605
}
606606

607-
// Tuples, generators and closures.
608607
ty::Generator(def_id, ref substs, _) => {
609-
let tys = substs.field_tys(def_id, tcx);
610-
univariant(&tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
608+
// FIXME(tmandry): For fields that are repeated in multiple
609+
// variants in the GeneratorLayout, we need code to ensure that
610+
// the offset of these fields never change. Right now this is
611+
// not an issue since every variant has every field, but once we
612+
// optimize this we have to be more careful.
613+
614+
let discr_index = substs.prefix_tys(def_id, tcx).count();
615+
let prefix_tys = substs.prefix_tys(def_id, tcx)
616+
.chain(iter::once(substs.discr_ty(tcx)));
617+
let prefix = univariant_uninterned(
618+
&prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
611619
&ReprOptions::default(),
612-
StructKind::AlwaysSized)?
620+
StructKind::AlwaysSized)?;
621+
622+
let mut size = prefix.size;
623+
let mut align = prefix.align;
624+
let variants_tys = substs.state_tys(def_id, tcx);
625+
let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
626+
let mut variant = univariant_uninterned(
627+
&variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
628+
&ReprOptions::default(),
629+
StructKind::Prefixed(prefix.size, prefix.align.abi))?;
630+
631+
variant.variants = Variants::Single { index: VariantIdx::new(i) };
632+
633+
size = size.max(variant.size);
634+
align = align.max(variant.align);
635+
636+
Ok(variant)
637+
}).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
638+
639+
let abi = if prefix.abi.is_uninhabited() ||
640+
variants.iter().all(|v| v.abi.is_uninhabited()) {
641+
Abi::Uninhabited
642+
} else {
643+
Abi::Aggregate { sized: true }
644+
};
645+
let discr = match &self.layout_of(substs.discr_ty(tcx))?.abi {
646+
Abi::Scalar(s) => s.clone(),
647+
_ => bug!(),
648+
};
649+
650+
let layout = tcx.intern_layout(LayoutDetails {
651+
variants: Variants::Multiple {
652+
discr,
653+
discr_kind: DiscriminantKind::Tag,
654+
discr_index,
655+
variants,
656+
},
657+
fields: prefix.fields,
658+
abi,
659+
size,
660+
align,
661+
});
662+
debug!("generator layout: {:#?}", layout);
663+
layout
613664
}
614665

615666
ty::Closure(def_id, ref substs) => {
@@ -1647,6 +1698,14 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
16471698

16481699
fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout {
16491700
let tcx = cx.tcx();
1701+
let discr_layout = |discr: &Scalar| -> C::TyLayout {
1702+
let layout = LayoutDetails::scalar(cx, discr.clone());
1703+
MaybeResult::from_ok(TyLayout {
1704+
details: tcx.intern_layout(layout),
1705+
ty: discr.value.to_ty(tcx)
1706+
})
1707+
};
1708+
16501709
cx.layout_of(match this.ty.sty {
16511710
ty::Bool |
16521711
ty::Char |
@@ -1721,7 +1780,19 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
17211780
}
17221781

17231782
ty::Generator(def_id, ref substs, _) => {
1724-
substs.field_tys(def_id, tcx).nth(i).unwrap()
1783+
match this.variants {
1784+
Variants::Single { index } => {
1785+
substs.state_tys(def_id, tcx)
1786+
.nth(index.as_usize()).unwrap()
1787+
.nth(i).unwrap()
1788+
}
1789+
Variants::Multiple { ref discr, discr_index, .. } => {
1790+
if i == discr_index {
1791+
return discr_layout(discr);
1792+
}
1793+
substs.prefix_tys(def_id, tcx).nth(i).unwrap()
1794+
}
1795+
}
17251796
}
17261797

17271798
ty::Tuple(tys) => tys[i].expect_ty(),
@@ -1741,11 +1812,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
17411812
// Discriminant field for enums (where applicable).
17421813
Variants::Multiple { ref discr, .. } => {
17431814
assert_eq!(i, 0);
1744-
let layout = LayoutDetails::scalar(cx, discr.clone());
1745-
return MaybeResult::from_ok(TyLayout {
1746-
details: tcx.intern_layout(layout),
1747-
ty: discr.value.to_ty(tcx)
1748-
});
1815+
return discr_layout(discr);
17491816
}
17501817
}
17511818
}

src/librustc/ty/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use std::ops::Deref;
4343
use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter};
4444
use std::slice;
4545
use std::{mem, ptr};
46+
use std::ops::Range;
4647
use syntax::ast::{self, Name, Ident, NodeId};
4748
use syntax::attr;
4849
use syntax::ext::hygiene::Mark;
@@ -2416,11 +2417,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
24162417
})
24172418
}
24182419

2420+
#[inline]
2421+
pub fn variant_range(&self) -> Range<VariantIdx> {
2422+
(VariantIdx::new(0)..VariantIdx::new(self.variants.len()))
2423+
}
2424+
24192425
/// Computes the discriminant value used by a specific variant.
24202426
/// Unlike `discriminants`, this is (amortized) constant-time,
24212427
/// only doing at most one query for evaluating an explicit
24222428
/// discriminant (the last one before the requested variant),
24232429
/// assuming there are no constant-evaluation errors there.
2430+
#[inline]
24242431
pub fn discriminant_for_variant(&self,
24252432
tcx: TyCtxt<'a, 'gcx, 'tcx>,
24262433
variant_index: VariantIdx)

0 commit comments

Comments
 (0)