@@ -3,7 +3,9 @@ use std::fmt;
33use arrayvec:: ArrayVec ;
44use either:: Either ;
55use 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+ } ;
79use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
810use rustc_middle:: mir:: { self , ConstValue } ;
911use rustc_middle:: ty:: Ty ;
@@ -510,6 +512,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
510512 ) ;
511513
512514 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) ;
513517
514518 // We have a subrange `niche_start..=niche_end` inside `range`.
515519 // 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> {
525529 // untagged_variant
526530 // }
527531 // 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 {
538536 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) ,
552575 ) ;
553576
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
560624 {
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;
566643 }
644+ }
567645
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+ }
570669
670+ let delta = niche_variants. start ( ) . as_u32 ( ) ;
571671 let tagged_discr = if delta == 0 {
572- tagged_discr
672+ relative_discr
573673 } 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) ) )
575675 } ;
576676
577677 let discr = bx. select (
0 commit comments