Skip to content

Commit 6b8fde6

Browse files
committed
Auto merge of rust-lang#120168 - dingxiangfei2009:coroutine-upvar, r=<try>
Relocate coroutine upvars into Unresumed state Related to rust-lang#62958 This PR is an attempt to address the async/coroutine size issue by allowing independent def-use/liveness analysis on individual upvars in coroutines. It has appeared to address partially the size doubling issue introduced by the use of upvars. However, there are caveats detailed in the following list that I would like to address before turning this draft in. - The treatment here towards the `ty::Coroutine` in MIR passes is unfortunately "messier" than my liking, which is something I definitely want to change. I propose to promote upvars into `Body<'tcx>` along with `local_decls`, so that we can safely handle them safely. I would happily open a new separate PR to improve the upvar management. - It is not a generic solution, yet. For instance, we are still doubling the size in the example of rust-lang#62958. If we insert a pass before MIR type analysis to remove unnecessary drops, which we can, that particular size doubling will be solved. However, if a `Future` upvar is alive across more than one yield points, that upvar is still ineligible. It makes sense because we would like to minimize moving of variant fields. How to handle these upvars is not the focus of this PR for now. Out of expectation of possible change in the high level plan, I am keeping this as a draft in hope of invoking conversations. 🙇 cc `@pnkfelix` for the context.
2 parents b14d8b2 + a68481f commit 6b8fde6

File tree

60 files changed

+1078
-431
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1078
-431
lines changed

compiler/rustc_abi/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1523,8 +1523,7 @@ pub struct LayoutS<FieldIdx: Idx, VariantIdx: Idx> {
15231523

15241524
/// Encodes information about multi-variant layouts.
15251525
/// Even with `Multiple` variants, a layout still has its own fields! Those are then
1526-
/// shared between all variants. One of them will be the discriminant,
1527-
/// but e.g. coroutines can have more.
1526+
/// shared between all variants. One of them will be the discriminant.
15281527
///
15291528
/// To access all fields of this layout, both `fields` and the fields of the active variant
15301529
/// must be taken into account.

compiler/rustc_borrowck/src/type_check/mod.rs

+13-18
Original file line numberDiff line numberDiff line change
@@ -816,22 +816,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
816816
}),
817817
};
818818
}
819-
ty::CoroutineClosure(_, args) => {
820-
return match args.as_coroutine_closure().upvar_tys().get(field.index()) {
819+
ty::CoroutineClosure(_def_id, args) => {
820+
let upvar_tys = args.as_coroutine_closure().upvar_tys();
821+
return match upvar_tys.get(field.index()) {
821822
Some(&ty) => Ok(ty),
822-
None => Err(FieldAccessError::OutOfRange {
823-
field_count: args.as_coroutine_closure().upvar_tys().len(),
824-
}),
823+
None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }),
825824
};
826825
}
827-
ty::Coroutine(_, args) => {
828-
// Only prefix fields (upvars and current state) are
829-
// accessible without a variant index.
830-
return match args.as_coroutine().prefix_tys().get(field.index()) {
831-
Some(ty) => Ok(*ty),
832-
None => Err(FieldAccessError::OutOfRange {
833-
field_count: args.as_coroutine().prefix_tys().len(),
834-
}),
826+
ty::Coroutine(_def_id, args) => {
827+
let upvar_tys = args.as_coroutine().upvar_tys();
828+
return match upvar_tys.get(field.index()) {
829+
Some(&ty) => Ok(ty),
830+
None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }),
835831
};
836832
}
837833
ty::Tuple(tys) => {
@@ -1905,11 +1901,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
19051901
// It doesn't make sense to look at a field beyond the prefix;
19061902
// these require a variant index, and are not initialized in
19071903
// aggregate rvalues.
1908-
match args.as_coroutine().prefix_tys().get(field_index.as_usize()) {
1904+
let upvar_tys = args.as_coroutine().upvar_tys();
1905+
match upvar_tys.get(field_index.as_usize()) {
19091906
Some(ty) => Ok(*ty),
1910-
None => Err(FieldAccessError::OutOfRange {
1911-
field_count: args.as_coroutine().prefix_tys().len(),
1912-
}),
1907+
None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }),
19131908
}
19141909
}
19151910
AggregateKind::CoroutineClosure(_, args) => {
@@ -2534,7 +2529,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
25342529

25352530
self.prove_aggregate_predicates(aggregate_kind, location);
25362531

2537-
if *aggregate_kind == AggregateKind::Tuple {
2532+
if matches!(aggregate_kind, AggregateKind::Tuple) {
25382533
// tuple rvalue field type is always the type of the op. Nothing to check here.
25392534
return;
25402535
}

compiler/rustc_codegen_cranelift/src/base.rs

+3
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,9 @@ fn codegen_stmt<'tcx>(
810810
let variant_dest = lval.downcast_variant(fx, variant_index);
811811
(variant_index, variant_dest, active_field_index)
812812
}
813+
mir::AggregateKind::Coroutine(_, _) => {
814+
(FIRST_VARIANT, lval.downcast_variant(fx, FIRST_VARIANT), None)
815+
}
813816
_ => (FIRST_VARIANT, lval, None),
814817
};
815818
if active_field_index.is_some() {

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
10801080
closure_or_coroutine_di_node: &'ll DIType,
10811081
) -> SmallVec<&'ll DIType> {
10821082
let (&def_id, up_var_tys) = match closure_or_coroutine_ty.kind() {
1083-
ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().prefix_tys()),
1083+
ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().upvar_tys()),
10841084
ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()),
10851085
ty::CoroutineClosure(def_id, args) => (def_id, args.as_coroutine_closure().upvar_tys()),
10861086
_ => {

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -686,12 +686,12 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>(
686686
let coroutine_layout =
687687
cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap();
688688

689-
let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id);
690689
let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx);
691690
let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len();
692691

