Skip to content

Commit faed5e2

Browse files
committed
Auto merge of #60187 - tmandry:generator-optimization, r=<try>
[WIP] Generator optimization: Overlap locals that never have storage live at the same time This depends on #59897 (which is currently failing). Only the commits from "Emit StorageDead for all locals" and on are relevant. The reason I'm opening this now is so we can get some perf numbers and discuss the approach.
2 parents 004c549 + e27b6fd commit faed5e2

File tree

28 files changed

+1065
-537
lines changed

28 files changed

+1065
-537
lines changed

src/librustc/mir/mod.rs

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

2032+
pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> {
2033+
self.elem(ProjectionElem::Downcast(None, variant_index))
2034+
}
2035+
20322036
pub fn index(self, index: Local) -> Place<'tcx> {
20332037
self.elem(ProjectionElem::Index(index))
20342038
}
@@ -2554,11 +2558,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
25542558
let var_name = tcx.hir().name_by_hir_id(freevar.var_id());
25552559
struct_fmt.field(&var_name.as_str(), place);
25562560
}
2557-
struct_fmt.field("$state", &places[freevars.len()]);
2558-
for i in (freevars.len() + 1)..places.len() {
2559-
struct_fmt
2560-
.field(&format!("${}", i - freevars.len() - 1), &places[i]);
2561-
}
25622561
});
25632562

25642563
struct_fmt.finish()
@@ -2999,7 +2998,7 @@ pub struct UnsafetyCheckResult {
29992998
/// The layout of generator state
30002999
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
30013000
pub struct GeneratorLayout<'tcx> {
3002-
pub fields: Vec<LocalDecl<'tcx>>,
3001+
pub variant_fields: IndexVec<VariantIdx, Vec<LocalDecl<'tcx>>>,
30033002
}
30043003

30053004
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@@ -3188,7 +3187,7 @@ BraceStructTypeFoldableImpl! {
31883187

31893188
BraceStructTypeFoldableImpl! {
31903189
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
3191-
fields
3190+
variant_fields
31923191
}
31933192
}
31943193

src/librustc/mir/tcx.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,13 @@ impl<'tcx> Rvalue<'tcx> {
212212
}
213213
Rvalue::Discriminant(ref place) => {
214214
let ty = place.ty(local_decls, tcx).ty;
215-
if let ty::Adt(adt_def, _) = ty.sty {
216-
adt_def.repr.discr_type().to_ty(tcx)
217-
} else {
218-
// This can only be `0`, for now, so `u8` will suffice.
219-
tcx.types.u8
215+
match ty.sty {
216+
ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx),
217+
ty::Generator(_, substs, _) => substs.discr_ty(tcx),
218+
_ => {
219+
// This can only be `0`, for now, so `u8` will suffice.
220+
tcx.types.u8
221+
}
220222
}
221223
}
222224
Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t),

src/librustc/ty/layout.rs

+71-10
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,57 @@ 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+
let discr_index = substs.prefix_tys(def_id, tcx).count();
609+
let prefix_tys = substs.prefix_tys(def_id, tcx)
610+
.chain(iter::once(substs.discr_ty(tcx)));
611+
let prefix = univariant_uninterned(
612+
&prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
611613
&ReprOptions::default(),
612-
StructKind::AlwaysSized)?
614+
StructKind::AlwaysSized)?;
615+
616+
let mut size = prefix.size;
617+
let mut align = prefix.align;
618+
let variants_tys = substs.state_tys(def_id, tcx);
619+
let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
620+
let mut variant = univariant_uninterned(
621+
&variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
622+
&ReprOptions::default(),
623+
StructKind::Prefixed(prefix.size, prefix.align.abi))?;
624+
625+
variant.variants = Variants::Single { index: VariantIdx::new(i) };
626+
627+
size = size.max(variant.size);
628+
align = align.max(variant.align);
629+
630+
Ok(variant)
631+
}).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
632+
633+
let abi = if prefix.abi.is_uninhabited() ||
634+
variants.iter().all(|v| v.abi.is_uninhabited()) {
635+
Abi::Uninhabited
636+
} else {
637+
Abi::Aggregate { sized: true }
638+
};
639+
let discr = match &self.layout_of(substs.discr_ty(tcx))?.abi {
640+
Abi::Scalar(s) => s.clone(),
641+
_ => bug!(),
642+
};
643+
644+
let layout = tcx.intern_layout(LayoutDetails {
645+
variants: Variants::Multiple {
646+
discr,
647+
discr_kind: DiscriminantKind::Tag,
648+
discr_index,
649+
variants,
650+
},
651+
fields: prefix.fields,
652+
abi,
653+
size,
654+
align,
655+
});
656+
debug!("generator layout: {:#?}", layout);
657+
layout
613658
}
614659

