@@ -1418,32 +1418,34 @@ impl<'tcx> GotocCtx<'tcx> {
1418
1418
subst : & ' tcx InternalSubsts < ' tcx > ,
1419
1419
) -> Type {
1420
1420
let pretty_name = self . ty_pretty_name ( ty) ;
1421
- self . ensure_struct ( self . ty_mangled_name ( ty ) , pretty_name , |ctx , name| {
1422
- // variants appearing in source code (in source code order)
1423
- let source_variants = & adtdef . variants ( ) ;
1424
- let layout = ctx . layout_of ( ty ) ;
1425
- // variants appearing in mir code
1426
- match & layout . variants {
1427
- Variants :: Single { index } => {
1421
+ // variants appearing in source code (in source code order)
1422
+ let source_variants = & adtdef . variants ( ) ;
1423
+ let layout = self . layout_of ( ty ) ;
1424
+ // variants appearing in mir code
1425
+ match & layout . variants {
1426
+ Variants :: Single { index } => {
1427
+ self . ensure_struct ( self . ty_mangled_name ( ty ) , pretty_name , |gcx , _| {
1428
1428
match source_variants. get ( * index) {
1429
1429
None => {
1430
1430
// an empty enum with no variants (its value cannot be instantiated)
1431
1431
vec ! [ ]
1432
1432
}
1433
1433
Some ( variant) => {
1434
1434
// a single enum is pretty much like a struct
1435
- let layout = ctx . layout_of ( ty) . layout ;
1436
- ctx . codegen_variant_struct_fields ( variant, subst, & layout, Size :: ZERO )
1435
+ let layout = gcx . layout_of ( ty) . layout ;
1436
+ gcx . codegen_variant_struct_fields ( variant, subst, & layout, Size :: ZERO )
1437
1437
}
1438
1438
}
1439
- }
1440
- Variants :: Multiple { tag_encoding, variants, tag_field, .. } => {
1441
- // Contrary to generators, currently enums have only one field (the discriminant), the rest are in the variants:
1442
- assert ! ( layout. fields. count( ) <= 1 ) ;
1443
- // Contrary to generators, the discriminant is the first (and only) field for enums:
1444
- assert_eq ! ( * tag_field, 0 ) ;
1445
- match tag_encoding {
1446
- TagEncoding :: Direct => {
1439
+ } )
1440
+ }
1441
+ Variants :: Multiple { tag_encoding, variants, tag_field, .. } => {
1442
+ // Contrary to generators, currently enums have only one field (the discriminant), the rest are in the variants:
1443
+ assert ! ( layout. fields. count( ) <= 1 ) ;
1444
+ // Contrary to generators, the discriminant is the first (and only) field for enums:
1445
+ assert_eq ! ( * tag_field, 0 ) ;
1446
+ match tag_encoding {
1447
+ TagEncoding :: Direct => {
1448
+ self . ensure_struct ( self . ty_mangled_name ( ty) , pretty_name, |gcx, name| {
1447
1449
// For direct encoding of tags, we generate a type with two fields:
1448
1450
// ```
1449
1451
// struct tag-<> { // enum type
@@ -1455,22 +1457,22 @@ impl<'tcx> GotocCtx<'tcx> {
1455
1457
// (`#[repr]`) and it represents which variant is being used.
1456
1458
// The `cases` field is a union of all variant types where the name
1457
1459
// of each union field is the name of the corresponding discriminant.
1458
- let discr_t = ctx . codegen_enum_discr_typ ( ty) ;
1459
- let int = ctx . codegen_ty ( discr_t) ;
1460
- let discr_offset = ctx . layout_of ( discr_t) . size ;
1460
+ let discr_t = gcx . codegen_enum_discr_typ ( ty) ;
1461
+ let int = gcx . codegen_ty ( discr_t) ;
1462
+ let discr_offset = gcx . layout_of ( discr_t) . size ;
1461
1463
let initial_offset =
1462
- ctx . variant_min_offset ( variants) . unwrap_or ( discr_offset) ;
1464
+ gcx . variant_min_offset ( variants) . unwrap_or ( discr_offset) ;
1463
1465
let mut fields = vec ! [ DatatypeComponent :: field( "case" , int) ] ;
1464
1466
if let Some ( padding) =
1465
- ctx . codegen_struct_padding ( discr_offset, initial_offset, 0 )
1467
+ gcx . codegen_struct_padding ( discr_offset, initial_offset, 0 )
1466
1468
{
1467
1469
fields. push ( padding) ;
1468
1470
}
1469
1471
let union_name = format ! ( "{}-union" , name) ;
1470
1472
let union_pretty_name = format ! ( "{}-union" , pretty_name) ;
1471
1473
fields. push ( DatatypeComponent :: field (
1472
1474
"cases" ,
1473
- ctx . ensure_union ( & union_name, & union_pretty_name, |ctx, name| {
1475
+ gcx . ensure_union ( & union_name, & union_pretty_name, |ctx, name| {
1474
1476
ctx. codegen_enum_cases (
1475
1477
name,
1476
1478
pretty_name,
@@ -1482,45 +1484,56 @@ impl<'tcx> GotocCtx<'tcx> {
1482
1484
} ) ,
1483
1485
) ) ;
1484
1486
fields
1485
- }
1486
- TagEncoding :: Niche { dataful_variant, .. } => {
1487
- // Enumerations with multiple variants and niche encoding have a
1488
- // specific format that can be used to optimize its layout and reduce
1489
- // memory consumption.
1490
- //
1491
- // These enumerations have one and only one variant with non-ZST
1492
- // fields which is referred to by the `dataful_variant` index. Their
1493
- // final size and alignment is equal to the one from the
1494
- // `dataful_variant`. All other variants either don't have any field
1495
- // or all fields types are ZST.
1496
- //
1497
- // Because of that, we can represent these enums as simple structures
1498
- // where each field represent one variant. This allows them to be
1499
- // referred to correctly.
1500
- //
1501
- // Note: I tried using a union instead but it had a significant runtime
1502
- // penalty.
1503
- tracing:: trace!(
1504
- ?name,
1505
- ?variants,
1506
- ?dataful_variant,
1507
- ?tag_encoding,
1508
- ?subst,
1509
- "codegen_enum: Niche"
1510
- ) ;
1511
- ctx. codegen_enum_cases (
1512
- name,
1513
- pretty_name,
1514
- adtdef,
1515
- subst,
1516
- variants,
1517
- Size :: ZERO ,
1518
- )
1519
- }
1487
+ } )
1488
+ }
1489
+ TagEncoding :: Niche { .. } => {
1490
+ self . codegen_enum_niche ( ty, adtdef, subst, variants)
1520
1491
}
1521
1492
}
1522
1493
}
1523
- } )
1494
+ }
1495
+ }
1496
+
1497
+ /// Codegen an enumeration that is encoded using niche optimization.
1498
+ ///
1499
+ /// Enumerations with multiple variants and niche encoding have a
1500
+ /// specific format that can be used to optimize its layout and reduce
1501
+ /// memory consumption.
1502
+ ///
1503
+ /// The niche is a location in the entire type where some bit pattern
1504
+ /// isn't valid. The compiler uses the `untagged_variant` index to
1505
+ /// access this field.
1506
+ /// The final size and alignment is also equal to the one from the
1507
+ /// `untagged_variant`. All other variants either don't have any field,
1508
+ /// or their size is smaller than the `untagged_variant`.
1509
+ /// See <https://github.com/rust-lang/rust/issues/46213> for more details.
1510
+ ///
1511
+ /// Because of that, we usually represent these enums as simple unions
1512
+ /// where each field represent one variant. This allows them to be
1513
+ /// referred to correctly.
1514
+ ///
1515
+ /// The one exception is the case where only one variant has data.
1516
+ /// We use a struct instead because it is more performant.
1517
+ fn codegen_enum_niche (
1518
+ & mut self ,
1519
+ ty : Ty < ' tcx > ,
1520
+ adtdef : & ' tcx AdtDef ,
1521
+ subst : & ' tcx InternalSubsts < ' tcx > ,
1522
+ variants : & IndexVec < VariantIdx , Layout > ,
1523
+ ) -> Type {
1524
+ let non_zst_count = variants. iter ( ) . filter ( |layout| layout. size ( ) . bytes ( ) > 0 ) . count ( ) ;
1525
+ let mangled_name = self . ty_mangled_name ( ty) ;
1526
+ let pretty_name = self . ty_pretty_name ( ty) ;
1527
+ tracing:: trace!( ?pretty_name, ?variants, ?subst, ?non_zst_count, "codegen_enum: Niche" ) ;
1528
+ if non_zst_count > 1 {
1529
+ self . ensure_union ( mangled_name, pretty_name, |gcx, name| {
1530
+ gcx. codegen_enum_cases ( name, pretty_name, adtdef, subst, variants, Size :: ZERO )
1531
+ } )
1532
+ } else {
1533
+ self . ensure_struct ( mangled_name, pretty_name, |gcx, name| {
1534
+ gcx. codegen_enum_cases ( name, pretty_name, adtdef, subst, variants, Size :: ZERO )
1535
+ } )
1536
+ }
1524
1537
}
1525
1538
1526
1539
pub ( crate ) fn variant_min_offset (
0 commit comments