Skip to content

Commit

Permalink
Auto merge of #60187 - tmandry:generator-optimization, r=<try>
Browse files Browse the repository at this point in the history
[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.
  • Loading branch information
bors committed Apr 23, 2019
2 parents 004c549 + e27b6fd commit faed5e2
Show file tree
Hide file tree
Showing 28 changed files with 1,065 additions and 537 deletions.
13 changes: 6 additions & 7 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2029,6 +2029,10 @@ impl<'tcx> Place<'tcx> {
variant_index))
}

pub fn downcast_unnamed(self, variant_index: VariantIdx) -> Place<'tcx> {
self.elem(ProjectionElem::Downcast(None, variant_index))
}

pub fn index(self, index: Local) -> Place<'tcx> {
self.elem(ProjectionElem::Index(index))
}
Expand Down Expand Up @@ -2554,11 +2558,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
let var_name = tcx.hir().name_by_hir_id(freevar.var_id());
struct_fmt.field(&var_name.as_str(), place);
}
struct_fmt.field("$state", &places[freevars.len()]);
for i in (freevars.len() + 1)..places.len() {
struct_fmt
.field(&format!("${}", i - freevars.len() - 1), &places[i]);
}
});

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

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

BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
fields
variant_fields
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,13 @@ impl<'tcx> Rvalue<'tcx> {
}
Rvalue::Discriminant(ref place) => {
let ty = place.ty(local_decls, tcx).ty;
if let ty::Adt(adt_def, _) = ty.sty {
adt_def.repr.discr_type().to_ty(tcx)
} else {
// This can only be `0`, for now, so `u8` will suffice.
tcx.types.u8
match ty.sty {
ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx),
ty::Generator(_, substs, _) => substs.discr_ty(tcx),
_ => {
// This can only be `0`, for now, so `u8` will suffice.
tcx.types.u8
}
}
}
Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t),
Expand Down
81 changes: 71 additions & 10 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,12 +604,57 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
tcx.intern_layout(unit)
}

// Tuples, generators and closures.
ty::Generator(def_id, ref substs, _) => {
let tys = substs.field_tys(def_id, tcx);
univariant(&tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
let discr_index = substs.prefix_tys(def_id, tcx).count();
let prefix_tys = substs.prefix_tys(def_id, tcx)
.chain(iter::once(substs.discr_ty(tcx)));
let prefix = univariant_uninterned(
&prefix_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::AlwaysSized)?
StructKind::AlwaysSized)?;

let mut size = prefix.size;
let mut align = prefix.align;
let variants_tys = substs.state_tys(def_id, tcx);
let variants = variants_tys.enumerate().map(|(i, variant_tys)| {
let mut variant = univariant_uninterned(
&variant_tys.map(|ty| self.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::Prefixed(prefix.size, prefix.align.abi))?;

variant.variants = Variants::Single { index: VariantIdx::new(i) };

size = size.max(variant.size);
align = align.max(variant.align);

Ok(variant)
}).collect::<Result<IndexVec<VariantIdx, _>, _>>()?;

let abi = if prefix.abi.is_uninhabited() ||
variants.iter().all(|v| v.abi.is_uninhabited()) {
Abi::Uninhabited
} else {
Abi::Aggregate { sized: true }
};
let discr = match &self.layout_of(substs.discr_ty(tcx))?.abi {
Abi::Scalar(s) => s.clone(),
_ => bug!(),
};

let layout = tcx.intern_layout(LayoutDetails {
variants: Variants::Multiple {
discr,
discr_kind: DiscriminantKind::Tag,
discr_index,
variants,
},
fields: prefix.fields,
abi,
size,
align,
});
debug!("generator layout: {:#?}", layout);
layout
}

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

fn field(this: TyLayout<'tcx>, cx: &C, i: usize) -> C::TyLayout {
let tcx = cx.tcx();
let handle_discriminant = |discr: &Scalar| -> C::TyLayout {
let layout = LayoutDetails::scalar(cx, discr.clone());
MaybeResult::from_ok(TyLayout {
details: tcx.intern_layout(layout),
ty: discr.value.to_ty(tcx)
})
};

cx.layout_of(match this.ty.sty {
ty::Bool |
ty::Char |
Expand Down Expand Up @@ -1720,7 +1773,19 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
}

ty::Generator(def_id, ref substs, _) => {
substs.field_tys(def_id, tcx).nth(i).unwrap()
match this.variants {
Variants::Single { index } => {
substs.state_tys(def_id, tcx)
.nth(index.as_usize()).unwrap()
.nth(i).unwrap()
}
Variants::Multiple { ref discr, discr_index, .. } => {
if i == discr_index {
return handle_discriminant(discr);
}
substs.prefix_tys(def_id, tcx).nth(i).unwrap()
}
}
}

ty::Tuple(tys) => tys[i],
Expand All @@ -1740,11 +1805,7 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
// Discriminant field for enums (where applicable).
Variants::Multiple { ref discr, .. } => {
assert_eq!(i, 0);
let layout = LayoutDetails::scalar(cx, discr.clone());
return MaybeResult::from_ok(TyLayout {
details: tcx.intern_layout(layout),
ty: discr.value.to_ty(tcx)
});
return handle_discriminant(discr);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use std::ops::Deref;
use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter};
use std::slice;
use std::{mem, ptr};
use std::ops::Range;
use syntax::ast::{self, Name, Ident, NodeId};
use syntax::attr;
use syntax::ext::hygiene::Mark;
Expand Down Expand Up @@ -2418,11 +2419,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
})
}

