@@ -197,6 +197,54 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
197
197
} )
198
198
}
199
199
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 } , 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) ;
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 : AbiAlign :: new ( align) ,
242
+ max_repr_align : None ,
243
+ unadjusted_abi_align : elt. align ( & self . cx ) . abi ,
244
+ randomization_seed : ( Hash64 :: new ( count) ) ,
245
+ } )
246
+ }
247
+
200
248
/// Compute the layout for a coroutine.
201
249
///
202
250
/// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine
@@ -807,6 +855,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
807
855
} ) ;
808
856
trace ! ( ?largest_niche) ;
809
857
858
+ let single_variant_layout_eligible =
859
+ !repr. inhibit_enum_layout_opt ( ) && valid_discriminants. len ( ) == 1 ;
860
+
810
861
// `max` is the last valid discriminant before the largest niche
811
862
// `min` is the first valid discriminant after the largest niche
812
863
let ( max, min) = largest_niche
@@ -839,10 +890,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
839
890
}
840
891
841
892
// Create the set of structs that represent each variant.
893
+ let mut single_inhabited_variant_no_tag_layout = None ;
842
894
let mut layout_variants = variants
843
895
. iter_enumerated ( )
844
896
. map ( |( i, field_layouts) | {
845
897
let uninhabited = field_layouts. iter ( ) . any ( |f| f. is_uninhabited ( ) ) ;
898
+ if !uninhabited && single_variant_layout_eligible {
899
+ single_inhabited_variant_no_tag_layout =
900
+ Some ( ( i, self . univariant ( field_layouts, repr, StructKind :: AlwaysSized ) ) ) ;
901
+ }
846
902
// We don't need to encode the tag in uninhabited variants in repr(Rust) enums
847
903
let struct_kind = if uninhabited && !repr. inhibit_enum_layout_opt ( ) {
848
904
StructKind :: AlwaysSized
@@ -869,6 +925,73 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
869
925
} )
870
926
. collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
871
927
928
+ // If there is a single uninhabited variant, we can use it mostly unchanged as the layout,
929
+ // without using a tag or niche.
930
+ //
931
+ // We do still need to modify it to make all the uninhabited variants fit so they
932
+ // can be partially-initialized.
933
+ //
934
+ // We keep this as a prospective layout, and don't assume it's better than the tagged
935
+ // layout and return it immediately; e.g. it's worse for `enum Foo { A, B(i32, !) }`
936
+ // because it has no niche.
937
+ let no_tag_layout = if single_variant_layout_eligible
938
+ && let Some ( ( single_inhabited_variant_idx, Ok ( mut st) ) ) =
939
+ single_inhabited_variant_no_tag_layout
940
+ {
941
+ // Keep track of original variant layouts (including the inhabited one)
942
+ // for `offset_of!`.
943
+ let mut variants = layout_variants. clone ( ) ;
944
+ variants[ single_inhabited_variant_idx] = st. clone ( ) ;
945
+
946
+ // We know that every other variant is uninhabited, and thus does not have a
947
+ // prefix for the tag, so we can use them to find the necessary size.
948
+ for ( idx, layout) in layout_variants. iter_enumerated ( ) {
949
+ if idx != single_inhabited_variant_idx {
950
+ st. size = cmp:: max ( st. size , layout. size ) ;
951
+ st. align = st. align . max ( layout. align ) ;
952
+ st. max_repr_align = st. max_repr_align . max ( layout. max_repr_align ) ;
953
+ st. unadjusted_abi_align =
954
+ st. unadjusted_abi_align . max ( layout. unadjusted_abi_align ) ;
955
+ }
956
+ }
957
+
958
+ // Align the maximum variant size to the largest alignment.
959
+ st. size = st. size . align_to ( st. align . abi ) ;
960
+
961
+ // If the inhabited variant's layout would use a non-Memory BackendRepr,
962
+ // but we made it bigger or more-aligned due to uninhabited variants,
963
+ // force it to be BackendRepr::Memory
964
+ match st. backend_repr {
965
+ BackendRepr :: Scalar ( ..) | BackendRepr :: ScalarPair ( ..) => {
966
+ if st. backend_repr . scalar_size ( & self . cx ) != Some ( st. size )
967
+ || st. backend_repr . scalar_align ( & self . cx ) != Some ( st. align . abi )
968
+ {
969
+ st. backend_repr = BackendRepr :: Memory { sized : true }
970
+ }
971
+ }
972
+ BackendRepr :: SimdVector { element, count } => {
973
+ // FIXME: is there a better way to do this than making a copy of
974
+ // `LayoutCalculator::simd_type` *just* for this?
975
+ let vector_layout = self . simd_type_for_scalar :: < FieldIdx , VariantIdx , _ > (
976
+ element,
977
+ count,
978
+ repr. packed ( ) ,
979
+ ) ?;
980
+ if vector_layout. size != st. size || vector_layout. align != st. align {
981
+ st. backend_repr = BackendRepr :: Memory { sized : true }
982
+ }
983
+ }
984
+ BackendRepr :: Memory { .. } => { }
985
+ }
986
+
987
+ st. variants =
988
+ Variants :: Single { index : single_inhabited_variant_idx, variants : Some ( variants) } ;
989
+
990
+ Some ( st)
991
+ } else {
992
+ None
993
+ } ;
994
+
872
995
// Align the maximum variant size to the largest alignment.
873
996
size = size. align_to ( align) ;
874
997
@@ -1149,22 +1272,36 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
1149
1272
randomization_seed : combined_seed,
1150
1273
} ;
1151
1274
1152
- let best_layout = match ( tagged_layout, niche_filling_layout) {
1153
- ( tl, Some ( nl) ) => {
1154
- // Pick the smaller layout; otherwise,
1155
- // pick the layout with the larger niche; otherwise,
1156
- // pick tagged as it has simpler codegen.
1275
+ // Pick the smallest layout; otherwise,
1276
+ // pick the layout with the largest niche; otherwise,
1277
+ // pick no_tag as it has simpler codegen than tagged and niched; otherwise,
1278
+ // pick tagged as it has simpler codegen than niched.
1279
+
1280
+ let better_layout_or_first =
1281
+ |l1 : LayoutData < FieldIdx , VariantIdx > , l2 : LayoutData < FieldIdx , VariantIdx > | {
1157
1282
use cmp:: Ordering :: * ;
1158
1283
let niche_size = |l : & LayoutData < FieldIdx , VariantIdx > | {
1159
1284
l. largest_niche . map_or ( 0 , |n| n. available ( dl) )
1160
1285
} ;
1161
- match ( tl . size . cmp ( & nl . size ) , niche_size ( & tl ) . cmp ( & niche_size ( & nl ) ) ) {
1162
- ( Greater , _) => nl ,
1163
- ( Equal , Less ) => nl ,
1164
- _ => tl ,
1286
+ match ( l1 . size . cmp ( & l2 . size ) , niche_size ( & l1 ) . cmp ( & niche_size ( & l2 ) ) ) {
1287
+ ( Greater , _) => l2 ,
1288
+ ( Equal , Less ) => l2 ,
1289
+ _ => l1 ,
1165
1290
}
1166
- }
1167
- ( tl, None ) => tl,
1291
+ } ;
1292
+
1293
+ let best_layout = match niche_filling_layout {
1294
+ None => tagged_layout,
1295
+ // Prefer tagged over niched if they have the same size and niche size,
1296
+ // as the tagged layout has simpler codegen.
1297
+ Some ( niched_layout) => better_layout_or_first ( tagged_layout, niched_layout) ,
1298
+ } ;
1299
+
1300
+ let best_layout = match no_tag_layout {
1301
+ None => best_layout,
1302
+ // Prefer no-tag over tagged/niched if they have the same size and niche size,
1303
+ // as the no-tag layout has simpler codegen.
1304
+ Some ( no_tag_layout) => better_layout_or_first ( no_tag_layout, best_layout) ,
1168
1305
} ;
1169
1306
1170
1307
Ok ( best_layout)
0 commit comments