Skip to content

Commit 7c7ac84

Browse files
Auto merge of #135527 - dingxiangfei2009:move-upvars-to-locals-for-tests, r=<try>
Move coroutine upvars into locals for better memory economy
2 parents 99b9a88 + 073de09 commit 7c7ac84

File tree

107 files changed

+2954
-661
lines changed

Some content is hidden

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

107 files changed

+2954
-661
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fmt::{self, Write};
33
use std::ops::{Bound, Deref};
44
use std::{cmp, iter};
55

6+
pub use coroutine::PackCoroutineLayout;
67
use rustc_hashes::Hash64;
78
use rustc_index::Idx;
89
use rustc_index::bit_set::BitMatrix;
@@ -210,17 +211,21 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
210211
>(
211212
&self,
212213
local_layouts: &IndexSlice<LocalIdx, F>,
213-
prefix_layouts: IndexVec<FieldIdx, F>,
214+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
215+
upvar_layouts: IndexVec<FieldIdx, F>,
214216
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
215217
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
218+
pack: PackCoroutineLayout,
216219
tag_to_layout: impl Fn(Scalar) -> F,
217220
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
218221
coroutine::layout(
219222
self,
220223
local_layouts,
221-
prefix_layouts,
224+
relocated_upvars,
225+
upvar_layouts,
222226
variant_fields,
223227
storage_conflicts,
228+
pack,
224229
tag_to_layout,
225230
)
226231
}

compiler/rustc_abi/src/layout/coroutine.rs

Lines changed: 100 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ use crate::{
3030
StructKind, TagEncoding, Variants, WrappingRange,
3131
};
3232

33+
/// This option controls how coroutine saved locals are packed
34+
/// into the coroutine state data
35+
#[derive(Debug, Clone, Copy)]
36+
pub enum PackCoroutineLayout {
37+
/// The classic layout where captures are always promoted to coroutine state prefix
38+
Classic,
39+
/// Captures are first saved into the `UNRESUME` state and promoted
40+
/// when they are used across more than one suspension
41+
CapturesOnly,
42+
}
43+
3344
/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
3445
#[derive(Clone, Debug, PartialEq)]
3546
enum SavedLocalEligibility<VariantIdx, FieldIdx> {
@@ -145,20 +156,23 @@ pub(super) fn layout<
145156
>(
146157
calc: &super::LayoutCalculator<impl HasDataLayout>,
147158
local_layouts: &IndexSlice<LocalIdx, F>,
148-
mut prefix_layouts: IndexVec<FieldIdx, F>,
159+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
160+
upvar_layouts: IndexVec<FieldIdx, F>,
149161
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
150162
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
163+
pack: PackCoroutineLayout,
151164
tag_to_layout: impl Fn(Scalar) -> F,
152165
) -> super::LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
153166
use SavedLocalEligibility::*;
154167

155168
let (ineligible_locals, assignments) =
156169
coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
157170

158-
// Build a prefix layout, including "promoting" all ineligible
159-
// locals as part of the prefix. We compute the layout of all of
160-
// these fields at once to get optimal packing.
161-
let tag_index = prefix_layouts.next_index();
171+
// Build a prefix layout, consisting of only the state tag and, as per request, upvars
172+
let tag_index = match pack {
173+
PackCoroutineLayout::CapturesOnly => FieldIdx::new(0),
174+
PackCoroutineLayout::Classic => upvar_layouts.next_index(),
175+
};
162176

163177
// `variant_fields` already accounts for the reserved variants, so no need to add them.
164178
let max_discr = (variant_fields.len() - 1) as u128;
@@ -169,17 +183,28 @@ pub(super) fn layout<
169183
};
170184

171185
let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]);
172-
prefix_layouts.push(tag_to_layout(tag));
173-
prefix_layouts.extend(promoted_layouts);
186+
// FIXME: when we introduce more pack scheme, we need to change the prefix layout here
187+
let prefix_layouts: IndexVec<_, _> = match pack {
188+
PackCoroutineLayout::Classic => {
189+
// Classic scheme packs the states as follows
190+
// [ <upvars>.. , <state tag>, <promoted ineligibles>] ++ <variant data>
191+
// In addition, UNRESUME overlaps with the <upvars> part
192+
upvar_layouts.into_iter().chain([tag_to_layout(tag)]).chain(promoted_layouts).collect()
193+
}
194+
PackCoroutineLayout::CapturesOnly => {
195+
[tag_to_layout(tag)].into_iter().chain(promoted_layouts).collect()
196+
}
197+
};
198+
debug!(?prefix_layouts, ?pack);
174199
let prefix =
175200
calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
176201

177202
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
178203

