Skip to content

Commit 7fa37a3

Browse files
committed
WIP
1 parent 3bb9eec commit 7fa37a3

File tree

5 files changed

+169
-50
lines changed

5 files changed

+169
-50
lines changed

compiler/rustc_codegen_llvm/src/type_of.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ fn struct_llfields<'a, 'tcx>(
118118
target_offset,
119119
effective_field_align.bytes()
120120
);
121+
if target_offset < offset {
122+
println!("BUG!");
123+
tracing::info!("BUG LAYOUT: {:#?}", layout);
124+
tracing::info!("i={}, offset={:?}, target_offset={:?}", i, offset, target_offset);
125+
tracing::info!("field0={:#?}", layout.field(cx, 0));
126+
tracing::info!("field1={:#?}", layout.field(cx, 1));
127+
}
121128
assert!(target_offset >= offset);
122129
let padding = target_offset - offset;
123130
if padding != Size::ZERO {

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,11 +1418,15 @@ enum SavedLocalEligibility {
14181418
// Also included in the layout are the upvars and the discriminant.
14191419
// These are included as fields on the "outer" layout; they are not part
14201420
// of any variant.
1421+
// TODO: change documentation
14211422
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
14221423
/// Compute the eligibility and assignment of each local.
1424+
/// Saved locals corresponding to upvars will not be returned in the return value, as they are
1425+
/// already stored (saved) in the generator implicitly.
14231426
fn generator_saved_local_eligibility(
14241427
&self,
14251428
info: &GeneratorLayout<'tcx>,
1429+
upvar_count: usize,
14261430
) -> (BitSet<GeneratorSavedLocal>, IndexVec<GeneratorSavedLocal, SavedLocalEligibility>) {
14271431
use SavedLocalEligibility::*;
14281432

@@ -1498,7 +1502,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
14981502
let mut used_variants = BitSet::new_empty(info.variant_fields.len());
14991503
for assignment in &assignments {
15001504
if let Assigned(idx) = assignment {
1501-
used_variants.insert(*idx);
1505+
if *idx != VariantIdx::new(0) {
1506+
used_variants.insert(*idx);
1507+
}
15021508
}
15031509
}
15041510
if used_variants.count() < 2 {
@@ -1511,8 +1517,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
15111517

15121518
// Write down the order of our locals that will be promoted to the prefix.
15131519
{
1520+
for upvar in 0..upvar_count {
1521+
let local = GeneratorSavedLocal::new(upvar);
1522+
ineligible_locals.remove(local);
1523+
assignments[local] = Ineligible(Some(upvar as u32));
1524+
}
15141525
for (idx, local) in ineligible_locals.iter().enumerate() {
1515-
assignments[local] = Ineligible(Some(idx as u32));
1526+
// Skip over tag and upvars
1527+
assignments[local] = Ineligible(Some((upvar_count + 1 + idx) as u32));
15161528
}
15171529
}
15181530
debug!("generator saved local assignments: {:?}", assignments);
@@ -1535,12 +1547,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
15351547
None => return Err(LayoutError::Unknown(ty)),
15361548
Some(info) => info,
15371549
};
1538-
let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info);
1550+
let upvar_count = substs.as_generator().prefix_tys().count();
1551+
1552+
let (ineligible_locals, assignments) =
1553+
self.generator_saved_local_eligibility(&info, upvar_count);
15391554

15401555
// Build a prefix layout, including "promoting" all ineligible
15411556
// locals as part of the prefix. We compute the layout of all of
15421557
// these fields at once to get optimal packing.
1543-
let tag_index = substs.as_generator().prefix_tys().count();
1558+
let tag_index = upvar_count;
15441559

15451560
// `info.variant_fields` already accounts for the reserved variants, so no need to add them.
15461561
let max_discr = (info.variant_fields.len() - 1) as u128;
@@ -1558,6 +1573,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
15581573
.map(|local| subst_field(info.field_tys[local]))
15591574
.map(|ty| tcx.mk_maybe_uninit(ty))
15601575
.map(|ty| self.layout_of(ty));
1576+
1577+
// The upvars are stored at the beginning of the generators
1578+
// They are also included in the 0th (unresumed) variant
1579+
// The promoted locals are placed directly after the upvars.
1580+
// Because of this the rest of the code can handle upvars and promoted locals in a generic
1581+
// way.
15611582
let prefix_layouts = substs
15621583
.as_generator()
15631584
.prefix_tys()
@@ -1580,35 +1601,57 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
15801601
// GeneratorLayout.
15811602
debug!("prefix = {:#?}", prefix);
15821603
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
1583-
FieldsShape::Arbitrary { mut offsets, memory_index } => {
1584-
let mut inverse_memory_index = invert_mapping(&memory_index);
1585-
1586-
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
1587-
// "outer" and "promoted" fields respectively.
1588-
let b_start = (tag_index + 1) as u32;
1589-
let offsets_b = offsets.split_off(b_start as usize);
1590-
let offsets_a = offsets;
1591-
1592-
// Disentangle the "a" and "b" components of `inverse_memory_index`
1593-
// by preserving the order but keeping only one disjoint "half" each.
1594-
// FIXME(eddyb) build a better abstraction for permutations, if possible.
1595-
let inverse_memory_index_b: Vec<_> =
1596-
inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect();
1597-
inverse_memory_index.retain(|&i| i < b_start);
1598-
let inverse_memory_index_a = inverse_memory_index;
1599-
1600-
// Since `inverse_memory_index_{a,b}` each only refer to their
1604+
FieldsShape::Arbitrary { offsets, memory_index } => {
1605+
let inverse_memory_index = invert_mapping(&memory_index);
1606+
1607+
// Outer fields - upvars and tag
1608+
let after_tag = tag_index + 1;
1609+
let offsets_outer: Vec<_> = offsets.iter().take(after_tag).cloned().collect();
1610+
let mut inverse_memory_index_outer = inverse_memory_index.clone();
1611+
inverse_memory_index_outer.retain(|&i| i < after_tag as u32);
1612+
1613+
// Promoted fields - upvars and promoted locals
1614+
let offsets_promoted = offsets;
1615+
let inverse_memory_index_promoted = inverse_memory_index;
1616+
/*let mut offsets_promoted = offsets;
1617+
offsets_promoted.remove(tag_index);
1618+
// Keep indices lower than tag, remove tag, and lower indices higher than tag
1619+
let tag_source_index = inverse_memory_index[tag_index];
1620+
let inverse_memory_index_promoted: Vec<_> = inverse_memory_index
1621+
.into_iter()
1622+
.filter_map(|source_index| {
1623+
if source_index != tag_source_index {
1624+
if source_index >= tag_source_index {
1625+
Some(source_index - 1)
1626+
} else {
1627+
Some(source_index)
1628+
}
1629+
} else {
1630+
None
1631+
}
1632+
})
1633+
.collect();*/
1634+
1635+
// Since `inverse_memory_index_{outer,promoted}` each only refer to their
16011636
// respective fields, they can be safely inverted
1602-
let memory_index_a = invert_mapping(&inverse_memory_index_a);
1603-
let memory_index_b = invert_mapping(&inverse_memory_index_b);
1637+
let memory_index_outer = invert_mapping(&inverse_memory_index_outer);
1638+
let memory_index_promoted = invert_mapping(&inverse_memory_index_promoted);
16041639

1605-
let outer_fields =
1606-
FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a };
1607-
(outer_fields, offsets_b, memory_index_b)
1640+
let outer_fields = FieldsShape::Arbitrary {
1641+
offsets: offsets_outer,
1642+
memory_index: memory_index_outer,
1643+
};
1644+
(outer_fields, offsets_promoted, memory_index_promoted)
16081645
}
16091646
_ => bug!(),
16101647
};
16111648

1649+
for (index, layout) in prefix_layouts.iter().enumerate() {
1650+
info!("LAYOUT, prefix_layout_{}={:#?}", index, layout);
1651+
}
1652+
info!("LAYOUT, tag_index={:?}, upvar_count={:?}, prefix_layouts.len()={:?}, prefix_size={:?}", tag_index, upvar_count, prefix_layouts.len(), prefix_size);
1653+
info!("LAYOUT, outer_fields={:#?}, promoted_offsets={:#?}, promoted_memory_index={:#?}", outer_fields, promoted_offsets, promoted_memory_index);
1654+
16121655
let mut size = prefix.size;
16131656
let mut align = prefix.align;
16141657
let variants = info
@@ -1626,11 +1669,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
16261669
})
16271670
.map(|local| subst_field(info.field_tys[*local]));
16281671

