@@ -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 } , 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
+
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
@@ -809,6 +857,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
809
857
} ) ;
810
858
trace ! ( ?largest_niche) ;
811
859
860
+ let single_variant_layout_eligible =
861
+ !repr. inhibit_enum_layout_opt ( ) && valid_discriminants. len ( ) == 1 ;
862
+
812
863
// `max` is the last valid discriminant before the largest niche
813
864
// `min` is the first valid discriminant after the largest niche
814
865
let ( max, min) = largest_niche
@@ -841,10 +892,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
841
892
}
842
893
843
894
// Create the set of structs that represent each variant.
895
+ let mut single_inhabited_variant_no_tag_layout = None ;
844
896
let mut layout_variants = variants
845
897
. iter_enumerated ( )
846
898
. map ( |( i, field_layouts) | {
847
899
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
+ }
848
904
// We don't need to encode the tag in uninhabited variants in repr(Rust) enums
849
905
let struct_kind = if uninhabited && !repr. inhibit_enum_layout_opt ( ) {
850
906
StructKind :: AlwaysSized
@@ -871,6 +927,72 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
871
927
} )
872
928
. collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
873
929
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
+
874
996
// Align the maximum variant size to the largest alignment.
875
997
size = size. align_to ( align. abi ) ;
876
998
@@ -1151,22 +1273,36 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
1151
1273
randomization_seed : combined_seed,
1152
1274
} ;
1153
1275
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 > | {
1159
1283
use cmp:: Ordering :: * ;
1160
1284
let niche_size = |l : & LayoutData < FieldIdx , VariantIdx > | {
1161
1285
l. largest_niche . map_or ( 0 , |n| n. available ( dl) )
1162
1286
} ;
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 ,
1167
1291
}
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) ,
1170
1306
} ;
1171
1307
1172
1308
Ok ( best_layout)
0 commit comments