615660
ty::Closure(def_id, ref substs) => {
@@ -1646,6 +1691,14 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
16461691

16471692
fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout {
16481693
let tcx = cx.tcx();
1694+
let handle_discriminant = |discr: &Scalar| -> C::TyLayout {
1695+
let layout = LayoutDetails::scalar(cx, discr.clone());
1696+
MaybeResult::from_ok(TyLayout {
1697+
details: tcx.intern_layout(layout),
1698+
ty: discr.value.to_ty(tcx)
1699+
})
1700+
};
1701+
16491702
cx.layout_of(match this.ty.sty {
16501703
ty::Bool |
16511704
ty::Char |
@@ -1720,7 +1773,19 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
17201773
}
17211774

17221775
ty::Generator(def_id, ref substs, _) => {
1723-
substs.field_tys(def_id, tcx).nth(i).unwrap()
1776+
match this.variants {
1777+
Variants::Single { index } => {
1778+
substs.state_tys(def_id, tcx)
1779+
.nth(index.as_usize()).unwrap()
1780+
.nth(i).unwrap()
1781+
}
1782+
Variants::Multiple { ref discr, discr_index, .. } => {
1783+
if i == discr_index {
1784+
return handle_discriminant(discr);
1785+
}
1786+
substs.prefix_tys(def_id, tcx).nth(i).unwrap()
1787+
}
1788+
}
17241789
}
17251790

17261791
ty::Tuple(tys) => tys[i],
@@ -1740,11 +1805,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
17401805
// Discriminant field for enums (where applicable).
17411806
Variants::Multiple { ref discr, .. } => {
17421807
assert_eq!(i, 0);
1743-
let layout = LayoutDetails::scalar(cx, discr.clone());
1744-
return MaybeResult::from_ok(TyLayout {
1745-
details: tcx.intern_layout(layout),
1746-
ty: discr.value.to_ty(tcx)
1747-
});
1808+
return handle_discriminant(discr);
17481809
}
17491810
}
17501811
}

src/librustc/ty/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use std::ops::Deref;
4141
use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter};
4242
use std::slice;
4343
use std::{mem, ptr};
44+
use std::ops::Range;
4445
use syntax::ast::{self, Name, Ident, NodeId};
4546
use syntax::attr;
4647
use syntax::ext::hygiene::Mark;
@@ -2418,11 +2419,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
24182419
})
24192420
}
24202421

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

src/librustc/ty/sty.rs

+114-30
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ use polonius_engine::Atom;
99
use rustc_data_structures::indexed_vec::Idx;
1010
use rustc_macros::HashStable;
1111
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, Kind, UnpackedKind};
12-
use crate::ty::{self, AdtDef, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
12+
use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
1313
use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
14+
use crate::ty::layout::VariantIdx;
1415
use crate::util::captures::Captures;
1516
use crate::mir::interpret::{Scalar, Pointer};
1617

1718
use smallvec::SmallVec;
18-
use std::iter;
1919
use std::cmp::Ordering;
2020
use std::marker::PhantomData;
21+
use std::ops::Range;
2122
use rustc_target::spec::abi;
2223
use syntax::ast::{self, Ident};
2324
use syntax::symbol::{keywords, InternedString};
@@ -298,14 +299,10 @@ static_assert!(MEM_SIZE_OF_TY_KIND: ::std::mem::size_of::<TyKind<'_>>() == 24);
298299
///
299300
/// ## Generators
300301
///
301-
/// Perhaps surprisingly, `ClosureSubsts` are also used for
302-
/// generators. In that case, what is written above is only half-true
303-
/// -- the set of type parameters is similar, but the role of CK and
304-
/// CS are different. CK represents the "yield type" and CS
305-
/// represents the "return type" of the generator.
306-
///
307-
/// It'd be nice to split this struct into ClosureSubsts and
308-
/// GeneratorSubsts, I believe. -nmatsakis
302+
/// Generators are handled similarly in `GeneratorSubsts`. The set of
303+
/// type parameters is similar, but the role of CK and CS are
304+
/// different. CK represents the "yield type" and CS represents the
305+
/// "return type" of the generator.
309306
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
310307
Debug, RustcEncodable, RustcDecodable, HashStable)]
311308
pub struct ClosureSubsts<'tcx> {
@@ -391,6 +388,7 @@ impl<'tcx> ClosureSubsts<'tcx> {
391388
}
392389
}
393390

391+
/// Similar to `ClosureSubsts`; see the above documentation for more.
394392
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug,
395393
RustcEncodable, RustcDecodable, HashStable)]
396394
pub struct GeneratorSubsts<'tcx> {
@@ -470,33 +468,91 @@ impl<'tcx> GeneratorSubsts<'tcx> {
470468
}
471469