1672+
let x = variant_only_tys
1673+
.map(|ty| self.layout_of(ty))
1674+
.collect::<Result<Vec<_>, _>>()?;
1675+
info!("VARIANT, index={:?}, variant_only_tys={:#?}", index, x);
1676+
16291677
let mut variant = self.univariant_uninterned(
16301678
ty,
1631-
&variant_only_tys
1632-
.map(|ty| self.layout_of(ty))
1633-
.collect::<Result<Vec<_>, _>>()?,
1679+
&x,
16341680
&ReprOptions::default(),
16351681
StructKind::Prefixed(prefix_size, prefix_align.abi),
16361682
)?;
@@ -1713,7 +1759,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
17131759
size,
17141760
align,
17151761
});
1716-
debug!("generator layout ({:?}): {:#?}", ty, layout);
1762+
info!("generator layout ({:?}): {:#?}", ty, layout);
17171763
Ok(layout)
17181764
}
17191765

compiler/rustc_mir_build/src/build/expr/as_rvalue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
256256
let result = match substs {
257257
UpvarSubsts::Generator(substs) => {
258258
// We implicitly set the discriminant to 0. See
259-
// librustc_mir/transform/deaggregator.rs for details.
259+
// rustc_mir_transform/src/deaggregator.rs for details.
260260
let movability = movability.unwrap();
261261
Box::new(AggregateKind::Generator(closure_id, substs, movability))
262262
}

compiler/rustc_mir_transform/src/generator.rs

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,20 @@ fn replace_local<'tcx>(
425425
new_local
426426
}
427427

