@@ -362,7 +362,10 @@ struct VnState<'body, 'a, 'tcx> {
362
362
rev_locals : IndexVec < VnIndex , SmallVec < [ Local ; 1 ] > > ,
363
363
values : ValueSet < ' a , ' tcx > ,
364
364
/// Values evaluated as constants if possible.
365
- evaluated : IndexVec < VnIndex , Option < OpTy < ' tcx > > > ,
365
+ /// - `None` are values not computed yet;
366
+ /// - `Some(None)` are values for which computation has failed;
367
+ /// - `Some(Some(op))` are successful computations.
368
+ evaluated : IndexVec < VnIndex , Option < Option < & ' a OpTy < ' tcx > > > > ,
366
369
/// Cache the deref values.
367
370
derefs : Vec < VnIndex > ,
368
371
ssa : & ' body SsaLocals ,
@@ -414,8 +417,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
414
417
let ( index, new) = self . values . insert ( ty, value) ;
415
418
if new {
416
419
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
417
- let evaluated = self . eval_to_const ( index) ;
418
- let _index = self . evaluated . push ( evaluated) ;
420
+ let _index = self . evaluated . push ( None ) ;
419
421
debug_assert_eq ! ( index, _index) ;
420
422
let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
421
423
debug_assert_eq ! ( index, _index) ;
@@ -428,7 +430,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
428
430
#[ instrument( level = "trace" , skip( self ) , ret) ]
429
431
fn new_opaque ( & mut self , ty : Ty < ' tcx > ) -> VnIndex {
430
432
let index = self . values . insert_unique ( ty, Value :: Opaque ) ;
431
- let _index = self . evaluated . push ( None ) ;
433
+ let _index = self . evaluated . push ( Some ( None ) ) ;
432
434
debug_assert_eq ! ( index, _index) ;
433
435
let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
434
436
debug_assert_eq ! ( index, _index) ;
@@ -466,8 +468,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
466
468
kind,
467
469
provenance,
468
470
} ) ;
469
- let evaluated = self . eval_to_const ( index) ;
470
- let _index = self . evaluated . push ( evaluated) ;
471
+ let _index = self . evaluated . push ( None ) ;
471
472
debug_assert_eq ! ( index, _index) ;
472
473
let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
473
474
debug_assert_eq ! ( index, _index) ;
@@ -491,8 +492,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
491
492
( index, true )
492
493
} ;
493
494
if new {
494
- let evaluated = self . eval_to_const ( index) ;
495
- let _index = self . evaluated . push ( evaluated) ;
495
+ let _index = self . evaluated . push ( None ) ;
496
496
debug_assert_eq ! ( index, _index) ;
497
497
let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
498
498
debug_assert_eq ! ( index, _index) ;
@@ -549,7 +549,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
549
549
}
550
550
551
551
#[ instrument( level = "trace" , skip( self ) , ret) ]
552
- fn eval_to_const ( & mut self , value : VnIndex ) -> Option < OpTy < ' tcx > > {
552
+ fn eval_to_const_inner ( & mut self , value : VnIndex ) -> Option < OpTy < ' tcx > > {
553
553
use Value :: * ;
554
554
let ty = self . ty ( value) ;
555
555
// Avoid computing layouts inside a coroutine, as that can cause cycles.
@@ -569,10 +569,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
569
569
self . ecx . eval_mir_constant ( value, DUMMY_SP , None ) . discard_err ( ) ?
570
570
}
571
571
Aggregate ( variant, ref fields) => {
572
- let fields = fields
573
- . iter ( )
574
- . map ( |& f| self . evaluated [ f] . as_ref ( ) )
575
- . collect :: < Option < Vec < _ > > > ( ) ?;
572
+ let fields =
573
+ fields. iter ( ) . map ( |& f| self . eval_to_const ( f) ) . collect :: < Option < Vec < _ > > > ( ) ?;
576
574
let variant = if ty. ty . is_enum ( ) { Some ( variant) } else { None } ;
577
575
if matches ! ( ty. backend_repr, BackendRepr :: Scalar ( ..) | BackendRepr :: ScalarPair ( ..) )
578
576
{
@@ -601,8 +599,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
601
599
}
602
600
}
603
601
RawPtr { pointer, metadata } => {
604
- let pointer = self . evaluated [ pointer ] . as_ref ( ) ?;
605
- let metadata = self . evaluated [ metadata ] . as_ref ( ) ?;
602
+ let pointer = self . eval_to_const ( pointer ) ?;
603
+ let metadata = self . eval_to_const ( metadata ) ?;
606
604
607
605
// Pointers don't have fields, so don't `project_field` them.
608
606
let data = self . ecx . read_pointer ( pointer) . discard_err ( ) ?;
@@ -616,7 +614,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
616
614
}
617
615
618
616
Projection ( base, elem) => {
619
- let base = self . evaluated [ base ] . as_ref ( ) ?;
617
+ let base = self . eval_to_const ( base ) ?;
620
618
// `Index` by constants should have been replaced by `ConstantIndex` by
621
619
// `simplify_place_projection`.
622
620
let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
@@ -625,7 +623,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
625
623
Address { base, projection, .. } => {
626
624
debug_assert ! ( !projection. contains( & ProjectionElem :: Deref ) ) ;
627
625
let pointer = match base {
628
- AddressBase :: Deref ( pointer) => self . evaluated [ pointer ] . as_ref ( ) ?,
626
+ AddressBase :: Deref ( pointer) => self . eval_to_const ( pointer ) ?,
629
627
// We have no stack to point to.
630
628
AddressBase :: Local ( _) => return None ,
631
629
} ;
@@ -641,7 +639,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
641
639
}
642
640
643
641
Discriminant ( base) => {
644
- let base = self . evaluated [ base ] . as_ref ( ) ?;
642
+ let base = self . eval_to_const ( base ) ?;
645
643
let variant = self . ecx . read_discriminant ( base) . discard_err ( ) ?;
646
644
let discr_value =
647
645
self . ecx . discriminant_for_variant ( base. layout . ty , variant) . discard_err ( ) ?;
@@ -658,7 +656,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
658
656
NullOp :: SizeOf => arg_layout. size . bytes ( ) ,
659
657
NullOp :: AlignOf => arg_layout. align . bytes ( ) ,
660
658
NullOp :: OffsetOf ( fields) => self
661
- . ecx
662
659
. tcx
663
660
. offset_of_subfield ( self . typing_env ( ) , arg_layout, fields. iter ( ) )
664
661
. bytes ( ) ,
@@ -668,34 +665,34 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
668
665
ImmTy :: from_uint ( val, ty) . into ( )
669
666
}
670
667
UnaryOp ( un_op, operand) => {
671
- let operand = self . evaluated [ operand ] . as_ref ( ) ?;
668
+ let operand = self . eval_to_const ( operand ) ?;
672
669
let operand = self . ecx . read_immediate ( operand) . discard_err ( ) ?;
673
670
let val = self . ecx . unary_op ( un_op, & operand) . discard_err ( ) ?;
674
671
val. into ( )
675
672
}
676
673
BinaryOp ( bin_op, lhs, rhs) => {
677
- let lhs = self . evaluated [ lhs] . as_ref ( ) ?;
674
+ let lhs = self . eval_to_const ( lhs) ?;
675
+ let rhs = self . eval_to_const ( rhs) ?;
678
676
let lhs = self . ecx . read_immediate ( lhs) . discard_err ( ) ?;
679
- let rhs = self . evaluated [ rhs] . as_ref ( ) ?;
680
677
let rhs = self . ecx . read_immediate ( rhs) . discard_err ( ) ?;
681
678
let val = self . ecx . binary_op ( bin_op, & lhs, & rhs) . discard_err ( ) ?;
682
679
val. into ( )
683
680
}
684
681
Cast { kind, value } => match kind {
685
682
CastKind :: IntToInt | CastKind :: IntToFloat => {
686
- let value = self . evaluated [ value ] . as_ref ( ) ?;
683
+ let value = self . eval_to_const ( value ) ?;
687
684
let value = self . ecx . read_immediate ( value) . discard_err ( ) ?;
688
685
let res = self . ecx . int_to_int_or_float ( & value, ty) . discard_err ( ) ?;
689
686
res. into ( )
690
687
}
691
688
CastKind :: FloatToFloat | CastKind :: FloatToInt => {
692
- let value = self . evaluated [ value ] . as_ref ( ) ?;
689
+ let value = self . eval_to_const ( value ) ?;
693
690
let value = self . ecx . read_immediate ( value) . discard_err ( ) ?;
694
691
let res = self . ecx . float_to_float_or_int ( & value, ty) . discard_err ( ) ?;
695
692
res. into ( )
696
693
}
697
694
CastKind :: Transmute | CastKind :: Subtype => {
698
- let value = self . evaluated [ value ] . as_ref ( ) ?;
695
+ let value = self . eval_to_const ( value ) ?;
699
696
// `offset` for immediates generally only supports projections that match the
700
697
// type of the immediate. However, as a HACK, we exploit that it can also do
701
698
// limited transmutes: it only works between types with the same layout, and
@@ -707,12 +704,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
707
704
&& !matches ! ( s1. primitive( ) , Primitive :: Pointer ( ..) )
708
705
}
709
706
( BackendRepr :: ScalarPair ( a1, b1) , BackendRepr :: ScalarPair ( a2, b2) ) => {
710
- a1. size ( & self . ecx ) == a2. size ( & self . ecx ) &&
711
- b1. size ( & self . ecx ) == b2. size ( & self . ecx ) &&
712
- // The alignment of the second component determines its offset, so that also needs to match.
713
- b1. align ( & self . ecx ) == b2. align ( & self . ecx ) &&
714
- // None of the inputs may be a pointer.
715
- !matches ! ( a1. primitive( ) , Primitive :: Pointer ( ..) )
707
+ a1. size ( & self . ecx ) == a2. size ( & self . ecx )
708
+ && b1. size ( & self . ecx ) == b2. size ( & self . ecx )
709
+ // The alignment of the second component determines its offset, so that also needs to match.
710
+ && b1. align ( & self . ecx ) == b2. align ( & self . ecx )
711
+ // None of the inputs may be a pointer.
712
+ && !matches ! ( a1. primitive( ) , Primitive :: Pointer ( ..) )
716
713
&& !matches ! ( b1. primitive( ) , Primitive :: Pointer ( ..) )
717
714
}
718
715
_ => false ,
@@ -724,7 +721,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
724
721
value. offset ( Size :: ZERO , ty, & self . ecx ) . discard_err ( ) ?
725
722
}
726
723
CastKind :: PointerCoercion ( ty:: adjustment:: PointerCoercion :: Unsize , _) => {
727
- let src = self . evaluated [ value ] . as_ref ( ) ?;
724
+ let src = self . eval_to_const ( value ) ?;
728
725
let dest = self . ecx . allocate ( ty, MemoryKind :: Stack ) . discard_err ( ) ?;
729
726
self . ecx . unsize_into ( src, ty, & dest) . discard_err ( ) ?;
730
727
self . ecx
@@ -733,13 +730,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
733
730
dest. into ( )
734
731
}
735
732
CastKind :: FnPtrToPtr | CastKind :: PtrToPtr => {
736
- let src = self . evaluated [ value ] . as_ref ( ) ?;
733
+ let src = self . eval_to_const ( value ) ?;
737
734
let src = self . ecx . read_immediate ( src) . discard_err ( ) ?;
738
735
let ret = self . ecx . ptr_to_ptr ( & src, ty) . discard_err ( ) ?;
739
736
ret. into ( )
740
737
}
741
738
CastKind :: PointerCoercion ( ty:: adjustment:: PointerCoercion :: UnsafeFnPointer , _) => {
742
- let src = self . evaluated [ value ] . as_ref ( ) ?;
739
+ let src = self . eval_to_const ( value ) ?;
743
740
let src = self . ecx . read_immediate ( src) . discard_err ( ) ?;
744
741
ImmTy :: from_immediate ( * src, ty) . into ( )
745
742
}
@@ -749,6 +746,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
749
746
Some ( op)
750
747
}
751
748
749
+ fn eval_to_const ( & mut self , index : VnIndex ) -> Option < & ' a OpTy < ' tcx > > {
750
+ if let Some ( op) = self . evaluated [ index] {
751
+ return op;
752
+ }
753
+ let op = self . eval_to_const_inner ( index) ;
754
+ self . evaluated [ index] = Some ( self . arena . alloc ( op) . as_ref ( ) ) ;
755
+ self . evaluated [ index] . unwrap ( )
756
+ }
757
+
752
758
/// Represent the *value* we obtain by dereferencing an `Address` value.
753
759
#[ instrument( level = "trace" , skip( self ) , ret) ]
754
760
fn dereference_address (
@@ -884,7 +890,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
884
890
if let ProjectionElem :: Index ( idx_local) = elem
885
891
&& let Some ( idx) = self . locals [ idx_local]
886
892
{
887
- if let Some ( offset) = self . evaluated [ idx ] . as_ref ( )
893
+ if let Some ( offset) = self . eval_to_const ( idx )
888
894
&& let Some ( offset) = self . ecx . read_target_usize ( offset) . discard_err ( )
889
895
&& let Some ( min_length) = offset. checked_add ( 1 )
890
896
{
@@ -1369,8 +1375,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1369
1375
1370
1376
let layout = self . ecx . layout_of ( lhs_ty) . ok ( ) ?;
1371
1377
1372
- let as_bits = |value : VnIndex | {
1373
- let constant = self . evaluated [ value ] . as_ref ( ) ?;
1378
+ let mut as_bits = |value : VnIndex | {
1379
+ let constant = self . eval_to_const ( value ) ?;
1374
1380
if layout. backend_repr . is_scalar ( ) {
1375
1381
let scalar = self . ecx . read_scalar ( constant) . discard_err ( ) ?;
1376
1382
scalar. to_bits ( constant. layout . size ) . discard_err ( )
@@ -1770,7 +1776,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
1770
1776
return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
1771
1777
}
1772
1778
1773
- let op = self . evaluated [ index ] . as_ref ( ) ?;
1779
+ let op = self . eval_to_const ( index ) ?;
1774
1780
if op. layout . is_unsized ( ) {
1775
1781
// Do not attempt to propagate unsized locals.
1776
1782
return None ;
0 commit comments