@@ -3,16 +3,17 @@ use std::fmt;
33use arrayvec:: ArrayVec ;
44use either:: Either ;
55use rustc_abi as abi;
6- use rustc_abi:: { Align , BackendRepr , Size } ;
6+ use rustc_abi:: { Align , BackendRepr , FIRST_VARIANT , Primitive , Size , TagEncoding , Variants } ;
77use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
88use rustc_middle:: mir:: { self , ConstValue } ;
99use rustc_middle:: ty:: Ty ;
1010use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
1111use rustc_middle:: { bug, span_bug} ;
12- use tracing:: debug;
12+ use tracing:: { debug, instrument } ;
1313
1414use super :: place:: { PlaceRef , PlaceValue } ;
1515use super :: { FunctionCx , LocalRef } ;
16+ use crate :: common:: IntPredicate ;
1617use crate :: traits:: * ;
1718use crate :: { MemFlags , size_of_val} ;
1819
@@ -415,6 +416,149 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
415416
416417 OperandRef { val, layout : field }
417418 }
419+
420+ /// Obtain the actual discriminant of a value.
421+ #[ instrument( level = "trace" , skip( fx, bx) ) ]
422+ pub fn codegen_get_discr < Bx : BuilderMethods < ' a , ' tcx , Value = V > > (
423+ self ,
424+ fx : & mut FunctionCx < ' a , ' tcx , Bx > ,
425+ bx : & mut Bx ,
426+ cast_to : Ty < ' tcx > ,
427+ ) -> V {
428+ let dl = & bx. tcx ( ) . data_layout ;
429+ let cast_to_layout = bx. cx ( ) . layout_of ( cast_to) ;
430+ let cast_to = bx. cx ( ) . immediate_backend_type ( cast_to_layout) ;
431+
432+ // We check uninhabitedness separately because a type like
433+ // `enum Foo { Bar(i32, !) }` is still reported as `Variants::Single`,
434+ // *not* as `Variants::Empty`.
435+ if self . layout . is_uninhabited ( ) {
436+ return bx. cx ( ) . const_poison ( cast_to) ;
437+ }
438+
439+ let ( tag_scalar, tag_encoding, tag_field) = match self . layout . variants {
440+ Variants :: Empty => unreachable ! ( "we already handled uninhabited types" ) ,
441+ Variants :: Single { index } => {
442+ let discr_val =
443+ if let Some ( discr) = self . layout . ty . discriminant_for_variant ( bx. tcx ( ) , index) {
444+ discr. val
445+ } else {
446+ // This arm is for types which are neither enums nor coroutines,
447+ // and thus for which the only possible "variant" should be the first one.
448+ assert_eq ! ( index, FIRST_VARIANT ) ;
449+ // There's thus no actual discriminant to return, so we return
450+ // what it would have been if this was a single-variant enum.
451+ 0
452+ } ;
453+ return bx. cx ( ) . const_uint_big ( cast_to, discr_val) ;
454+ }
455+ Variants :: Multiple { tag, ref tag_encoding, tag_field, .. } => {
456+ ( tag, tag_encoding, tag_field)
457+ }
458+ } ;
459+
460+ // Read the tag/niche-encoded discriminant from memory.
461+ let tag_op = match self . val {
462+ OperandValue :: ZeroSized => bug ! ( ) ,
463+ OperandValue :: Immediate ( _) | OperandValue :: Pair ( _, _) => {
464+ self . extract_field ( fx, bx, tag_field)
465+ }
466+ OperandValue :: Ref ( place) => {
467+ let tag = place. with_type ( self . layout ) . project_field ( bx, tag_field) ;
468+ bx. load_operand ( tag)
469+ }
470+ } ;
471+ let tag_imm = tag_op. immediate ( ) ;
472+
473+ // Decode the discriminant (specifically if it's niche-encoded).
474+ match * tag_encoding {
475+ TagEncoding :: Direct => {
476+ let signed = match tag_scalar. primitive ( ) {
477+ // We use `i1` for bytes that are always `0` or `1`,
478+ // e.g., `#[repr(i8)] enum E { A, B }`, but we can't
479+ // let LLVM interpret the `i1` as signed, because
480+ // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`.
481+ Primitive :: Int ( _, signed) => !tag_scalar. is_bool ( ) && signed,
482+ _ => false ,
483+ } ;
484+ bx. intcast ( tag_imm, cast_to, signed)
485+ }
486+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
487+ // Cast to an integer so we don't have to treat a pointer as a
488+ // special case.
489+ let ( tag, tag_llty) = match tag_scalar. primitive ( ) {
490+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
491+ Primitive :: Pointer ( _) => {
492+ let t = bx. type_from_integer ( dl. ptr_sized_integer ( ) ) ;
493+ let tag = bx. ptrtoint ( tag_imm, t) ;
494+ ( tag, t)
495+ }
496+ _ => ( tag_imm, bx. cx ( ) . immediate_backend_type ( tag_op. layout ) ) ,
497+ } ;
498+
499+ let relative_max = niche_variants. end ( ) . as_u32 ( ) - niche_variants. start ( ) . as_u32 ( ) ;
500+
501+ // We have a subrange `niche_start..=niche_end` inside `range`.
502+ // If the value of the tag is inside this subrange, it's a
503+ // "niche value", an increment of the discriminant. Otherwise it
504+ // indicates the untagged variant.
505+ // A general algorithm to extract the discriminant from the tag
506+ // is:
507+ // relative_tag = tag - niche_start
508+ // is_niche = relative_tag <= (ule) relative_max
509+ // discr = if is_niche {
510+ // cast(relative_tag) + niche_variants.start()
511+ // } else {
512+ // untagged_variant
513+ // }
514+ // However, we will likely be able to emit simpler code.
515+ let ( is_niche, tagged_discr, delta) = if relative_max == 0 {
516+ // Best case scenario: only one tagged variant. This will
517+ // likely become just a comparison and a jump.
518+ // The algorithm is:
519+ // is_niche = tag == niche_start
520+ // discr = if is_niche {
521+ // niche_start
522+ // } else {
523+ // untagged_variant
524+ // }
525+ let niche_start = bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ;
526+ let is_niche = bx. icmp ( IntPredicate :: IntEQ , tag, niche_start) ;
527+ let tagged_discr =
528+ bx. cx ( ) . const_uint ( cast_to, niche_variants. start ( ) . as_u32 ( ) as u64 ) ;
529+ ( is_niche, tagged_discr, 0 )
530+ } else {
531+ // The special cases don't apply, so we'll have to go with
532+ // the general algorithm.
533+ let relative_discr = bx. sub ( tag, bx. cx ( ) . const_uint_big ( tag_llty, niche_start) ) ;
534+ let cast_tag = bx. intcast ( relative_discr, cast_to, false ) ;
535+ let is_niche = bx. icmp (
536+ IntPredicate :: IntULE ,
537+ relative_discr,
538+ bx. cx ( ) . const_uint ( tag_llty, relative_max as u64 ) ,
539+ ) ;
540+ ( is_niche, cast_tag, niche_variants. start ( ) . as_u32 ( ) as u128 )
541+ } ;
542+
543+ let tagged_discr = if delta == 0 {
544+ tagged_discr
545+ } else {
546+ bx. add ( tagged_discr, bx. cx ( ) . const_uint_big ( cast_to, delta) )
547+ } ;
548+
549+ let discr = bx. select (
550+ is_niche,
551+ tagged_discr,
552+ bx. cx ( ) . const_uint ( cast_to, untagged_variant. as_u32 ( ) as u64 ) ,
553+ ) ;
554+
555+ // In principle we could insert assumes on the possible range of `discr`, but
556+ // currently in LLVM this seems to be a pessimization.
557+
558+ discr
559+ }
560+ }
561+ }
418562}
419563
420564impl < ' a , ' tcx , V : CodegenObject > OperandValue < V > {
0 commit comments