@@ -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,189 @@ 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 ) ,
552- ) ;
553-
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
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 )
560540 {
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) ;
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 + 2 == 7` to `COMPLICATED == (7 - 2)`.
562+ {
563+ // Work in whichever size is wider, because it's possible for
564+ // the untagged variant to be further away from the niches than
565+ // is possible to represent in the smaller type.
566+ let ( wide_size, wide_ibty) = if cast_to_layout. size > tag_size {
567+ ( cast_to_layout. size , cast_to)
568+ } else {
569+ ( tag_size, tag_llty)
570+ } ;
571+
572+ struct NoWrapData < V > {
573+ wide_tag : V ,
574+ is_niche : V ,
575+ needs_assume : bool ,
576+ wide_niche_to_variant : u128 ,
577+ wide_niche_untagged : u128 ,
566578 }
567579
568- ( is_niche, cast_tag, niche_variants. start ( ) . as_u32 ( ) as u128 )
569- } ;
580+ let first_variant = u128:: from ( niche_variants. start ( ) . as_u32 ( ) ) ;
581+ let untagged_variant = u128:: from ( untagged_variant. as_u32 ( ) ) ;
582+
583+ let opt_data = if tag_range. no_unsigned_wraparound ( tag_size) == Ok ( true ) {
584+ let wide_tag = bx. zext ( tag, wide_ibty) ;
585+ let extend = |x| x;
586+ let wide_niche_start = extend ( niche_start) ;
587+ let wide_niche_end = extend ( niche_end) ;
588+ debug_assert ! ( wide_niche_start <= wide_niche_end) ;
589+ let wide_first_variant = extend ( first_variant) ;
590+ let wide_untagged_variant = extend ( untagged_variant) ;
591+ let wide_niche_to_variant =
592+ wide_first_variant. wrapping_sub ( wide_niche_start) ;
593+ let wide_niche_untagged = wide_size
594+ . truncate ( wide_untagged_variant. wrapping_sub ( wide_niche_to_variant) ) ;
595+ let ( is_niche, needs_assume) = if tag_range. start == niche_start {
596+ let end = bx. cx ( ) . const_uint_big ( tag_llty, niche_end) ;
597+ (
598+ bx. icmp ( IntPredicate :: IntULE , tag, end) ,
599+ wide_niche_untagged <= wide_niche_end,
600+ )
601+ } else if tag_range. end == niche_end {
602+ let start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
603+ (
604+ bx. icmp ( IntPredicate :: IntUGE , tag, start) ,
605+ wide_niche_untagged >= wide_niche_start,
606+ )
607+ } else {
608+ bug ! ( )
609+ } ;
610+ Some ( NoWrapData {
611+ wide_tag,
612+ is_niche,
613+ needs_assume,
614+ wide_niche_to_variant,
615+ wide_niche_untagged,
616+ } )
617+ } else if tag_range. no_signed_wraparound ( tag_size) == Ok ( true ) {
618+ let wide_tag = bx. sext ( tag, wide_ibty) ;
619+ let extend = |x| tag_size. sign_extend ( x) ;
620+ let wide_niche_start = extend ( niche_start) ;
621+ let wide_niche_end = extend ( niche_end) ;
622+ debug_assert ! ( wide_niche_start <= wide_niche_end) ;
623+ let wide_first_variant = extend ( first_variant) ;
624+ let wide_untagged_variant = extend ( untagged_variant) ;
625+ let wide_niche_to_variant =
626+ wide_first_variant. wrapping_sub ( wide_niche_start) ;
627+ let wide_niche_untagged = wide_size. sign_extend (
628+ wide_untagged_variant
629+ . wrapping_sub ( wide_niche_to_variant)
630+ . cast_unsigned ( ) ,
631+ ) ;
632+ let ( is_niche, needs_assume) = if tag_range. start == niche_start {
633+ let end = bx. cx ( ) . const_uint_big ( tag_llty, niche_end) ;
634+ (
635+ bx. icmp ( IntPredicate :: IntSLE , tag, end) ,
636+ wide_niche_untagged <= wide_niche_end,
637+ )
638+ } else if tag_range. end == niche_end {
639+ let start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
640+ (
641+ bx. icmp ( IntPredicate :: IntSGE , tag, start) ,
642+ wide_niche_untagged >= wide_niche_start,
643+ )
644+ } else {
645+ bug ! ( )
646+ } ;
647+ Some ( NoWrapData {
648+ wide_tag,
649+ is_niche,
650+ needs_assume,
651+ wide_niche_to_variant : wide_niche_to_variant. cast_unsigned ( ) ,
652+ wide_niche_untagged : wide_niche_untagged. cast_unsigned ( ) ,
653+ } )
654+ } else {
655+ None
656+ } ;
657+ if let Some ( NoWrapData {
658+ wide_tag,
659+ is_niche,
660+ needs_assume,
661+ wide_niche_to_variant,
662+ wide_niche_untagged,
663+ } ) = opt_data
664+ {
665+ let wide_niche_untagged =
666+ bx. cx ( ) . const_uint_big ( wide_ibty, wide_niche_untagged) ;
667+ if needs_assume && bx. cx ( ) . sess ( ) . opts . optimize != OptLevel :: No {
668+ let not_untagged =
669+ bx. icmp ( IntPredicate :: IntNE , wide_tag, wide_niche_untagged) ;
670+ bx. assume ( not_untagged) ;
671+ }
672+
673+ let wide_niche = bx. select ( is_niche, wide_tag, wide_niche_untagged) ;
674+ let cast_niche = bx. trunc ( wide_niche, cast_to) ;
675+ let discr = if wide_niche_to_variant == 0 {
676+ cast_niche
677+ } else {
678+ let niche_to_variant =
679+ bx. cx ( ) . const_uint_big ( cast_to, wide_niche_to_variant) ;
680+ bx. add ( cast_niche, niche_to_variant)
681+ } ;
682+ return discr;
683+ }
684+ }
685+
686+ // Otherwise the special cases don't apply,
687+ // so we'll have to go with the general algorithm.
688+ let relative_tag = bx. sub ( tag, bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ) ;
689+ let relative_discr = bx. intcast ( relative_tag, cast_to, false ) ;
690+ let is_niche = bx. icmp (
691+ IntPredicate :: IntULE ,
692+ relative_tag,
693+ bx. cx ( ) . const_uint ( tag_llty, u64:: from ( relative_max) ) ,
694+ ) ;
695+
696+ // Thanks to parameter attributes and load metadata, LLVM already knows
697+ // the general valid range of the tag. It's possible, though, for there
698+ // to be an impossible value *in the middle*, which those ranges don't
699+ // communicate, so it's worth an `assume` to let the optimizer know.
700+ if niche_variants. contains ( & untagged_variant)
701+ && bx. cx ( ) . sess ( ) . opts . optimize != OptLevel :: No
702+ {
703+ let impossible =
704+ u64:: from ( untagged_variant. as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ) ;
705+ let impossible = bx. cx ( ) . const_uint ( tag_llty, impossible) ;
706+ let ne = bx. icmp ( IntPredicate :: IntNE , relative_tag, impossible) ;
707+ bx. assume ( ne) ;
708+ }
570709
710+ let delta = niche_variants. start ( ) . as_u32 ( ) ;
571711 let tagged_discr = if delta == 0 {
572- tagged_discr
712+ relative_discr
573713 } else {
574- bx. add ( tagged_discr , bx. cx ( ) . const_uint_big ( cast_to, delta) )
714+ bx. add ( relative_discr , bx. cx ( ) . const_uint ( cast_to, u64 :: from ( delta) ) )
575715 } ;
576716
577717 let discr = bx. select (
0 commit comments