Skip to content

Commit a997b26

Browse files
abi: compute coroutine layout to reuse slots of promoted captures
In the event that upvar captures are promoted, most certainly because a coroutine suspends at least once, the slots in the promotion prefix shall be reused. This means that the copies emitted in the upvar relocation MIR pass will eventually elided and eliminated in the codegen phase, hence no additional runtime cost is realised. Co-authored-by: Dario Nieuwenhuis <[email protected]>
1 parent 14850d6 commit a997b26

File tree

2 files changed

+53
-34
lines changed

2 files changed

+53
-34
lines changed

compiler/rustc_abi/src/layout.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
215215
>(
216216
&self,
217217
local_layouts: &IndexSlice<LocalIdx, F>,
218-
prefix_layouts: IndexVec<FieldIdx, F>,
218+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
219219
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
220220
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
221221
tag_to_layout: impl Fn(Scalar) -> F,
222222
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
223223
coroutine::layout(
224224
self,
225225
local_layouts,
226-
prefix_layouts,
226+
relocated_upvars,
227227
variant_fields,
228228
storage_conflicts,
229229
tag_to_layout,

compiler/rustc_abi/src/layout/coroutine.rs

+51-32
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ pub(super) fn layout<
145145
>(
146146
calc: &super::LayoutCalculator<impl HasDataLayout>,
147147
local_layouts: &IndexSlice<LocalIdx, F>,
148-
mut prefix_layouts: IndexVec<FieldIdx, F>,
148+
relocated_upvars: &IndexSlice<LocalIdx, Option<LocalIdx>>,
149149
variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
150150
storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
151151
tag_to_layout: impl Fn(Scalar) -> F,
@@ -155,10 +155,8 @@ pub(super) fn layout<
155155
let (ineligible_locals, assignments) =
156156
coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
157157

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.len();
158+
// Build a prefix layout, consisting of only the state tag.
159+
let tag_index = 0;
162160

163161
// `variant_fields` already accounts for the reserved variants, so no need to add them.
164162
let max_discr = (variant_fields.len() - 1) as u128;
@@ -169,17 +167,17 @@ pub(super) fn layout<
169167
};
170168

171169
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);
170+
let prefix_layouts: IndexVec<_, _> =
171+
[tag_to_layout(tag)].into_iter().chain(promoted_layouts).collect();
174172
let prefix =
175173
calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
176174

177175
let (prefix_size, prefix_align) = (prefix.size, prefix.align);
178176

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.
177+
// Split the prefix layout into the discriminant and
178+
// the "promoted" fields.
179+
// Promoted fields will get included in each variant
180+
// that requested them in CoroutineLayout.
183181
debug!("prefix = {:#?}", prefix);
184182
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
185183
FieldsShape::Arbitrary { mut offsets, memory_index } => {
@@ -218,19 +216,45 @@ pub(super) fn layout<
218216
let variants = variant_fields
219217
.iter_enumerated()
220218
.map(|(index, variant_fields)| {
219+
let is_unresumed = index == VariantIdx::new(0);
220+
let mut is_ineligible = IndexVec::from_elem_n(None, variant_fields.len());
221+
for (field, &local) in variant_fields.iter_enumerated() {
222+
if is_unresumed {
223+
// NOTE(@dingxiangfei2009): rewrite this when let-chain #53667
224+
// is stabilized
225+
if let Some(inner_local) = relocated_upvars[local] {
226+
if let Ineligible(Some(promoted_field)) = assignments[inner_local] {
227+
is_ineligible.insert(field, promoted_field);
228+
continue;
229+
}
230+
}
231+
}
232+
match assignments[local] {
233+
Assigned(v) if v == index => {}
234+
Ineligible(Some(promoted_field)) => {
235+
is_ineligible.insert(field, promoted_field);
236+
}
237+
Ineligible(None) => {
238+
panic!("an ineligible local should have been promoted into the prefix")
239+
}
240+
Assigned(_) => {
241+
panic!("an eligible local should have been assigned to exactly one variant")
242+
}
243+
Unassigned => {
244+
panic!("each saved local should have been inspected at least once")
245+
}
246+
}
247+
}
221248
// 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,
249+
let fields: IndexVec<_, _> = variant_fields
250+
.iter_enumerated()
251+
.filter_map(|(field, &local)| {
252+
if is_ineligible.contains(field) { None } else { Some(local_layouts[local]) }
229253
})
230-
.map(|local| local_layouts[*local]);
254+
.collect();
231255

232256
let mut variant = calc.univariant(
233-
&variant_only_tys.collect::<IndexVec<_, _>>(),
257+
&fields,
234258
&ReprOptions::default(),
235259
StructKind::Prefixed(prefix_size, prefix_align.abi),
236260
)?;
@@ -254,19 +278,14 @@ pub(super) fn layout<
254278
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
255279

256280
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
257-
let combined_offsets = variant_fields
281+
let combined_offsets = is_ineligible
258282
.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-
}
283+
.map(|(i, &is_ineligible)| {
284+
let (offset, memory_index) = if let Some(field_idx) = is_ineligible {
285+
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
286+
} else {
287+
let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
288+
(offset, promoted_memory_index.len() as u32 + memory_index)
270289
};
271290
combined_inverse_memory_index[memory_index] = i;
272291
offset

0 commit comments

Comments
 (0)