@@ -197,6 +197,54 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
197197 } )
198198 }
199199
200+ pub fn simd_type_for_scalar < FieldIdx : Idx , VariantIdx : Idx , F > (
201+ & self ,
202+ element : Scalar ,
203+ count : u64 ,
204+ repr_packed : bool ,
205+ ) -> LayoutCalculatorResult < FieldIdx , VariantIdx , F > {
206+ let elt = element;
207+ if count == 0 {
208+ return Err ( LayoutCalculatorError :: ZeroLengthSimdType ) ;
209+ } else if count > crate :: MAX_SIMD_LANES {
210+ return Err ( LayoutCalculatorError :: OversizedSimdType {
211+ max_lanes : crate :: MAX_SIMD_LANES ,
212+ } ) ;
213+ }
214+
215+ // Compute the size and alignment of the vector
216+ let dl = self . cx . data_layout ( ) ;
217+ let size = elt
218+ . size ( & self . cx )
219+ . checked_mul ( count, dl)
220+ . ok_or_else ( || LayoutCalculatorError :: SizeOverflow ) ?;
221+ let ( repr, align) = if repr_packed && !count. is_power_of_two ( ) {
222+ // Non-power-of-two vectors have padding up to the next power-of-two.
223+ // If we're a packed repr, remove the padding while keeping the alignment as close
224+ // to a vector as possible.
225+ ( BackendRepr :: Memory { sized : true } , AbiAlign { abi : Align :: max_aligned_factor ( size) } )
226+ } else {
227+ ( BackendRepr :: SimdVector { element, count } , dl. llvmlike_vector_align ( size) )
228+ } ;
229+ let size = size. align_to ( align. abi ) ;
230+
231+ Ok ( LayoutData {
232+ variants : Variants :: Single { index : VariantIdx :: new ( 0 ) , variants : None } ,
233+ fields : FieldsShape :: Arbitrary {
234+ offsets : [ Size :: ZERO ] . into ( ) ,
235+ memory_index : [ 0 ] . into ( ) ,
236+ } ,
237+ backend_repr : repr,
238+ largest_niche : None ,
239+ uninhabited : false ,
240+ size,
241+ align,
242+ max_repr_align : None ,
243+ unadjusted_abi_align : elt. align ( & self . cx ) . abi ,
244+ randomization_seed : ( Hash64 :: new ( count) ) ,
245+ } )
246+ }
247+
200248 /// Compute the layout for a coroutine.
201249 ///
202250 /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine
@@ -809,6 +857,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
809857 } ) ;
810858 trace ! ( ?largest_niche) ;
811859
860+ let single_variant_layout_eligible =
861+ !repr. inhibit_enum_layout_opt ( ) && valid_discriminants. len ( ) == 1 ;
862+
812863 // `max` is the last valid discriminant before the largest niche
813864 // `min` is the first valid discriminant after the largest niche
814865 let ( max, min) = largest_niche
@@ -841,10 +892,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
841892 }
842893
843894 // Create the set of structs that represent each variant.
895+ let mut single_inhabited_variant_no_tag_layout = None ;
844896 let mut layout_variants = variants
845897 . iter_enumerated ( )
846898 . map ( |( i, field_layouts) | {
847899 let uninhabited = field_layouts. iter ( ) . any ( |f| f. is_uninhabited ( ) ) ;
900+ if !uninhabited && single_variant_layout_eligible {
901+ single_inhabited_variant_no_tag_layout =
902+ Some ( ( i, self . univariant ( field_layouts, repr, StructKind :: AlwaysSized ) ) ) ;
903+ }
848904 // We don't need to encode the tag in uninhabited variants in repr(Rust) enums
849905 let struct_kind = if uninhabited && !repr. inhibit_enum_layout_opt ( ) {
850906 StructKind :: AlwaysSized
@@ -871,6 +927,72 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
871927 } )
872928 . collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
873929
930+ // If there is a single uninhabited variant, we can use it mostly unchanged as the layout,
931+ // without using a tag or niche.
932+ //
933+ // We do still need to modify it to make all the uninhabited variants fit so they
934+ // can be partially-initialized.
935+ //
936+ // FIXME: We shouldn't assume this is better than the tagged layout; it's worse for
937+ // `enum Foo { A, B(i32, !) }` because it has no niche.
938+ let no_tag_layout = if single_variant_layout_eligible
939+ && let Some ( ( single_inhabited_variant_idx, Ok ( mut st) ) ) =
940+ single_inhabited_variant_no_tag_layout
941+ {
942+ // Keep track of original variant layouts (including the inhabited one)
943+ // for `offset_of!`.
944+ let mut variants = layout_variants. clone ( ) ;
945+ variants[ single_inhabited_variant_idx] = st. clone ( ) ;
946+
947+ // We know that every other variant is uninhabited, and thus does not have a
948+ // prefix for the tag, so we can use them to find the necessary size.
949+ for ( idx, layout) in layout_variants. iter_enumerated ( ) {
950+ if idx != single_inhabited_variant_idx {
951+ st. size = cmp:: max ( st. size , layout. size ) ;
952+ st. align = st. align . max ( layout. align ) ;
953+ st. max_repr_align = st. max_repr_align . max ( layout. max_repr_align ) ;
954+ st. unadjusted_abi_align =
955+ st. unadjusted_abi_align . max ( layout. unadjusted_abi_align ) ;
956+ }
957+ }
958+
959+ // Align the maximum variant size to the largest alignment.
960+ st. size = st. size . align_to ( st. align . abi ) ;
961+
962+ // If the inhabited variant's layout would use a non-Memory BackendRepr,
963+ // but we made it bigger or more-aligned due to uninhabited variants,
964+ // force it to be BackendRepr::Memory
965+ match st. backend_repr {
966+ BackendRepr :: Scalar ( ..) | BackendRepr :: ScalarPair ( ..) => {
967+ if st. backend_repr . scalar_size ( & self . cx ) != Some ( st. size )
968+ || st. backend_repr . scalar_align ( & self . cx ) != Some ( st. align . abi )
969+ {
970+ st. backend_repr = BackendRepr :: Memory { sized : true }
971+ }
972+ }
973+ BackendRepr :: SimdVector { element, count } => {
974+ // FIXME: is there a better way to do this than making a copy of
975+ // `LayoutCalculator::simd_type` *just* for this?
976+ let vector_layout = self . simd_type_for_scalar :: < FieldIdx , VariantIdx , _ > (
977+ element,
978+ count,
979+ repr. packed ( ) ,
980+ ) ?;
981+ if vector_layout. size != st. size || vector_layout. align != st. align {
982+ st. backend_repr = BackendRepr :: Memory { sized : true }
983+ }
984+ }
985+ BackendRepr :: Memory { .. } => { }
986+ }
987+
988+ st. variants =
989+ Variants :: Single { index : single_inhabited_variant_idx, variants : Some ( variants) } ;
990+
991+ Some ( st)
992+ } else {
993+ None
994+ } ;
995+
874996 // Align the maximum variant size to the largest alignment.
875997 size = size. align_to ( align. abi ) ;
876998
@@ -1151,22 +1273,36 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
11511273 randomization_seed : combined_seed,
11521274 } ;
11531275
1154- let best_layout = match ( tagged_layout, niche_filling_layout) {
1155- ( tl, Some ( nl) ) => {
1156- // Pick the smaller layout; otherwise,
1157- // pick the layout with the larger niche; otherwise,
1158- // pick tagged as it has simpler codegen.
1276+ // Pick the smallest layout; otherwise,
1277+ // pick the layout with the largest niche; otherwise,
1278+ // pick no_tag as it has simpler codegen than tagged and niched; otherwise,
1279+ // pick tagged as it has simpler codegen than niched.
1280+
1281+ let better_layout_or_first =
1282+ |l1 : LayoutData < FieldIdx , VariantIdx > , l2 : LayoutData < FieldIdx , VariantIdx > | {
11591283 use cmp:: Ordering :: * ;
11601284 let niche_size = |l : & LayoutData < FieldIdx , VariantIdx > | {
11611285 l. largest_niche . map_or ( 0 , |n| n. available ( dl) )
11621286 } ;
1163- match ( tl . size . cmp ( & nl . size ) , niche_size ( & tl ) . cmp ( & niche_size ( & nl ) ) ) {
1164- ( Greater , _) => nl ,
1165- ( Equal , Less ) => nl ,
1166- _ => tl ,
1287+ match ( l1 . size . cmp ( & l2 . size ) , niche_size ( & l1 ) . cmp ( & niche_size ( & l2 ) ) ) {
1288+ ( Greater , _) => l2 ,
1289+ ( Equal , Less ) => l2 ,
1290+ _ => l1 ,
11671291 }
1168- }
1169- ( tl, None ) => tl,
1292+ } ;
1293+
1294+ let best_layout = match niche_filling_layout {
1295+ None => tagged_layout,
1296+ // Prefer tagged over niched if they have the same size and niche size,
1297+ // as the tagged layout has simpler codegen.
1298+ Some ( niched_layout) => better_layout_or_first ( tagged_layout, niched_layout) ,
1299+ } ;
1300+
1301+ let best_layout = match no_tag_layout {
1302+ None => best_layout,
1303+ // Prefer no-tag over tagged/niched if they have the same size and niche size,
1304+ // as the no-tag layout has simpler codegen.
1305+ Some ( no_tag_layout) => better_layout_or_first ( no_tag_layout, best_layout) ,
11701306 } ;
11711307
11721308 Ok ( best_layout)
0 commit comments