@@ -805,19 +805,19 @@ pub enum Variants {
805
805
variants : Vec < CachedLayout > ,
806
806
} ,
807
807
808
- /// Two cases distinguished by a niche (a value invalid for a type):
808
+ /// Multiple cases distinguished by a niche (values invalid for a type):
809
809
/// the variant `dataful_variant` contains a niche at an arbitrary
810
- /// offset (field 0 of the enum), which is set to `niche_value`
811
- /// for the other variant .
810
+ /// offset (field 0 of the enum), which for a variant with discriminant
811
+ /// `d` is set to `(d - niche_variants.start).wrapping_add(niche_start)` .
812
812
///
813
813
/// For example, `Option<(usize, &T)>` is represented such that
814
814
/// `None` has a null pointer for the second tuple field, and
815
815
/// `Some` is the identity function (with a non-null reference).
816
816
NicheFilling {
817
817
dataful_variant : usize ,
818
- niche_variant : usize ,
818
+ niche_variants : RangeInclusive < usize > ,
819
819
niche : Scalar ,
820
- niche_value : u128 ,
820
+ niche_start : u128 ,
821
821
variants : Vec < CachedLayout > ,
822
822
}
823
823
}
@@ -1372,11 +1372,11 @@ impl<'a, 'tcx> CachedLayout {
1372
1372
} ) . collect :: < Result < Vec < _ > , _ > > ( )
1373
1373
} ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1374
1374
1375
- let ( inh_first, inh_second, inh_third ) = {
1375
+ let ( inh_first, inh_second) = {
1376
1376
let mut inh_variants = ( 0 ..variants. len ( ) ) . filter ( |& v| {
1377
1377
variants[ v] . iter ( ) . all ( |f| f. abi != Abi :: Uninhabited )
1378
1378
} ) ;
1379
- ( inh_variants. next ( ) , inh_variants. next ( ) , inh_variants . next ( ) )
1379
+ ( inh_variants. next ( ) , inh_variants. next ( ) )
1380
1380
} ;
1381
1381
if inh_first. is_none ( ) {
1382
1382
// Uninhabited because it has no variants, or only uninhabited ones.
@@ -1472,68 +1472,94 @@ impl<'a, 'tcx> CachedLayout {
1472
1472
let no_explicit_discriminants = def. variants . iter ( ) . enumerate ( )
1473
1473
. all ( |( i, v) | v. discr == ty:: VariantDiscr :: Relative ( i) ) ;
1474
1474
1475
- if inh_second. is_some ( ) && inh_third. is_none ( ) &&
1476
- !def. repr . inhibit_enum_layout_opt ( ) &&
1477
- no_explicit_discriminants {
1478
- // Nullable pointer optimization
1479
- let ( a, b) = ( inh_first. unwrap ( ) , inh_second. unwrap ( ) ) ;
1480
- for & ( i, other) in & [ ( a, b) , ( b, a) ] {
1481
- if !variants[ other] . iter ( ) . all ( |f| f. is_zst ( ) ) {
1482
- continue ;
1475
+ // Niche-filling enum optimization.
1476
+ if !def. repr . inhibit_enum_layout_opt ( ) && no_explicit_discriminants {
1477
+ let mut dataful_variant = None ;
1478
+ let mut niche_variants = usize:: max_value ( ) ..=0 ;
1479
+
1480
+ // Find one non-ZST variant.
1481
+ ' variants: for ( v, fields) in variants. iter ( ) . enumerate ( ) {
1482
+ for f in fields {
1483
+ if f. abi == Abi :: Uninhabited {
1484
+ continue ' variants;
1485
+ }
1486
+ if !f. is_zst ( ) {
1487
+ if dataful_variant. is_none ( ) {
1488
+ dataful_variant = Some ( v) ;
1489
+ continue ' variants;
1490
+ } else {
1491
+ dataful_variant = None ;
1492
+ break ' variants;
1493
+ }
1494
+ }
1495
+ }
1496
+ if niche_variants. start > v {
1497
+ niche_variants. start = v;
1483
1498
}
1499
+ niche_variants. end = v;
1500
+ }
1501
+
1502
+ if niche_variants. start > niche_variants. end {
1503
+ dataful_variant = None ;
1504
+ }
1484
1505
1506
+ if let Some ( i) = dataful_variant {
1507
+ let count = ( niche_variants. end - niche_variants. start + 1 ) as u128 ;
1485
1508
for ( field_index, field) in variants[ i] . iter ( ) . enumerate ( ) {
1486
- if let Some ( ( offset, niche, niche_value) ) = field. find_niche ( cx) ? {
1487
- let st = variants. iter ( ) . enumerate ( ) . map ( |( j, v) | {
1488
- let mut st = univariant_uninterned ( v,
1489
- & def. repr , StructKind :: AlwaysSized ) ?;
1490
- st. variants = Variants :: Single { index : j } ;
1491
- Ok ( st)
1492
- } ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1493
-
1494
- let offset = st[ i] . fields . offset ( field_index) + offset;
1495
- let CachedLayout {
1496
- size,
1497
- mut align,
1498
- mut primitive_align,
1499
- ..
1500
- } = st[ i] ;
1501
-
1502
- let mut niche_align = niche. value . align ( dl) ;
1503
- let abi = if offset. bytes ( ) == 0 && niche. value . size ( dl) == size {
1504
- Abi :: Scalar ( niche. clone ( ) )
1505
- } else {
1506
- let mut packed = st[ i] . abi . is_packed ( ) ;
1507
- if offset. abi_align ( niche_align) != offset {
1508
- packed = true ;
1509
- niche_align = dl. i8_align ;
1510
- }
1511
- Abi :: Aggregate {
1512
- sized : true ,
1513
- packed
1514
- }
1509
+ let ( offset, niche, niche_start) =
1510
+ match field. find_niche ( cx, count) ? {
1511
+ Some ( niche) => niche,
1512
+ None => continue
1515
1513
} ;
1516
- align = align. max ( niche_align) ;
1517
- primitive_align = primitive_align. max ( niche_align) ;
1518
-
1519
- return Ok ( tcx. intern_layout ( CachedLayout {
1520
- variants : Variants :: NicheFilling {
1521
- dataful_variant : i,
1522
- niche_variant : other,
1523
- niche,
1524
- niche_value,
1525
- variants : st,
1526
- } ,
1527
- fields : FieldPlacement :: Arbitrary {
1528
- offsets : vec ! [ offset] ,
1529
- memory_index : vec ! [ 0 ]
1530
- } ,
1531
- abi,
1532
- size,
1533
- align,
1534
- primitive_align
1535
- } ) ) ;
1536
- }
1514
+ let st = variants. iter ( ) . enumerate ( ) . map ( |( j, v) | {
1515
+ let mut st = univariant_uninterned ( v,
1516
+ & def. repr , StructKind :: AlwaysSized ) ?;
1517
+ st. variants = Variants :: Single { index : j } ;
1518
+ Ok ( st)
1519
+ } ) . collect :: < Result < Vec < _ > , _ > > ( ) ?;
1520
+
1521
+ let offset = st[ i] . fields . offset ( field_index) + offset;
1522
+ let CachedLayout {
1523
+ size,
1524
+ mut align,
1525
+ mut primitive_align,
1526
+ ..
1527
+ } = st[ i] ;
1528
+
1529
+ let mut niche_align = niche. value . align ( dl) ;
1530
+ let abi = if offset. bytes ( ) == 0 && niche. value . size ( dl) == size {
1531
+ Abi :: Scalar ( niche. clone ( ) )
1532
+ } else {
1533
+ let mut packed = st[ i] . abi . is_packed ( ) ;
1534
+ if offset. abi_align ( niche_align) != offset {
1535
+ packed = true ;
1536
+ niche_align = dl. i8_align ;
1537
+ }
1538
+ Abi :: Aggregate {
1539
+ sized : true ,
1540
+ packed
1541
+ }
1542
+ } ;
1543
+ align = align. max ( niche_align) ;
1544
+ primitive_align = primitive_align. max ( niche_align) ;
1545
+
1546
+ return Ok ( tcx. intern_layout ( CachedLayout {
1547
+ variants : Variants :: NicheFilling {
1548
+ dataful_variant : i,
1549
+ niche_variants,
1550
+ niche,
1551
+ niche_start,
1552
+ variants : st,
1553
+ } ,
1554
+ fields : FieldPlacement :: Arbitrary {
1555
+ offsets : vec ! [ offset] ,
1556
+ memory_index : vec ! [ 0 ]
1557
+ } ,
1558
+ abi,
1559
+ size,
1560
+ align,
1561
+ primitive_align
1562
+ } ) ) ;
1537
1563
}
1538
1564
}
1539
1565
}
@@ -2272,50 +2298,50 @@ impl<'a, 'tcx> TyLayout<'tcx> {
2272
2298
}
2273
2299
2274
2300
/// Find the offset of a niche leaf field, starting from
2275
- /// the given type and recursing through aggregates.
2301
+ /// the given type and recursing through aggregates, which
2302
+ /// has at least `count` consecutive invalid values.
2276
2303
/// The tuple is `(offset, scalar, niche_value)`.
2277
2304
// FIXME(eddyb) traverse already optimized enums.
2278
- fn find_niche < C > ( & self , cx : C )
2305
+ fn find_niche < C > ( & self , cx : C , count : u128 )
2279
2306
-> Result < Option < ( Size , Scalar , u128 ) > , LayoutError < ' tcx > >
2280
2307
where C : LayoutOf < Ty < ' tcx > , TyLayout = Result < Self , LayoutError < ' tcx > > > +
2281
2308
HasTyCtxt < ' tcx >
2282
2309
{
2283
2310
let scalar_component = |scalar : & Scalar , offset| {
2284
- // FIXME(eddyb) support negative/wrap-around discriminant ranges.
2285
- let Scalar { value, ref valid_range } = * scalar;
2286
- if valid_range. start < valid_range. end {
2287
- let bits = value. size ( cx) . bits ( ) ;
2288
- assert ! ( bits <= 128 ) ;
2289
- let max_value = !0u128 >> ( 128 - bits) ;
2290
- if valid_range. start > 0 {
2291
- let niche = valid_range. start - 1 ;
2292
- Ok ( Some ( ( offset, Scalar {
2293
- value,
2294
- valid_range : niche..=valid_range. end
2295
- } , niche) ) )
2296
- } else if valid_range. end < max_value {
2297
- let niche = valid_range. end + 1 ;
2298
- Ok ( Some ( ( offset, Scalar {
2299
- value,
2300
- valid_range : valid_range. start ..=niche
2301
- } , niche) ) )
2302
- } else {
2303
- Ok ( None )
2304
- }
2311
+ let Scalar { value, valid_range : ref v } = * scalar;
2312
+
2313
+ let bits = value. size ( cx) . bits ( ) ;
2314
+ assert ! ( bits <= 128 ) ;
2315
+ let max_value = !0u128 >> ( 128 - bits) ;
2316
+
2317
+ // Find out how many values are outside the valid range.
2318
+ let niches = if v. start <= v. end {
2319
+ v. start + ( max_value - v. end )
2305
2320
} else {
2306
- Ok ( None )
2321
+ v. start - v. end - 1
2322
+ } ;
2323
+
2324
+ // Give up if we can't fit `count` consecutive niches.
2325
+ if count > niches {
2326
+ return None ;
2307
2327
}
2328
+
2329
+ let niche_start = v. end . wrapping_add ( 1 ) & max_value;
2330
+ let niche_end = v. end . wrapping_add ( count) & max_value;
2331
+ Some ( ( offset, Scalar {
2332
+ value,
2333
+ valid_range : v. start ..=niche_end
2334
+ } , niche_start) )
2308
2335
} ;
2309
2336
2310
2337
match self . abi {
2311
2338
Abi :: Scalar ( ref scalar) => {
2312
- return scalar_component ( scalar, Size :: from_bytes ( 0 ) ) ;
2339
+ return Ok ( scalar_component ( scalar, Size :: from_bytes ( 0 ) ) ) ;
2313
2340
}
2314
2341
Abi :: ScalarPair ( ref a, ref b) => {
2315
- if let Some ( result) = scalar_component ( a, Size :: from_bytes ( 0 ) ) ? {
2316
- return Ok ( Some ( result) ) ;
2317
- }
2318
- return scalar_component ( b, a. value . size ( cx) . abi_align ( b. value . align ( cx) ) ) ;
2342
+ return Ok ( scalar_component ( a, Size :: from_bytes ( 0 ) ) . or_else ( || {
2343
+ scalar_component ( b, a. value . size ( cx) . abi_align ( b. value . align ( cx) ) )
2344
+ } ) ) ;
2319
2345
}
2320
2346
_ => { }
2321
2347
}
@@ -2328,13 +2354,13 @@ impl<'a, 'tcx> TyLayout<'tcx> {
2328
2354
return Ok ( None ) ;
2329
2355
}
2330
2356
}
2331
- if let FieldPlacement :: Array { count , .. } = self . fields {
2332
- if count > 0 {
2333
- return self . field ( cx, 0 ) ?. find_niche ( cx) ;
2357
+ if let FieldPlacement :: Array { .. } = self . fields {
2358
+ if self . fields . count ( ) > 0 {
2359
+ return self . field ( cx, 0 ) ?. find_niche ( cx, count ) ;
2334
2360
}
2335
2361
}
2336
2362
for i in 0 ..self . fields . count ( ) {
2337
- let r = self . field ( cx, i) ?. find_niche ( cx) ?;
2363
+ let r = self . field ( cx, i) ?. find_niche ( cx, count ) ?;
2338
2364
if let Some ( ( offset, scalar, niche_value) ) = r {
2339
2365
let offset = self . fields . offset ( i) + offset;
2340
2366
return Ok ( Some ( ( offset, scalar, niche_value) ) ) ;
@@ -2364,15 +2390,16 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
2364
2390
}
2365
2391
NicheFilling {
2366
2392
dataful_variant,
2367
- niche_variant ,
2393
+ niche_variants : RangeInclusive { start , end } ,
2368
2394
ref niche,
2369
- niche_value ,
2395
+ niche_start ,
2370
2396
ref variants,
2371
2397
} => {
2372
2398
dataful_variant. hash_stable ( hcx, hasher) ;
2373
- niche_variant. hash_stable ( hcx, hasher) ;
2399
+ start. hash_stable ( hcx, hasher) ;
2400
+ end. hash_stable ( hcx, hasher) ;
2374
2401
niche. hash_stable ( hcx, hasher) ;
2375
- niche_value . hash_stable ( hcx, hasher) ;
2402
+ niche_start . hash_stable ( hcx, hasher) ;
2376
2403
variants. hash_stable ( hcx, hasher) ;
2377
2404
}
2378
2405
}
0 commit comments