@@ -9,8 +9,8 @@ use rustc_middle::mir;
9
9
use rustc_middle:: mir:: tcx:: PlaceTy ;
10
10
use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf , TyAndLayout } ;
11
11
use rustc_middle:: ty:: { self , Ty } ;
12
- use rustc_target:: abi:: { Abi , Align , FieldsShape , Int , TagEncoding } ;
13
- use rustc_target:: abi:: { VariantIdx , Variants } ;
12
+ use rustc_target:: abi:: { Abi , Align , FieldsShape , Int , Integer , TagEncoding } ;
13
+ use rustc_target:: abi:: { Primitive , Scalar , VariantIdx , Variants } ;
14
14
15
15
#[ derive( Copy , Clone , Debug ) ]
16
16
pub struct PlaceRef < ' tcx , V > {
@@ -265,9 +265,46 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
265
265
bx. cx ( ) . const_uint ( cast_to, untagged_variant. as_u32 ( ) as u64 ) ,
266
266
)
267
267
}
268
+ // Handle other non-patological cases (= without niche wraparound, and with
269
+ // niche_variants.end fitting in the tag type).
270
+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start }
271
+ if is_niche_after_nonniche ( tag_scalar, niche_start, niche_variants, bx. cx ( ) ) =>
272
+ {
273
+ let is_untagged = bx. icmp (
274
+ IntPredicate :: IntULT ,
275
+ tag,
276
+ bx. cx ( ) . const_uint_big ( niche_llty, niche_start) ,
277
+ ) ;
278
+
279
+ let first_niche_variant = niche_variants. start ( ) . as_u32 ( ) as u128 ;
280
+ let niche_discr = match first_niche_variant. cmp ( & niche_start) {
281
+ std:: cmp:: Ordering :: Less => {
282
+ let diff = niche_start - first_niche_variant;
283
+ let diff = bx. cx ( ) . const_uint_big ( niche_llty, diff) ;
284
+ bx. unchecked_usub ( tag, diff)
285
+ }
286
+ std:: cmp:: Ordering :: Equal => tag,
287
+ std:: cmp:: Ordering :: Greater => {
288
+ let diff = first_niche_variant as u64 - niche_start as u64 ;
289
+ let diff = bx. cx ( ) . const_uint ( niche_llty, diff) ;
290
+ bx. unchecked_uadd ( tag, diff)
291
+ }
292
+ } ;
293
+
294
+ let niche_discr = bx. sub ( tag, diff) ;
295
+ let discr = bx. select (
296
+ is_untagged,
297
+ bx. cx ( ) . const_uint ( niche_llty, untagged_variant. as_u32 ( ) as u64 ) ,
298
+ niche_discr,
299
+ ) ;
300
+
301
+ bx. intcast ( discr, cast_to, false )
302
+ }
268
303
TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
269
- // Assumption: here, llty should not be pointer.
270
- // FIXME(eddyb) check the actual primitive type here.
304
+ if matches ! ( tag_scalar. primitive( ) , Primitive :: Pointer ) {
305
+ // arith and non-null constants won't work on pointers
306
+ bug ! ( "pointer with more than one niche variant" )
307
+ }
271
308
272
309
// Rebase from niche values to discriminants, and check
273
310
// whether the result is in range for the niche variants.
@@ -552,3 +589,33 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
552
589
let offset = bx. and ( neg_value, align_minus_1) ;
553
590
bx. add ( value, offset)
554
591
}
592
+
593
+ /// Are all niche variants encoded in tag greater than the actual untagged tag values (so that we
594
+ /// can avoid the "relative discriminant" and we can simply `< niche_start` to ask whether it's
595
+ /// untagged or not). Also check if last _variant_ value fits in the _tag_'s type.
596
+ // Perhaps we can store this as flag in TagEncoding::Niche instead of recomputing?
597
+ fn is_niche_after_nonniche (
598
+ tag : Scalar ,
599
+ niche_start : u128 ,
600
+ niche_variants : & std:: ops:: RangeInclusive < VariantIdx > ,
601
+ cx : & impl rustc_target:: abi:: HasDataLayout ,
602
+ ) -> bool {
603
+ let tag_range = tag. valid_range ( cx) ;
604
+ if tag_range. wraps ( ) {
605
+ return false ;
606
+ }
607
+ let n_variants = niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
608
+ match tag. primitive ( ) {
609
+ Int ( int, _) => {
610
+ if niche_start. checked_add ( n_variants as u128 ) != Some ( tag_range. end ) {
611
+ return false ;
612
+ }
613
+ match int {
614
+ Integer :: I8 if niche_variants. end ( ) . as_u32 ( ) > u8:: MAX as u32 => false ,
615
+ Integer :: I16 if niche_variants. end ( ) . as_u32 ( ) > u16:: MAX as u32 => false ,
616
+ _ => true ,
617
+ }
618
+ }
619
+ _ => false ,
620
+ }
621
+ }
0 commit comments