428+
/// Upvars are stored in locals directly after the return place and generator arguments.
429+
/// _0 = return place
430+
/// _1 = generator type
431+
/// _2 = resume type
432+
/// _3..._n = upvars
433+
/// TODO: where is this guaranteed?
434+
fn upvar_locals<'tcx>(upvars: &[Ty<'tcx>]) -> impl Iterator<Item = Local> {
435+
(0..upvars.len()).map(|index| Local::new(3 + index))
436+
}
437+
438+
fn is_upvar_local<'tcx>(upvars: &[Ty<'tcx>], local: Local) -> bool {
439+
local >= Local::new(3) && local < Local::new(3 + upvars.len())
440+
}
441+
428442
struct LivenessInfo {
429443
/// Which locals are live across any suspension point.
430444
saved_locals: GeneratorSavedLocals,
@@ -449,6 +463,7 @@ fn locals_live_across_suspend_points(
449463
tcx: TyCtxt<'tcx>,
450464
body: &Body<'tcx>,
451465
always_live_locals: &storage::AlwaysLiveLocals,
466+
upvars: &[Ty<'tcx>],
452467
movable: bool,
453468
) -> LivenessInfo {
454469
let body_ref: &Body<'_> = &body;
@@ -490,6 +505,11 @@ fn locals_live_across_suspend_points(
490505
let mut source_info_at_suspension_points = Vec::new();
491506
let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
492507

508+
// Upvars are always stored in the generator
509+
for local in upvar_locals(upvars) {
510+
live_locals_at_any_suspension_point.insert(local);
511+
}
512+
493513
for (block, data) in body.basic_blocks().iter_enumerated() {
494514
if let TerminatorKind::Yield { .. } = data.terminator().kind {
495515
let loc = Location { block, statement_index: data.statements.len() };
@@ -762,6 +782,7 @@ fn sanitize_witness<'tcx>(
762782
fn compute_layout<'tcx>(
763783
liveness: LivenessInfo,
764784
body: &mut Body<'tcx>,
785+
upvars: &[Ty<'tcx>],
765786
) -> (
766787
FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
767788
GeneratorLayout<'tcx>,
@@ -775,34 +796,44 @@ fn compute_layout<'tcx>(
775796
storage_liveness,
776797
} = liveness;
777798

778-
// Gather live local types and their indices.
779-
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
780-
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
781-
for (saved_local, local) in saved_locals.iter_enumerated() {
782-
locals.push(local);
783-
tys.push(body.local_decls[local].ty);
784-
debug!("generator saved local {:?} => {:?}", saved_local, local);
785-
}
786-
787-
// Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
799+
// Leave variants for the UNRESUMED, RETURNED, and POISONED states.
788800
// In debuginfo, these will correspond to the beginning (UNRESUMED) or end
789801
// (RETURNED, POISONED) of the function.
790802
const RESERVED_VARIANTS: usize = 3;
791803
let body_span = body.source_scopes[OUTERMOST_SOURCE_SCOPE].span;
792-
let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = [
804+
let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = std::array::IntoIter::new([
793805
SourceInfo::outermost(body_span.shrink_to_lo()),
794806
SourceInfo::outermost(body_span.shrink_to_hi()),
795807
SourceInfo::outermost(body_span.shrink_to_hi()),
796-
]
797-
.iter()
798-
.copied()
808+
])
799809
.collect();
800810

801811
// Build the generator variant field list.
802812
// Create a map from local indices to generator struct indices.
803813
let mut variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>> =
804814
iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect();
815+
816+
// Gather live local types and their indices.
817+
// Store upvars in the UNRESUMED variant.
818+
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
819+
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
820+
let unresumed_idx = VariantIdx::new(UNRESUMED);
805821
let mut remap = FxHashMap::default();
822+
823+
for (saved_local, local) in saved_locals.iter_enumerated() {
824+
locals.push(local);
825+
tys.push(body.local_decls[local].ty);
826+
827+
if is_upvar_local(upvars, local) {
828+
variant_fields[unresumed_idx].push(saved_local);
829+
remap.entry(locals[saved_local]).or_insert((
830+
tys[saved_local],
831+
unresumed_idx,
832+
local.as_usize() - 3,
833+
));
834+
}
835+
}
836+
806837
for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
807838
let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx);
808839
let mut fields = IndexVec::new();
@@ -1249,7 +1280,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
12491280
ty::Generator(_, substs, movability) => {
12501281
let substs = substs.as_generator();
12511282
(
1252-
substs.upvar_tys().collect(),
1283+
substs.upvar_tys().collect::<Vec<_>>(),
12531284
substs.witness(),
12541285
substs.discr_ty(tcx),
12551286
movability == hir::Movability::Movable,
@@ -1297,9 +1328,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
12971328
let always_live_locals = storage::AlwaysLiveLocals::new(&body);
12981329

12991330
let liveness_info =
1300-
locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
1331+
locals_live_across_suspend_points(tcx, body, &always_live_locals, &upvars, movable);
13011332

1302-
sanitize_witness(tcx, body, interior, upvars, &liveness_info.saved_locals);
1333+
sanitize_witness(tcx, body, interior, upvars.clone(), &liveness_info.saved_locals);
13031334

13041335
if tcx.sess.opts.debugging_opts.validate_mir {
13051336
let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
@@ -1314,7 +1345,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
13141345
// Extract locals which are live across suspension point into `layout`
13151346
// `remap` gives a mapping from local indices onto generator struct indices
13161347
// `storage_liveness` tells us which locals have live storage at suspension points
1317-
let (remap, layout, storage_liveness) = compute_layout(liveness_info, body);
1348+
let (remap, layout, storage_liveness) = compute_layout(liveness_info, body, &upvars);
13181349

13191350
let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
13201351

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-pass
2+
// edition:2018
3+
4+
async fn parameter_no_drop_no_use(_a: u32) {
5+
std::future::ready(()).await;
6+
}
7+
8+
async fn parameter_explicit_drop(a: u32) {
9+
std::future::ready(()).await;
10+
drop(a);
11+
}
12+
13+
struct HasDrop(u32);
14+
15+
impl Drop for HasDrop {
16+
fn drop(&mut self) {
17+
println!("drop");
18+
}
19+
}
20+
21+
async fn parameter_implicit_drop(_a: HasDrop) {
22+
std::future::ready(()).await;
23+
}
24+
25+
async fn parameter_rebind(a: HasDrop) {
26+
let _x = a;
27+
std::future::ready(()).await;
28+
}
29+
30+
fn main() {
31+
assert_eq!(8, std::mem::size_of_val(&parameter_no_drop_no_use(0)));
32+
assert_eq!(8, std::mem::size_of_val(&parameter_explicit_drop(0)));
33+
assert_eq!(8, std::mem::size_of_val(&parameter_implicit_drop(HasDrop(0))));
34+
assert_eq!(16, std::mem::size_of_val(&parameter_rebind(HasDrop(0))));
35+
}

0 commit comments

Comments
 (0)