472470
impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> {
471+
/// Generator have not been resumed yet
472+
pub const UNRESUMED: usize = 0;
473+
/// Generator has returned / is completed
474+
pub const RETURNED: usize = 1;
475+
/// Generator has been poisoned
476+
pub const POISONED: usize = 2;
477+
478+
const UNRESUMED_NAME: &'static str = "Unresumed";
479+
const RETURNED_NAME: &'static str = "Returned";
480+
const POISONED_NAME: &'static str = "Panicked";
481+
482+
/// The valid variant indices of this Generator.
483+
#[inline]
484+
pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range<VariantIdx> {
485+
// FIXME requires optimized MIR
486+
let num_variants = self.state_tys(def_id, tcx).count();
487+
(VariantIdx::new(0)..VariantIdx::new(num_variants))
488+
}
489+
490+
/// The discriminant for the given variant. Panics if the variant_index is
491+
/// out of range.
492+
#[inline]
493+
pub fn discriminant_for_variant(
494+
&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: VariantIdx
495+
) -> Discr<'tcx> {
496+
// Generators don't support explicit discriminant values, so they are
497+
// the same as the variant index.
498+
assert!(self.variant_range(def_id, tcx).contains(&variant_index));
499+
Discr { val: variant_index.as_usize() as u128, ty: self.discr_ty(tcx) }
500+
}
501+
502+
/// The set of all discriminants for the Generator, enumerated with their
503+
/// variant indices.
504+
#[inline]
505+
pub fn discriminants(
506+
&'a self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>
507+
) -> impl Iterator<Item=(VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
508+
self.variant_range(def_id, tcx).map(move |index| {
509+
(index, Discr { val: index.as_usize() as u128, ty: self.discr_ty(tcx) })
510+
})
511+
}
512+
513+
/// Calls `f` with a reference to the name of the enumerator for the given
514+
/// variant `v`.
515+
#[inline]
516+
pub fn map_variant_name<R>(&self, v: VariantIdx, f: impl FnOnce(&str) -> R) -> R {
517+
let name = match v.as_usize() {
518+
Self::UNRESUMED => Self::UNRESUMED_NAME,
519+
Self::RETURNED => Self::RETURNED_NAME,
520+
Self::POISONED => Self::POISONED_NAME,
521+
_ => {
522+
return f(&format!("variant#{}", v.as_usize()));
523+
}
524+
};
525+
f(name)
526+
}
527+
528+
/// The type of the state discriminant used in the generator type.
529+
#[inline]
530+
pub fn discr_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
531+
tcx.types.u32
532+
}
533+
473534
/// This returns the types of the MIR locals which had to be stored across suspension points.
474535
/// It is calculated in rustc_mir::transform::generator::StateTransform.
475536
/// All the types here must be in the tuple in GeneratorInterior.
476-
pub fn state_tys(
477-
self,
478-
def_id: DefId,
479-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
480-
) -> impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a {
481-
let state = tcx.generator_layout(def_id).fields.iter();
482-
state.map(move |d| d.ty.subst(tcx, self.substs))
483-
}
484-
485-
/// This is the types of the fields of a generate which
486-
/// is available before the generator transformation.
487-
/// It includes the upvars and the state discriminant which is u32.
488-
pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
489-
impl Iterator<Item=Ty<'tcx>> + 'a
537+
///
538+
/// The locals are grouped by their variant number. Note that some locals may
539+
/// be repeated in multiple variants.
540+
#[inline]
541+
pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
542+
impl Iterator<Item=impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a>
490543
{
491-
self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
544+
tcx.generator_layout(def_id)
545+
.variant_fields.iter()
546+
.map(move |v| v.iter().map(move |d| d.ty.subst(tcx, self.substs)))
492547
}
493548

494-
/// This is the types of all the fields stored in a generator.
495-
/// It includes the upvars, state types and the state discriminant which is u32.
496-
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
497-
impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a
549+
/// This is the types of the fields of a generator which are not stored in a
550+
/// variant.
551+
#[inline]
552+
pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
553+
impl Iterator<Item=Ty<'tcx>> + 'a
498554
{
499-
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
555+
self.upvar_tys(def_id, tcx)
500556
}
501557
}
502558

@@ -1994,6 +2050,34 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
19942050
}
19952051
}
19962052

2053+
/// If the type contains variants, returns the valid range of variant indices.
2054+
/// FIXME This requires the optimized MIR in the case of generators.
2055+
#[inline]
2056+
pub fn variant_range(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Range<VariantIdx>> {
2057+
match self.sty {
2058+
TyKind::Adt(adt, _) => Some(adt.variant_range()),
2059+
TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)),
2060+
_ => None,
2061+
}
2062+
}
2063+
2064+
/// If the type contains variants, returns the variant for `variant_index`.
2065+
/// Panics if `variant_index` is out of range.
2066+
/// FIXME This requires the optimized MIR in the case of generators.
2067+
#[inline]
2068+
pub fn discriminant_for_variant(
2069+
&self,
2070+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
2071+
variant_index: VariantIdx
2072+
) -> Option<Discr<'tcx>> {
2073+
match self.sty {
2074+
TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)),
2075+
TyKind::Generator(def_id, substs, _) =>
2076+
Some(substs.discriminant_for_variant(def_id, tcx, variant_index)),
2077+
_ => None,
2078+
}
2079+
}
2080+
19972081
/// Push onto `out` the regions directly referenced from this type (but not
19982082
/// types reachable from this type via `walk_tys`). This ignores late-bound
19992083
/// regions binders.

0 commit comments

Comments
 (0)