#[inline]
pub fn variant_range(&self) -> Range<VariantIdx> {
(VariantIdx::new(0)..VariantIdx::new(self.variants.len()))
}

/// Computes the discriminant value used by a specific variant.
/// Unlike `discriminants`, this is (amortized) constant-time,
/// only doing at most one query for evaluating an explicit
/// discriminant (the last one before the requested variant),
/// assuming there are no constant-evaluation errors there.
#[inline]
pub fn discriminant_for_variant(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: VariantIdx)
Expand Down
144 changes: 114 additions & 30 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ use polonius_engine::Atom;
use rustc_data_structures::indexed_vec::Idx;
use rustc_macros::HashStable;
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, Kind, UnpackedKind};
use crate::ty::{self, AdtDef, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
use crate::ty::layout::VariantIdx;
use crate::util::captures::Captures;
use crate::mir::interpret::{Scalar, Pointer};

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

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

impl<'a, 'gcx, 'tcx> GeneratorSubsts<'tcx> {
/// Generator have not been resumed yet
pub const UNRESUMED: usize = 0;
/// Generator has returned / is completed
pub const RETURNED: usize = 1;
/// Generator has been poisoned
pub const POISONED: usize = 2;

const UNRESUMED_NAME: &'static str = "Unresumed";
const RETURNED_NAME: &'static str = "Returned";
const POISONED_NAME: &'static str = "Panicked";

/// The valid variant indices of this Generator.
#[inline]
pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Range<VariantIdx> {
// FIXME requires optimized MIR
let num_variants = self.state_tys(def_id, tcx).count();
(VariantIdx::new(0)..VariantIdx::new(num_variants))
}

/// The discriminant for the given variant. Panics if the variant_index is
/// out of range.
#[inline]
pub fn discriminant_for_variant(
&self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>, variant_index: VariantIdx
) -> Discr<'tcx> {
// Generators don't support explicit discriminant values, so they are
// the same as the variant index.
assert!(self.variant_range(def_id, tcx).contains(&variant_index));
Discr { val: variant_index.as_usize() as u128, ty: self.discr_ty(tcx) }
}

/// The set of all discriminants for the Generator, enumerated with their
/// variant indices.
#[inline]
pub fn discriminants(
&'a self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>
) -> impl Iterator<Item=(VariantIdx, Discr<'tcx>)> + Captures<'gcx> + 'a {
self.variant_range(def_id, tcx).map(move |index| {
(index, Discr { val: index.as_usize() as u128, ty: self.discr_ty(tcx) })
})
}

/// Calls `f` with a reference to the name of the enumerator for the given
/// variant `v`.
#[inline]
pub fn map_variant_name<R>(&self, v: VariantIdx, f: impl FnOnce(&str) -> R) -> R {
let name = match v.as_usize() {
Self::UNRESUMED => Self::UNRESUMED_NAME,
Self::RETURNED => Self::RETURNED_NAME,
Self::POISONED => Self::POISONED_NAME,
_ => {
return f(&format!("variant#{}", v.as_usize()));
}
};
f(name)
}

/// The type of the state discriminant used in the generator type.
#[inline]
pub fn discr_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
tcx.types.u32
}

/// This returns the types of the MIR locals which had to be stored across suspension points.
/// It is calculated in rustc_mir::transform::generator::StateTransform.
/// All the types here must be in the tuple in GeneratorInterior.
pub fn state_tys(
self,
def_id: DefId,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
) -> impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a {
let state = tcx.generator_layout(def_id).fields.iter();
state.map(move |d| d.ty.subst(tcx, self.substs))
}

/// This is the types of the fields of a generate which
/// is available before the generator transformation.
/// It includes the upvars and the state discriminant which is u32.
pub fn pre_transforms_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
///
/// The locals are grouped by their variant number. Note that some locals may
/// be repeated in multiple variants.
#[inline]
pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a>
{
self.upvar_tys(def_id, tcx).chain(iter::once(tcx.types.u32))
tcx.generator_layout(def_id)
.variant_fields.iter()
.map(move |v| v.iter().map(move |d| d.ty.subst(tcx, self.substs)))
}

/// This is the types of all the fields stored in a generator.
/// It includes the upvars, state types and the state discriminant which is u32.
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a
/// This is the types of the fields of a generator which are not stored in a
/// variant.
#[inline]
pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
self.upvar_tys(def_id, tcx)
}
}

Expand Down Expand Up @@ -1994,6 +2050,34 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}

/// If the type contains variants, returns the valid range of variant indices.
/// FIXME This requires the optimized MIR in the case of generators.
#[inline]
pub fn variant_range(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Range<VariantIdx>> {
match self.sty {
TyKind::Adt(adt, _) => Some(adt.variant_range()),
TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)),
_ => None,
}
}

/// If the type contains variants, returns the variant for `variant_index`.
/// Panics if `variant_index` is out of range.
/// FIXME This requires the optimized MIR in the case of generators.
#[inline]
pub fn discriminant_for_variant(
&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: VariantIdx
) -> Option<Discr<'tcx>> {
match self.sty {
TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)),
TyKind::Generator(def_id, substs, _) =>
Some(substs.discriminant_for_variant(def_id, tcx, variant_index)),
_ => None,
}
}

/// Push onto `out` the regions directly referenced from this type (but not
/// types reachable from this type via `walk_tys`). This ignores late-bound
/// regions binders.
Expand Down
Loading

0 comments on commit faed5e2

Please sign in to comment.