@@ -3,7 +3,9 @@ use std::fmt;
3
3
use arrayvec:: ArrayVec ;
4
4
use either:: Either ;
5
5
use rustc_abi as abi;
6
- use rustc_abi:: { Align , BackendRepr , FIRST_VARIANT , Primitive , Size , TagEncoding , Variants } ;
6
+ use rustc_abi:: {
7
+ Align , BackendRepr , FIRST_VARIANT , Primitive , Size , TagEncoding , VariantIdx , Variants ,
8
+ } ;
7
9
use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
8
10
use rustc_middle:: mir:: { self , ConstValue } ;
9
11
use rustc_middle:: ty:: Ty ;
@@ -510,6 +512,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
510
512
) ;
511
513
512
514
let relative_max = niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
515
+ let tag_range = tag_scalar. valid_range ( & dl) ;
516
+ let tag_size = tag_scalar. size ( & dl) ;
513
517
514
518
// We have a subrange `niche_start..=niche_end` inside `range`.
515
519
// If the value of the tag is inside this subrange, it's a
@@ -525,53 +529,149 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
525
529
// untagged_variant
526
530
// }
527
531
// However, we will likely be able to emit simpler code.
528
- let ( is_niche, tagged_discr, delta) = if relative_max == 0 {
529
- // Best case scenario: only one tagged variant. This will
530
- // likely become just a comparison and a jump.
531
- // The algorithm is:
532
- // is_niche = tag == niche_start
533
- // discr = if is_niche {
534
- // niche_start
535
- // } else {
536
- // untagged_variant
537
- // }
532
+
533
+ // First, the incredibly-common case of a two-variant enum (like
534
+ // `Option` or `Result`) where we only need one check.
535
+ if relative_max == 0 {
538
536
let niche_start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
539
- let is_niche = bx. icmp ( IntPredicate :: IntEQ , tag, niche_start) ;
540
- let tagged_discr =
541
- bx. cx ( ) . const_uint ( cast_to, niche_variants. start ( ) . as_u32 ( ) as u64 ) ;
542
- ( is_niche, tagged_discr, 0 )
543
- } else {
544
- // The special cases don't apply, so we'll have to go with
545
- // the general algorithm.
546
- let relative_discr = bx. sub ( tag, bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ) ;
547
- let cast_tag = bx. intcast ( relative_discr, cast_to, false ) ;
548
- let is_niche = bx. icmp (
549
- IntPredicate :: IntULE ,
550
- relative_discr,
551
- bx. cx ( ) . const_uint ( tag_llty, relative_max as u64 ) ,
537
+ let is_natural = bx. icmp ( IntPredicate :: IntNE , tag, niche_start) ;
538
+ return if untagged_variant == VariantIdx :: from_u32 ( 1 )
539
+ && * niche_variants. start ( ) == VariantIdx :: from_u32 ( 0 )
540
+ {
541
+ // The polarity of the comparison above is picked so we can
542
+ // just extend for `Option<T>`, which has these variants.
543
+ bx. zext ( is_natural, cast_to)
544
+ } else {
545
+ let tagged_discr =
546
+ bx. cx ( ) . const_uint ( cast_to, u64:: from ( niche_variants. start ( ) . as_u32 ( ) ) ) ;
547
+ let untagged_discr =
548
+ bx. cx ( ) . const_uint ( cast_to, u64:: from ( untagged_variant. as_u32 ( ) ) ) ;
549
+ bx. select ( is_natural, untagged_discr, tagged_discr)
550
+ } ;
551
+ }
552
+
553
+ let niche_end =
554
+ tag_size. truncate ( u128:: from ( relative_max) . wrapping_add ( niche_start) ) ;
555
+
556
+ // Next, the layout algorithm prefers to put the niches at one end,
557
+ // so look for cases where we don't need to calculate a relative_tag
558
+ // at all and can just look at the original tag value directly.
559
+ // This also lets us move any possibly-wrapping addition to the end
560
+ // where it's easiest to get rid of in the normal uses: it's easy
561
+ // to optimize `COMPLICATED + 7 == 2` to `COMPLICATED == (2 - 7)`.
562
+ {
563
+ // Work in whichever size is wider, so we don't need to worry
564
+ // about wrapping when computing `wide_niche_untagged`.
565
+ let ( wide_size, wide_ibty) = if cast_to_layout. size > tag_size {
566
+ ( cast_to_layout. size , cast_to)
567
+ } else {
568
+ ( tag_size, tag_llty)
569
+ } ;
570
+
571
+ let wide_niche_to_variant =
572
+ u128:: from ( niche_variants. start ( ) . as_u32 ( ) ) . wrapping_sub ( niche_start) ;
573
+ let wide_niche_untagged = wide_size. truncate (
574
+ u128:: from ( untagged_variant. as_u32 ( ) ) . wrapping_sub ( wide_niche_to_variant) ,
552
575
) ;
553
576
554
- // Thanks to parameter attributes and load metadata, LLVM already knows
555
- // the general valid range of the tag. It's possible, though, for there
556
- // to be an impossible value *in the middle*, which those ranges don't
557
- // communicate, so it's worth an `assume` to let the optimizer know.
558
- if niche_variants. contains ( & untagged_variant)
559
- && bx. cx ( ) . sess ( ) . opts . optimize != OptLevel :: No
577
+ let wide_tag_and_is_niche_and_needs_assume =
578
+ if tag_range. no_unsigned_wraparound ( tag_size) == Ok ( true ) {
579
+ let wide_tag = bx. zext ( tag, wide_ibty) ;
580
+ Some ( if tag_range. start == niche_start {
581
+ let end = bx. cx ( ) . const_uint_big ( tag_llty, niche_end) ;
582
+ (
583
+ wide_tag,
584
+ bx. icmp ( IntPredicate :: IntULE , tag, end) ,
585
+ wide_niche_untagged <= niche_end,
586
+ )
587
+ } else if tag_range. end == niche_end {
588
+ let start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
589
+ (
590
+ wide_tag,
591
+ bx. icmp ( IntPredicate :: IntUGE , tag, start) ,
592
+ wide_niche_untagged >= niche_start,
593
+ )
594
+ } else {
595
+ bug ! ( )
596
+ } )
597
+ } else if tag_range. no_signed_wraparound ( tag_size) == Ok ( true ) {
598
+ let wide_tag = bx. sext ( tag, wide_ibty) ;
599
+ let wide_niche_untagged = wide_size. sign_extend ( wide_niche_untagged) ;
600
+ Some ( if tag_range. start == niche_start {
601
+ let end = bx. cx ( ) . const_uint_big ( tag_llty, niche_end) ;
602
+ let niche_end = wide_size. sign_extend ( niche_end) ;
603
+ (
604
+ wide_tag,
605
+ bx. icmp ( IntPredicate :: IntSLE , tag, end) ,
606
+ wide_niche_untagged <= niche_end,
607
+ )
608
+ } else if tag_range. end == niche_end {
609
+ let start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
610
+ let niche_start = wide_size. sign_extend ( niche_start) ;
611
+ (
612
+ wide_tag,
613
+ bx. icmp ( IntPredicate :: IntSGE , tag, start) ,
614
+ wide_niche_untagged >= niche_start,
615
+ )
616
+ } else {
617
+ bug ! ( )
618
+ } )
619
+ } else {
620
+ None
621
+ } ;
622
+ if let Some ( ( wide_tag, is_niche, needs_assume) ) =
623
+ wide_tag_and_is_niche_and_needs_assume
560
624
{
561
- let impossible =
562
- u64:: from ( untagged_variant. as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ) ;
563
- let impossible = bx. cx ( ) . const_uint ( tag_llty, impossible) ;
564
- let ne = bx. icmp ( IntPredicate :: IntNE , relative_discr, impossible) ;
565
- bx. assume ( ne) ;
625
+ let wide_niche_untagged =
626
+ bx. cx ( ) . const_uint_big ( wide_ibty, wide_niche_untagged) ;
627
+ if needs_assume && bx. cx ( ) . sess ( ) . opts . optimize != OptLevel :: No {
628
+ let not_untagged =
629
+ bx. icmp ( IntPredicate :: IntNE , wide_tag, wide_niche_untagged) ;
630
+ bx. assume ( not_untagged) ;
631
+ }
632
+
633
+ let wide_niche = bx. select ( is_niche, wide_tag, wide_niche_untagged) ;
634
+ let cast_niche = bx. trunc ( wide_niche, cast_to) ;
635
+ let discr = if wide_niche_to_variant == 0 {
636
+ cast_niche
637
+ } else {
638
+ let niche_to_variant =
639
+ bx. cx ( ) . const_uint_big ( cast_to, wide_niche_to_variant) ;
640
+ bx. add ( cast_niche, niche_to_variant)
641
+ } ;
642
+ return discr;
566
643
}
644
+ }
567
645
568
- ( is_niche, cast_tag, niche_variants. start ( ) . as_u32 ( ) as u128 )
569
- } ;
646
+ // Otherwise the special cases don't apply,
647
+ // so we'll have to go with the general algorithm.
648
+ let relative_tag = bx. sub ( tag, bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ) ;
649
+ let relative_discr = bx. intcast ( relative_tag, cast_to, false ) ;
650
+ let is_niche = bx. icmp (
651
+ IntPredicate :: IntULE ,
652
+ relative_tag,
653
+ bx. cx ( ) . const_uint ( tag_llty, u64:: from ( relative_max) ) ,
654
+ ) ;
655
+
656
+ // Thanks to parameter attributes and load metadata, LLVM already knows
657
+ // the general valid range of the tag. It's possible, though, for there
658
+ // to be an impossible value *in the middle*, which those ranges don't
659
+ // communicate, so it's worth an `assume` to let the optimizer know.
660
+ if niche_variants. contains ( & untagged_variant)
661
+ && bx. cx ( ) . sess ( ) . opts . optimize != OptLevel :: No
662
+ {
663
+ let impossible =
664
+ u64:: from ( untagged_variant. as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ) ;
665
+ let impossible = bx. cx ( ) . const_uint ( tag_llty, impossible) ;
666
+ let ne = bx. icmp ( IntPredicate :: IntNE , relative_tag, impossible) ;
667
+ bx. assume ( ne) ;
668
+ }
570
669
670
+ let delta = niche_variants. start ( ) . as_u32 ( ) ;
571
671
let tagged_discr = if delta == 0 {
572
- tagged_discr
672
+ relative_discr
573
673
} else {
574
- bx. add ( tagged_discr , bx. cx ( ) . const_uint_big ( cast_to, delta) )
674
+ bx. add ( relative_discr , bx. cx ( ) . const_uint ( cast_to, u64 :: from ( delta) ) )
575
675
} ;
576
676
577
677
let discr = bx. select (
0 commit comments