179-
// Split the prefix layout into the "outer" fields (upvars and
180-
// discriminant) and the "promoted" fields. Promoted fields will
181-
// get included in each variant that requested them in
182-
// CoroutineLayout.
204+
// Split the prefix layout into the discriminant and
205+
// the "promoted" fields.
206+
// Promoted fields will get included in each variant
207+
// that requested them in CoroutineLayout.
183208
debug!("prefix = {:#?}", prefix);
184209
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
185210
FieldsShape::Arbitrary { mut offsets, memory_index } => {
@@ -218,19 +243,67 @@ pub(super) fn layout<
218243
let variants = variant_fields
219244
.iter_enumerated()
220245
.map(|(index, variant_fields)| {
246+
let is_unresumed = index == VariantIdx::new(0);
247+
if is_unresumed && matches!(pack, PackCoroutineLayout::Classic) {
248+
let fields = FieldsShape::Arbitrary {
249+
offsets: (0..tag_index.index()).map(|i| outer_fields.offset(i)).collect(),
250+
memory_index: (0..tag_index.index())
251+
.map(|i| {
252+
(outer_fields.memory_index(i) + promoted_memory_index.len()) as u32
253+
})
254+
.collect(),
255+
};
256+
let align = prefix.align;
257+
let size = prefix.size;
258+
return Ok(LayoutData {
259+
fields,
260+
variants: Variants::Single { index },
261+
backend_repr: BackendRepr::Memory { sized: true },
262+
largest_niche: None,
263+
uninhabited: false,
264+
align,
265+
size,
266+
max_repr_align: None,
267+
unadjusted_abi_align: align.abi,
268+
randomization_seed: Default::default(),
269+
});
270+
}
271+
let mut is_ineligible = IndexVec::from_elem_n(None, variant_fields.len());
272+
for (field, &local) in variant_fields.iter_enumerated() {
273+
if is_unresumed {
274+
if let Some(inner_local) = relocated_upvars[local]
275+
&& let Ineligible(Some(promoted_field)) = assignments[inner_local]
276+
{
277+
is_ineligible.insert(field, promoted_field);
278+
continue;
279+
}
280+
}
281+
match assignments[local] {
282+
Assigned(v) if v == index => {}
283+
Ineligible(Some(promoted_field)) => {
284+
is_ineligible.insert(field, promoted_field);
285+
}
286+
Ineligible(None) => {
287+
panic!("an ineligible local should have been promoted into the prefix")
288+
}
289+
Assigned(_) => {
290+
panic!("an eligible local should have been assigned to exactly one variant")
291+
}
292+
Unassigned => {
293+
panic!("each saved local should have been inspected at least once")
294+
}
295+
}
296+
}
221297
// Only include overlap-eligible fields when we compute our variant layout.
222-
let variant_only_tys = variant_fields
223-
.iter()
224-
.filter(|local| match assignments[**local] {
225-
Unassigned => unreachable!(),
226-
Assigned(v) if v == index => true,
227-
Assigned(_) => unreachable!("assignment does not match variant"),
228-
Ineligible(_) => false,
298+
let fields: IndexVec<_, _> = variant_fields
299+
.iter_enumerated()
300+
.filter_map(|(field, &local)| {
301+
if is_ineligible.contains(field) { None } else { Some(local_layouts[local]) }
229302
})
230-
.map(|local| local_layouts[*local]);
303+
.collect();
231304

232305
let mut variant = calc.univariant(
233-
&variant_only_tys.collect::<IndexVec<_, _>>(),
306+
&fields,
234307
&ReprOptions::default(),
235308
StructKind::Prefixed(prefix_size, prefix_align.abi),
236309
)?;
@@ -254,19 +327,14 @@ pub(super) fn layout<
254327
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
255328

256329
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
257-
let combined_offsets = variant_fields
330+
let combined_offsets = is_ineligible
258331
.iter_enumerated()
259-
.map(|(i, local)| {
260-
let (offset, memory_index) = match assignments[*local] {
261-
Unassigned => unreachable!(),
262-
Assigned(_) => {
263-
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
264-
(offset, promoted_memory_index.len() as u32 + memory_index)
265-
}
266-
Ineligible(field_idx) => {
267-
let field_idx = field_idx.unwrap();
268-
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
269-
}
332+
.map(|(i, &is_ineligible)| {
333+
let (offset, memory_index) = if let Some(field_idx) = is_ineligible {
334+
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
335+
} else {
336+
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
337+
(offset, promoted_memory_index.len() as u32 + memory_index)
270338
};
271339
combined_inverse_memory_index[memory_index] = i;
272340
offset

compiler/rustc_abi/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
6767
pub use extern_abi::CVariadicStatus;
6868
pub use extern_abi::{ExternAbi, all_names};
6969
#[cfg(feature = "nightly")]
70-
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
70+
pub use layout::{
71+
FIRST_VARIANT, FieldIdx, Layout, PackCoroutineLayout, TyAbiInterface, TyAndLayout, VariantIdx,
72+
};
7173
pub use layout::{LayoutCalculator, LayoutCalculatorError};
7274

7375
/// Requirements for a `StableHashingContext` to be used in this crate.

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -421,49 +421,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
421421
Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
422422
));
423423

424-
let captured_place = self.upvars[upvar_index.index()];
425-
426-
err.span_label(span, format!("cannot {act}"));
427-
428-
let upvar_hir_id = captured_place.get_root_variable();
429-
430-
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
431-
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) =
432-
pat.kind
433-
{
434-
if upvar_ident.name == kw::SelfLower {
435-
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
436-
if let Some(fn_decl) = node.fn_decl() {
437-
if !matches!(
438-
fn_decl.implicit_self,
439-
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
440-
) {
441-
err.span_suggestion_verbose(
442-
upvar_ident.span.shrink_to_lo(),
443-
"consider changing this to be mutable",
444-
"mut ",
445-
Applicability::MachineApplicable,
446-
);
447-
break;
448-
}
449-
}
450-
}
451-
} else {
452-
err.span_suggestion_verbose(
453-
upvar_ident.span.shrink_to_lo(),
454-
"consider changing this to be mutable",
455-
"mut ",
456-
Applicability::MachineApplicable,
457-
);
458-
}
459-
}
460-
461-
let tcx = self.infcx.tcx;
462-
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
463-
&& let ty::Closure(id, _) = *ty.kind()
464-
{
465-
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
466-
}
424+
self.suggest_mutable_upvar(*upvar_index, the_place_err, &mut err, span, act);
467425
}
468426

469427
// Complete hack to approximate old AST-borrowck diagnostic: if the span starts
@@ -569,6 +527,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
569527
}
570528
}
571529

530+
fn suggest_mutable_upvar(
531+
&self,
532+
upvar_index: FieldIdx,
533+
the_place_err: PlaceRef<'tcx>,
534+
err: &mut Diag<'infcx>,
535+
span: Span,
536+
act: &str,
537+
) {
538+
let captured_place = self.upvars[upvar_index.index()];
539+
540+
err.span_label(span, format!("cannot {act}"));
541+
542+
let upvar_hir_id = captured_place.get_root_variable();
543+
544+
if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id)
545+
&& let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = pat.kind
546+
{
547+
if upvar_ident.name == kw::SelfLower {
548+
for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
549+
if let Some(fn_decl) = node.fn_decl() {
550+
if !matches!(
551+
fn_decl.implicit_self,
552+
hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
553+
) {
554+
err.span_suggestion_verbose(
555+
upvar_ident.span.shrink_to_lo(),
556+
"consider changing this to be mutable",
557+
"mut ",
558+
Applicability::MachineApplicable,
559+
);
560+
break;
561+
}
562+
}
563+
}
564+
} else {
565+
err.span_suggestion_verbose(
566+
upvar_ident.span.shrink_to_lo(),
567+
"consider changing this to be mutable",
568+
"mut ",
569+
Applicability::MachineApplicable,
570+
);
571+
}
572+
}
573+
574+
let tcx = self.infcx.tcx;
575+
if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
576+
&& let ty::Closure(id, _) = *ty.kind()
577+
{
578+
self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, err);
579+
}
580+
}
581+
572582
/// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
573583
fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
574584
let Some(adt) = ty.ty_adt_def() else { return };

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2495,7 +2495,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
24952495
// If the local may have been initialized, and it is now currently being
24962496
// mutated, then it is justified to be annotated with the `mut`
24972497
// keyword, since the mutation may be a possible reassignment.
2498-
if is_local_mutation_allowed != LocalMutationIsAllowed::Yes
2498+
if !matches!(is_local_mutation_allowed, LocalMutationIsAllowed::Yes)
24992499
&& self.is_local_ever_initialized(local, state).is_some()
25002500
{
25012501
self.used_mut.insert(local);

compiler/rustc_borrowck/src/path_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
33
use rustc_abi::FieldIdx;
44
use rustc_data_structures::graph::dominators::Dominators;
55
use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
6-
use rustc_middle::ty::TyCtxt;
6+
use rustc_middle::ty::{CapturedPlace, TyCtxt};
77
use tracing::debug;
88

99
use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
@@ -133,7 +133,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
133133
/// of a closure type.
134134
pub(crate) fn is_upvar_field_projection<'tcx>(
135135
tcx: TyCtxt<'tcx>,
136-
upvars: &[&rustc_middle::ty::CapturedPlace<'tcx>],
136+
upvars: &[&CapturedPlace<'tcx>],
137137
place_ref: PlaceRef<'tcx>,
138138
body: &Body<'tcx>,
139139
) -> Option<FieldIdx> {

0 commit comments

Comments
 (0)