693692
let tag_base_type = tag_base_type(cx, coroutine_type_and_layout);
694693

694+
let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id);
695695
let variant_names_type_di_node = build_variant_names_type_di_node(
696696
cx,
697697
coroutine_type_di_node,

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs

+4-25
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
326326
coroutine_type_and_layout: TyAndLayout<'tcx>,
327327
coroutine_type_di_node: &'ll DIType,
328328
coroutine_layout: &CoroutineLayout<'tcx>,
329-
common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
329+
_common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
330330
) -> &'ll DIType {
331331
let variant_name = CoroutineArgs::variant_name(variant_index);
332332
let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
@@ -337,7 +337,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
337337

338338
let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index);
339339

340-
let coroutine_args = match coroutine_type_and_layout.ty.kind() {
340+
let _coroutine_args = match coroutine_type_and_layout.ty.kind() {
341341
ty::Coroutine(_, args) => args.as_coroutine(),
342342
_ => unreachable!(),
343343
};
@@ -355,7 +355,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
355355
),
356356
|cx, variant_struct_type_di_node| {
357357
// Fields that just belong to this variant/state
358-
let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
358+
(0..variant_layout.fields.count())
359359
.map(|field_index| {
360360
let coroutine_saved_local = coroutine_layout.variant_fields[variant_index]
361361
[FieldIdx::from_usize(field_index)];
@@ -377,28 +377,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
377377
type_di_node(cx, field_type),
378378
)
379379
})
380-
.collect();
381-
382-
// Fields that are common to all states
383-
let common_fields: SmallVec<_> = coroutine_args
384-
.prefix_tys()
385-
.iter()
386-
.zip(common_upvar_names)
387-
.enumerate()
388-
.map(|(index, (upvar_ty, upvar_name))| {
389-
build_field_di_node(
390-
cx,
391-
variant_struct_type_di_node,
392-
upvar_name.as_str(),
393-
cx.size_and_align_of(upvar_ty),
394-
coroutine_type_and_layout.fields.offset(index),
395-
DIFlags::FlagZero,
396-
type_di_node(cx, upvar_ty),
397-
)
398-
})
399-
.collect();
400-
401-
state_specific_fields.into_iter().chain(common_fields).collect()
380+
.collect()
402381
},
403382
|cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
404383
)

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
126126
let variant_dest = dest.project_downcast(bx, variant_index);
127127
(variant_index, variant_dest, active_field_index)
128128
}
129+
mir::AggregateKind::Coroutine(_, _) => {
130+
(FIRST_VARIANT, dest.project_downcast(bx, FIRST_VARIANT), None)
131+
}
129132
_ => (FIRST_VARIANT, dest, None),
130133
};
131134
if active_field_index.is_some() {

compiler/rustc_const_eval/src/interpret/step.rs

+3
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
303303
let variant_dest = self.project_downcast(dest, variant_index)?;
304304
(variant_index, variant_dest, active_field_index)
305305
}
306+
mir::AggregateKind::Coroutine(_def_id, _args) => {
307+
(FIRST_VARIANT, self.project_downcast(dest, FIRST_VARIANT)?, None)
308+
}
306309
_ => (FIRST_VARIANT, dest.clone(), None),
307310
};
308311
if active_field_index.is_some() {

compiler/rustc_const_eval/src/transform/validate.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -743,14 +743,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
743743
};
744744

