@@ -1418,32 +1418,34 @@ impl<'tcx> GotocCtx<'tcx> {
14181418 subst : & ' tcx InternalSubsts < ' tcx > ,
14191419 ) -> Type {
14201420 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 , _| {
14281428 match source_variants. get ( * index) {
14291429 None => {
14301430 // an empty enum with no variants (its value cannot be instantiated)
14311431 vec ! [ ]
14321432 }
14331433 Some ( variant) => {
14341434 // 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 )
14371437 }
14381438 }
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| {
14471449 // For direct encoding of tags, we generate a type with two fields:
14481450 // ```
14491451 // struct tag-<> { // enum type
@@ -1455,22 +1457,22 @@ impl<'tcx> GotocCtx<'tcx> {
14551457 // (`#[repr]`) and it represents which variant is being used.
14561458 // The `cases` field is a union of all variant types where the name
14571459 // 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 ;
14611463 let initial_offset =
1462- ctx . variant_min_offset ( variants) . unwrap_or ( discr_offset) ;
1464+ gcx . variant_min_offset ( variants) . unwrap_or ( discr_offset) ;
14631465 let mut fields = vec ! [ DatatypeComponent :: field( "case" , int) ] ;
14641466 if let Some ( padding) =
1465- ctx . codegen_struct_padding ( discr_offset, initial_offset, 0 )
1467+ gcx . codegen_struct_padding ( discr_offset, initial_offset, 0 )
14661468 {
14671469 fields. push ( padding) ;
14681470 }
14691471 let union_name = format ! ( "{}-union" , name) ;
14701472 let union_pretty_name = format ! ( "{}-union" , pretty_name) ;
14711473 fields. push ( DatatypeComponent :: field (
14721474 "cases" ,
1473- ctx . ensure_union ( & union_name, & union_pretty_name, |ctx, name| {
1475+ gcx . ensure_union ( & union_name, & union_pretty_name, |ctx, name| {
14741476 ctx. codegen_enum_cases (
14751477 name,
14761478 pretty_name,
@@ -1482,45 +1484,56 @@ impl<'tcx> GotocCtx<'tcx> {
14821484 } ) ,
14831485 ) ) ;
14841486 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)
15201491 }
15211492 }
15221493 }
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+ }
15241537 }
15251538
15261539 pub ( crate ) fn variant_min_offset (
0 commit comments