@@ -3,16 +3,17 @@ 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 , Size } ;
6
+ use rustc_abi:: { Align , BackendRepr , FIRST_VARIANT , Primitive , Size , TagEncoding , Variants } ;
7
7
use rustc_middle:: mir:: interpret:: { Pointer , Scalar , alloc_range} ;
8
8
use rustc_middle:: mir:: { self , ConstValue } ;
9
9
use rustc_middle:: ty:: Ty ;
10
10
use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
11
11
use rustc_middle:: { bug, span_bug} ;
12
- use tracing:: debug;
12
+ use tracing:: { debug, instrument } ;
13
13
14
14
use super :: place:: { PlaceRef , PlaceValue } ;
15
15
use super :: { FunctionCx , LocalRef } ;
16
+ use crate :: common:: IntPredicate ;
16
17
use crate :: traits:: * ;
17
18
use crate :: { MemFlags , size_of_val} ;
18
19
@@ -415,6 +416,149 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
415
416
416
417
OperandRef { val, layout : field }
417
418
}
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
+ }
418
562
}
419
563
420
564
impl < ' a , ' tcx , V : CodegenObject > OperandValue < V > {
0 commit comments