Skip to content

Commit fb1f57e

Browse files
authored
Rollup merge of rust-lang#63034 - tmandry:reduce-generator-size-regressions, r=cramertj
Fix generator size regressions due to optimization I tested the generator optimizations in rust-lang#60187 and rust-lang#61922 on the Fuchsia build, and noticed that some small generators (about 8% of the async fns in our build) increased in size slightly. This is because in rust-lang#60187 we split the fields into two groups, a "prefix" non-overlap region and an overlap region, and lay them out separately. This can introduce unnecessary padding bytes between the two groups. In every single case in the Fuchsia build, it was due to there being only a single variant being used in the overlap region. This means that we aren't doing any overlapping, period. So it's better to combine the two regions into one and lay out all the fields at once, which is what this change does. r? @cramertj cc @eddyb @Zoxc
2 parents 6a91782 + b40788e commit fb1f57e

File tree

6 files changed

+75
-10
lines changed

6 files changed

+75
-10
lines changed

src/libcore/mem/maybe_uninit.rs

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ use crate::mem::ManuallyDrop;
209209
/// guarantee may evolve.
210210
#[allow(missing_debug_implementations)]
211211
#[stable(feature = "maybe_uninit", since = "1.36.0")]
212+
// Lang item so we can wrap other types in it. This is useful for generators.
213+
#[cfg_attr(not(bootstrap), lang = "maybe_uninit")]
212214
#[derive(Copy)]
213215
#[repr(transparent)]
214216
pub union MaybeUninit<T> {

src/librustc/middle/lang_items.rs

+2
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ language_item_table! {
365365

366366
ManuallyDropItem, "manually_drop", manually_drop, Target::Struct;
367367

368+
MaybeUninitLangItem, "maybe_uninit", maybe_uninit, Target::Union;
369+
368370
DebugTraitLangItem, "debug_trait", debug_trait, Target::Trait;
369371

370372
// Align offset for stride != 1, must not panic.

src/librustc/ty/context.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -2347,18 +2347,17 @@ impl<'tcx> TyCtxt<'tcx> {
23472347
self.mk_ty(Foreign(def_id))
23482348
}
23492349

2350-
pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
2351-
let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem);
2352-
let adt_def = self.adt_def(def_id);
2353-
let substs = InternalSubsts::for_item(self, def_id, |param, substs| {
2350+
fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
2351+
let adt_def = self.adt_def(wrapper_def_id);
2352+
let substs = InternalSubsts::for_item(self, wrapper_def_id, |param, substs| {
23542353
match param.kind {
23552354
GenericParamDefKind::Lifetime |
23562355
GenericParamDefKind::Const => {
23572356
bug!()
23582357
}
23592358
GenericParamDefKind::Type { has_default, .. } => {
23602359
if param.index == 0 {
2361-
ty.into()
2360+
ty_param.into()
23622361
} else {
23632362
assert!(has_default);
23642363
self.type_of(param.def_id).subst(self, substs).into()
@@ -2369,6 +2368,18 @@ impl<'tcx> TyCtxt<'tcx> {
23692368
self.mk_ty(Adt(adt_def, substs))
23702369
}
23712370

2371+
#[inline]
2372+
pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
2373+
let def_id = self.require_lang_item(lang_items::OwnedBoxLangItem);
2374+
self.mk_generic_adt(def_id, ty)
2375+
}
2376+
2377+
#[inline]
2378+
pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
2379+
let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem);
2380+
self.mk_generic_adt(def_id, ty)
2381+
}
2382+
23722383
#[inline]
23732384
pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
23742385
self.mk_ty(RawPtr(tm))

src/librustc/ty/layout.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,27 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
13681368
}
13691369
}
13701370

1371+
// Count the number of variants in use. If only one of them, then it is
1372+
// impossible to overlap any locals in our layout. In this case it's
1373+
// always better to make the remaining locals ineligible, so we can
1374+
// lay them out with the other locals in the prefix and eliminate
1375+
// unnecessary padding bytes.
1376+
{
1377+
let mut used_variants = BitSet::new_empty(info.variant_fields.len());
1378+
for assignment in &assignments {
1379+
match assignment {
1380+
Assigned(idx) => { used_variants.insert(*idx); }
1381+
_ => {}
1382+
}
1383+
}
1384+
if used_variants.count() < 2 {
1385+
for assignment in assignments.iter_mut() {
1386+
*assignment = Ineligible(None);
1387+
}
1388+
ineligible_locals.insert_all();
1389+
}
1390+
}
1391+
13711392
// Write down the order of our locals that will be promoted to the prefix.
13721393
{
13731394
let mut idx = 0u32;
@@ -1406,24 +1427,21 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
14061427
Abi::Scalar(s) => s.clone(),
14071428
_ => bug!(),
14081429
};
1409-
// FIXME(eddyb) wrap each promoted type in `MaybeUninit` so that they
1410-
// don't poison the `largest_niche` or `abi` fields of `prefix`.
14111430
let promoted_layouts = ineligible_locals.iter()
14121431
.map(|local| subst_field(info.field_tys[local]))
1432+
.map(|ty| tcx.mk_maybe_uninit(ty))
14131433
.map(|ty| self.layout_of(ty));
14141434
let prefix_layouts = substs.prefix_tys(def_id, tcx)
14151435
.map(|ty| self.layout_of(ty))
14161436
.chain(iter::once(Ok(discr_layout)))
14171437
.chain(promoted_layouts)
14181438
.collect::<Result<Vec<_>, _>>()?;
1419-
let mut prefix = self.univariant_uninterned(
1439+
let prefix = self.univariant_uninterned(
14201440
ty,
14211441
&prefix_layouts,
14221442
&ReprOptions::default(),
14231443
StructKind::AlwaysSized,
14241444
)?;
1425-
// FIXME(eddyb) need `MaybeUninit` around promoted types (see above).
1426-
prefix.largest_niche = None;
14271445

14281446
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
14291447

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test that niche finding works with captured generator upvars.
2+
3+
#![feature(generators)]
4+
5+
use std::mem::size_of_val;
6+
7+
fn take<T>(_: T) {}
8+
9+
fn main() {
10+
let x = false;
11+
let gen1 = || {
12+
yield;
13+
take(x);
14+
};
15+
16+
assert_eq!(size_of_val(&gen1), size_of_val(&Some(gen1)));
17+
}

src/test/ui/async-await/issues/issue-59972.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Incorrect handling of uninhabited types could cause us to mark generator
2+
// types as entirely uninhabited, when they were in fact constructible. This
3+
// caused us to hit "unreachable" code (illegal instruction on x86).
4+
15
// run-pass
26

37
// compile-flags: --edition=2018
@@ -19,7 +23,18 @@ async fn contains_never() {
1923
let error2 = error;
2024
}
2125

26+
#[allow(unused)]
27+
async fn overlap_never() {
28+
let error1 = uninhabited_async();
29+
noop().await;
30+
let error2 = uninhabited_async();
31+
drop(error1);
32+
noop().await;
33+
drop(error2);
34+
}
35+
2236
#[allow(unused_must_use)]
2337
fn main() {
2438
contains_never();
39+
overlap_never();
2540
}

0 commit comments

Comments
 (0)