@@ -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 ) ]
3546enum SavedLocalEligibility < VariantIdx , FieldIdx > {
@@ -74,6 +85,7 @@ fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: I
7485 }
7586 }
7687 }
88+ debug ! ( ?ineligible_locals, "after counting variants containing a saved local" ) ;
7789
7890 // Next, check every pair of eligible locals to see if they
7991 // conflict.
@@ -103,6 +115,7 @@ fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: I
103115 trace ! ( "removing local {:?} due to conflict with {:?}" , remove, other) ;
104116 }
105117 }
118+ debug ! ( ?ineligible_locals, "after checking conflicts" ) ;
106119
107120 // Count the number of variants in use. If only one of them, then it is
108121 // impossible to overlap any locals in our layout. In this case it's
@@ -122,6 +135,7 @@ fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: I
122135 }
123136 ineligible_locals. insert_all ( ) ;
124137 }
138+ debug ! ( ?ineligible_locals, "after checking used variants" ) ;
125139 }
126140
127141 // Write down the order of our locals that will be promoted to the prefix.
@@ -145,20 +159,24 @@ pub(super) fn layout<
145159> (
146160 calc : & super :: LayoutCalculator < impl HasDataLayout > ,
147161 local_layouts : & IndexSlice < LocalIdx , F > ,
148- mut prefix_layouts : IndexVec < FieldIdx , F > ,
162+ relocated_upvars : & IndexSlice < LocalIdx , Option < LocalIdx > > ,
163+ upvar_layouts : IndexVec < FieldIdx , F > ,
149164 variant_fields : & IndexSlice < VariantIdx , IndexVec < FieldIdx , LocalIdx > > ,
150165 storage_conflicts : & BitMatrix < LocalIdx , LocalIdx > ,
166+ pack : PackCoroutineLayout ,
151167 tag_to_layout : impl Fn ( Scalar ) -> F ,
152168) -> super :: LayoutCalculatorResult < FieldIdx , VariantIdx , F > {
153169 use SavedLocalEligibility :: * ;
154170
155171 let ( ineligible_locals, assignments) =
156172 coroutine_saved_local_eligibility ( local_layouts. len ( ) , variant_fields, storage_conflicts) ;
173+ debug ! ( ?ineligible_locals) ;
157174
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 ( ) ;
175+ // Build a prefix layout, consisting of only the state tag and, as per request, upvars
176+ let tag_index = match pack {
177+ PackCoroutineLayout :: CapturesOnly => FieldIdx :: new ( 0 ) ,
178+ PackCoroutineLayout :: Classic => upvar_layouts. next_index ( ) ,
179+ } ;
162180
163181 // `variant_fields` already accounts for the reserved variants, so no need to add them.
164182 let max_discr = ( variant_fields. len ( ) - 1 ) as u128 ;
@@ -168,18 +186,38 @@ pub(super) fn layout<
168186 valid_range : WrappingRange { start : 0 , end : max_discr } ,
169187 } ;
170188
171- 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) ;
189+ let upvars_in_unresumed: rustc_hash:: FxHashSet < _ > =
190+ variant_fields[ VariantIdx :: new ( 0 ) ] . iter ( ) . copied ( ) . collect ( ) ;
191+ let promoted_layouts = ineligible_locals. iter ( ) . filter_map ( |local| {
192+ if matches ! ( pack, PackCoroutineLayout :: Classic ) && upvars_in_unresumed. contains ( & local) {
193+ // We do not need to promote upvars, they are already in the upvar region
194+ None
195+ } else {
196+ Some ( local_layouts[ local] )
197+ }
198+ } ) ;
199+ // FIXME: when we introduce more pack scheme, we need to change the prefix layout here
200+ let prefix_layouts: IndexVec < _ , _ > = match pack {
201+ PackCoroutineLayout :: Classic => {
202+ // Classic scheme packs the states as follows
203+ // [ <upvars>.. , <state tag>, <promoted ineligibles>] ++ <variant data>
204+ // In addition, UNRESUME overlaps with the <upvars> part
205+ upvar_layouts. into_iter ( ) . chain ( [ tag_to_layout ( tag) ] ) . chain ( promoted_layouts) . collect ( )
206+ }
207+ PackCoroutineLayout :: CapturesOnly => {
208+ [ tag_to_layout ( tag) ] . into_iter ( ) . chain ( promoted_layouts) . collect ( )
209+ }
210+ } ;
211+ debug ! ( ?prefix_layouts, ?pack) ;
174212 let prefix =
175213 calc. univariant ( & prefix_layouts, & ReprOptions :: default ( ) , StructKind :: AlwaysSized ) ?;
176214
177215 let ( prefix_size, prefix_align) = ( prefix. size , prefix. align ) ;
178216
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.
217+ // Split the prefix layout into the discriminant and
218+ // the "promoted" fields.
219+ // Promoted fields will get included in each variant
220+ // that requested them in CoroutineLayout.
183221 debug ! ( "prefix = {:#?}" , prefix) ;
184222 let ( outer_fields, promoted_offsets, promoted_memory_index) = match prefix. fields {
185223 FieldsShape :: Arbitrary { mut offsets, memory_index } => {
@@ -213,24 +251,75 @@ pub(super) fn layout<
213251 _ => unreachable ! ( ) ,
214252 } ;
215253
254+ // Here we start to compute layout of each state variant
216255 let mut size = prefix. size ;
217256 let mut align = prefix. align ;
218257 let variants = variant_fields
219258 . iter_enumerated ( )
220259 . map ( |( index, variant_fields) | {
260+ // Special case: UNRESUMED overlaps with the upvar region of the prefix,
261+ // so that moving upvars may eventually become a no-op.
262+ let is_unresumed = index. index ( ) == 0 ;
263+ if is_unresumed && matches ! ( pack, PackCoroutineLayout :: Classic ) {
264+ let fields = FieldsShape :: Arbitrary {
265+ offsets : ( 0 ..tag_index. index ( ) ) . map ( |i| outer_fields. offset ( i) ) . collect ( ) ,
266+ memory_index : ( 0 ..tag_index. index ( ) )
267+ . map ( |i| {
268+ ( outer_fields. memory_index ( i) + promoted_memory_index. len ( ) ) as u32
269+ } )
270+ . collect ( ) ,
271+ } ;
272+ let align = prefix. align ;
273+ let size = prefix. size ;
274+ return Ok ( LayoutData {
275+ fields,
276+ variants : Variants :: Single { index } ,
277+ backend_repr : BackendRepr :: Memory { sized : true } ,
278+ largest_niche : None ,
279+ uninhabited : false ,
280+ align,
281+ size,
282+ max_repr_align : None ,
283+ unadjusted_abi_align : align. abi ,
284+ randomization_seed : Default :: default ( ) ,
285+ } ) ;
286+ }
287+ let mut is_ineligible = IndexVec :: from_elem_n ( None , variant_fields. len ( ) ) ;
288+ for ( field, & local) in variant_fields. iter_enumerated ( ) {
289+ if is_unresumed {
290+ if let Some ( inner_local) = relocated_upvars[ local]
291+ && let Ineligible ( Some ( promoted_field) ) = assignments[ inner_local]
292+ {
293+ is_ineligible. insert ( field, promoted_field) ;
294+ continue ;
295+ }
296+ }
297+ match assignments[ local] {
298+ Assigned ( v) if v == index => { }
299+ Ineligible ( Some ( promoted_field) ) => {
300+ is_ineligible. insert ( field, promoted_field) ;
301+ }
302+ Ineligible ( None ) => {
303+ panic ! ( "an ineligible local should have been promoted into the prefix" )
304+ }
305+ Assigned ( _) => {
306+ panic ! ( "an eligible local should have been assigned to exactly one variant" )
307+ }
308+ Unassigned => {
309+ panic ! ( "each saved local should have been inspected at least once" )
310+ }
311+ }
312+ }
221313 // 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 ,
314+ let fields: IndexVec < _ , _ > = variant_fields
315+ . iter_enumerated ( )
316+ . filter_map ( |( field, & local) | {
317+ if is_ineligible. contains ( field) { None } else { Some ( local_layouts[ local] ) }
229318 } )
230- . map ( |local| local_layouts [ * local ] ) ;
319+ . collect ( ) ;
231320
232321 let mut variant = calc. univariant (
233- & variant_only_tys . collect :: < IndexVec < _ , _ > > ( ) ,
322+ & fields ,
234323 & ReprOptions :: default ( ) ,
235324 StructKind :: Prefixed ( prefix_size, prefix_align. abi ) ,
236325 ) ?;
@@ -254,19 +343,14 @@ pub(super) fn layout<
254343 IndexVec :: from_elem_n ( FieldIdx :: new ( invalid_field_idx) , invalid_field_idx) ;
255344
256345 let mut offsets_and_memory_index = iter:: zip ( offsets, memory_index) ;
257- let combined_offsets = variant_fields
346+ let combined_offsets = is_ineligible
258347 . 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- }
348+ . map ( |( i, & is_ineligible) | {
349+ let ( offset, memory_index) = if let Some ( field_idx) = is_ineligible {
350+ ( promoted_offsets[ field_idx] , promoted_memory_index[ field_idx] )
351+ } else {
352+ let ( offset, memory_index) = offsets_and_memory_index. next ( ) . unwrap ( ) ;
353+ ( offset, promoted_memory_index. len ( ) as u32 + memory_index)
270354 } ;
271355 combined_inverse_memory_index[ memory_index] = i;
272356 offset
0 commit comments