745745
ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args)
746+
} else if let Some(&ty) = args.as_coroutine().upvar_tys().get(f.as_usize())
747+
{
748+
ty
746749
} else {
747-
let Some(&f_ty) = args.as_coroutine().prefix_tys().get(f.index())
748-
else {
749-
fail_out_of_bounds(self, location);
750-
return;
751-
};
752-
753-
f_ty
750+
fail_out_of_bounds(self, location);
751+
return;
754752
};
755753

756754
check_equal(self, location, f_ty);

compiler/rustc_middle/src/mir/query.rs

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ pub struct CoroutineLayout<'tcx> {
5151
/// await).
5252
pub variant_source_info: IndexVec<VariantIdx, SourceInfo>,
5353

54+
/// The starting index of upvars.
55+
pub upvar_start: CoroutineSavedLocal,
56+
5457
/// Which saved locals are storage-live at the same time. Locals that do not
5558
/// have conflicts with each other are allowed to overlap in the computed
5659
/// layout.
@@ -101,6 +104,7 @@ impl Debug for CoroutineLayout<'_> {
101104
}
102105

103106
fmt.debug_struct("CoroutineLayout")
107+
.field("upvar_start", &self.upvar_start)
104108
.field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated()))
105109
.field(
106110
"variant_fields",
@@ -110,7 +114,12 @@ impl Debug for CoroutineLayout<'_> {
110114
.map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))),
111115
),
112116
)
117+
.field("field_names", &MapPrinter::new(self.field_names.iter_enumerated()))
113118
.field("storage_conflicts", &self.storage_conflicts)
119+
.field(
120+
"variant_source_info",
121+
&MapPrinter::new(self.variant_source_info.iter_enumerated()),
122+
)
114123
.finish()
115124
}
116125
}

compiler/rustc_middle/src/mir/tcx.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,11 @@ impl<'tcx> PlaceTy<'tcx> {
7474
T: ::std::fmt::Debug + Copy,
7575
{
7676
if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
77-
bug!("cannot use non field projection on downcasted place")
77+
bug!(
78+
"cannot use non field projection on downcasted place from {:?} (variant {:?}), got {elem:?}",
79+
self.ty,
80+
self.variant_index
81+
)
7882
}
7983
let answer = match *elem {
8084
ProjectionElem::Deref => {

compiler/rustc_middle/src/ty/layout.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ where
856856
if i == tag_field {
857857
return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
858858
}
859-
TyMaybeWithLayout::Ty(args.as_coroutine().prefix_tys()[i])
859+
bug!("coroutine has no prefix field");
860860
}
861861
},
862862

compiler/rustc_middle/src/ty/sty.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
609609
witness: witness.expect_ty(),
610610
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
611611
},
612-
_ => bug!("coroutine args missing synthetics"),
612+
_ => bug!("coroutine args missing synthetics, got {:?}", self.args),
613613
}
614614
}
615615

@@ -762,13 +762,6 @@ impl<'tcx> CoroutineArgs<'tcx> {
762762
})
763763
})
764764
}
765-
766-
/// This is the types of the fields of a coroutine which are not stored in a
767-
/// variant.
768-
#[inline]
769-
pub fn prefix_tys(self) -> &'tcx List<Ty<'tcx>> {
770-
self.upvar_tys()
771-
}
772765
}
773766

774767
#[derive(Debug, Copy, Clone, HashStable)]

compiler/rustc_mir_dataflow/src/framework/engine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ where
288288
}
289289

290290
None if dump_enabled(tcx, A::NAME, def_id) => {
291-
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
291+
create_dump_file(tcx, ".dot", true, A::NAME, &pass_name.unwrap_or("-----"), body)?
292292
}
293293

294294
_ => return (Ok(()), results),

0 commit comments

Comments
 (0)