3
3
//! Currently, this pass only propagates scalar values.
4
4
5
5
use rustc_const_eval:: const_eval:: CheckAlignment ;
6
- use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , Projectable } ;
6
+ use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , PlaceTy , Projectable } ;
7
7
use rustc_data_structures:: fx:: FxHashMap ;
8
8
use rustc_hir:: def:: DefKind ;
9
- use rustc_middle:: mir:: interpret:: { AllocId , ConstAllocation , ConstValue , InterpResult , Scalar } ;
9
+ use rustc_middle:: mir:: interpret:: {
10
+ AllocId , ConstAllocation , ConstValue , GlobalAlloc , InterpResult , Scalar ,
11
+ } ;
10
12
use rustc_middle:: mir:: visit:: { MutVisitor , PlaceContext , Visitor } ;
11
13
use rustc_middle:: mir:: * ;
12
- use rustc_middle:: ty:: layout:: TyAndLayout ;
14
+ use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
13
15
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
14
16
use rustc_mir_dataflow:: value_analysis:: {
15
17
Map , PlaceIndex , State , TrackElem , ValueAnalysis , ValueAnalysisWrapper , ValueOrPlace ,
16
18
} ;
17
19
use rustc_mir_dataflow:: { lattice:: FlatSet , Analysis , Results , ResultsVisitor } ;
18
20
use rustc_span:: def_id:: DefId ;
19
21
use rustc_span:: DUMMY_SP ;
20
- use rustc_target:: abi:: { Align , FieldIdx , VariantIdx } ;
22
+ use rustc_target:: abi:: { Align , FieldIdx , Size , VariantIdx , FIRST_VARIANT } ;
21
23
22
24
use crate :: MirPass ;
23
25
@@ -546,110 +548,130 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> {
546
548
547
549
fn try_make_constant (
548
550
& self ,
551
+ ecx : & mut InterpCx < ' tcx , ' tcx , DummyMachine > ,
549
552
place : Place < ' tcx > ,
550
553
state : & State < FlatSet < Scalar > > ,
551
554
map : & Map ,
552
555
) -> Option < ConstantKind < ' tcx > > {
553
556
let ty = place. ty ( self . local_decls , self . patch . tcx ) . ty ;
554
557
let place = map. find ( place. as_ref ( ) ) ?;
555
- if let FlatSet :: Elem ( Scalar :: Int ( value) ) = state. get_idx ( place, map) {
556
- Some ( ConstantKind :: Val ( ConstValue :: Scalar ( value. into ( ) ) , ty) )
558
+ let layout = ecx. layout_of ( ty) . ok ( ) ?;
559
+ if layout. abi . is_scalar ( ) {
560
+ let value = propagatable_scalar ( * ecx. tcx , place, state, map) ?;
561
+ Some ( ConstantKind :: Val ( ConstValue :: Scalar ( value) , ty) )
557
562
} else {
558
- let valtree = self . try_make_valtree ( place, ty, state, map) ?;
559
- let constant = ty:: Const :: new_value ( self . patch . tcx , valtree, ty) ;
560
- Some ( ConstantKind :: Ty ( constant) )
563
+ let alloc_id = ecx
564
+ . intern_with_temp_alloc ( layout, |ecx, dest| {
565
+ try_write_constant ( ecx, dest, place, ty, state, map)
566
+ } )
567
+ . ok ( ) ?;
568
+ Some ( ConstantKind :: Val ( ConstValue :: Indirect { alloc_id, offset : Size :: ZERO } , ty) )
561
569
}
562
570
}
571
+ }
563
572
564
- fn try_make_valtree (
565
- & self ,
566
- place : PlaceIndex ,
567
- ty : Ty < ' tcx > ,
568
- state : & State < FlatSet < Scalar > > ,
569
- map : & Map ,
570
- ) -> Option < ty:: ValTree < ' tcx > > {
571
- let tcx = self . patch . tcx ;
572
- match ty. kind ( ) {
573
- // ZSTs.
574
- ty:: FnDef ( ..) => Some ( ty:: ValTree :: zst ( ) ) ,
575
-
576
- // Scalars.
577
- ty:: Bool | ty:: Int ( _) | ty:: Uint ( _) | ty:: Float ( _) | ty:: Char => {
578
- if let FlatSet :: Elem ( Scalar :: Int ( value) ) = state. get_idx ( place, map) {
579
- Some ( ty:: ValTree :: Leaf ( value) )
580
- } else {
581
- None
582
- }
573
+ fn propagatable_scalar < ' tcx > (
574
+ tcx : TyCtxt < ' tcx > ,
575
+ place : PlaceIndex ,
576
+ state : & State < FlatSet < Scalar > > ,
577
+ map : & Map ,
578
+ ) -> Option < Scalar > {
579
+ if let FlatSet :: Elem ( value) = state. get_idx ( place, map) {
580
+ if let Scalar :: Ptr ( pointer, _) = value {
581
+ let ( alloc_id, _) = pointer. into_parts ( ) ;
582
+ match tcx. global_alloc ( alloc_id) {
583
+ // Do not propagate pointers to functions and vtables as they may
584
+ // lose identify during codegen, which is a miscompilation.
585
+ GlobalAlloc :: Function ( _) | GlobalAlloc :: VTable ( ..) => return None ,
586
+ GlobalAlloc :: Memory ( _) | GlobalAlloc :: Static ( _) => { }
583
587
}
588
+ }
589
+ Some ( value)
590
+ } else {
591
+ None
592
+ }
593
+ }
584
594
585
- // Unsupported for now.
586
- ty:: Array ( _, _) => None ,
587
-
588
- ty:: Tuple ( elem_tys) => {
589
- let branches = elem_tys
590
- . iter ( )
591
- . enumerate ( )
592
- . map ( |( i, ty) | {
593
- let field = map. apply ( place, TrackElem :: Field ( FieldIdx :: from_usize ( i) ) ) ?;
594
- self . try_make_valtree ( field, ty, state, map)
595
- } )
596
- . collect :: < Option < Vec < _ > > > ( ) ?;
597
- Some ( ty:: ValTree :: Branch ( tcx. arena . alloc_from_iter ( branches. into_iter ( ) ) ) )
595
+ #[ instrument( level = "trace" , skip( ecx, state, map) ) ]
596
+ fn try_write_constant < ' tcx > (
597
+ ecx : & mut InterpCx < ' _ , ' tcx , DummyMachine > ,
598
+ dest : & PlaceTy < ' tcx > ,
599
+ place : PlaceIndex ,
600
+ ty : Ty < ' tcx > ,
601
+ state : & State < FlatSet < Scalar > > ,
602
+ map : & Map ,
603
+ ) -> InterpResult < ' tcx > {
604
+ let layout = ecx. layout_of ( ty) ?;
605
+ match ty. kind ( ) {
606
+ // ZSTs. Nothing to do.
607
+ ty:: FnDef ( ..) => { }
608
+
609
+ // Scalars.
610
+ _ if layout. abi . is_scalar ( ) => {
611
+ let value = propagatable_scalar ( * ecx. tcx , place, state, map) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
612
+ ecx. write_immediate ( Immediate :: Scalar ( value) , dest) ?;
613
+ }
614
+ // Those are scalars, must be handled above.
615
+ ty:: Bool | ty:: Int ( _) | ty:: Uint ( _) | ty:: Float ( _) | ty:: Char => bug ! ( ) ,
616
+
617
+ ty:: Tuple ( elem_tys) => {
618
+ for ( i, elem) in elem_tys. iter ( ) . enumerate ( ) {
619
+ let field = map. apply ( place, TrackElem :: Field ( FieldIdx :: from_usize ( i) ) ) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
620
+ let field_dest = ecx. project_field ( dest, i) ?;
621
+ try_write_constant ( ecx, & field_dest, field, elem, state, map) ?;
598
622
}
623
+ }
599
624
600
- ty:: Adt ( def, args) => {
601
- if def. is_union ( ) {
602
- return None ;
603
- }
625
+ ty:: Adt ( def, args) => {
626
+ if def. is_union ( ) {
627
+ throw_inval ! ( ConstPropNonsense )
628
+ }
604
629
605
- let ( variant_idx, variant_def, variant_place) = if def. is_enum ( ) {
606
- let discr = map. apply ( place, TrackElem :: Discriminant ) ?;
607
- let FlatSet :: Elem ( Scalar :: Int ( discr) ) = state. get_idx ( discr, map) else {
608
- return None ;
609
- } ;
610
- let discr_bits = discr. assert_bits ( discr. size ( ) ) ;
611
- let ( variant, _) =
612
- def. discriminants ( tcx) . find ( |( _, var) | discr_bits == var. val ) ?;
613
- let variant_place = map. apply ( place, TrackElem :: Variant ( variant) ) ?;
614
- let variant_int = ty:: ValTree :: Leaf ( variant. as_u32 ( ) . into ( ) ) ;
615
- ( Some ( variant_int) , def. variant ( variant) , variant_place)
616
- } else {
617
- ( None , def. non_enum_variant ( ) , place)
630
+ let ( variant_idx, variant_def, variant_place, variant_dest) = if def. is_enum ( ) {
631
+ let discr = map. apply ( place, TrackElem :: Discriminant ) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
632
+ let FlatSet :: Elem ( Scalar :: Int ( discr) ) = state. get_idx ( discr, map) else {
633
+ throw_inval ! ( ConstPropNonsense )
618
634
} ;
619
-
620
- let branches = variant_def
621
- . fields
622
- . iter_enumerated ( )
623
- . map ( |( i, field) | {
624
- let ty = field. ty ( tcx, args) ;
625
- let field = map. apply ( variant_place, TrackElem :: Field ( i) ) ?;
626
- self . try_make_valtree ( field, ty, state, map)
627
- } )
628
- . collect :: < Option < Vec < _ > > > ( ) ?;
629
- Some ( ty:: ValTree :: Branch (
630
- tcx. arena . alloc_from_iter ( variant_idx. into_iter ( ) . chain ( branches) ) ,
631
- ) )
635
+ let discr_bits = discr. assert_bits ( discr. size ( ) ) ;
636
+ let ( variant, _) = def. discriminants ( * ecx. tcx ) . find ( |( _, var) | discr_bits == var. val ) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
637
+ let variant_place = map. apply ( place, TrackElem :: Variant ( variant) ) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
638
+ let variant_dest = ecx. project_downcast ( dest, variant) ?;
639
+ ( variant, def. variant ( variant) , variant_place, variant_dest)
640
+ } else {
641
+ ( FIRST_VARIANT , def. non_enum_variant ( ) , place, dest. clone ( ) )
642
+ } ;
643
+
644
+ for ( i, field) in variant_def. fields . iter_enumerated ( ) {
645
+ let ty = field. ty ( * ecx. tcx , args) ;
646
+ let field = map. apply ( variant_place, TrackElem :: Field ( i) ) . ok_or ( err_inval ! ( ConstPropNonsense ) ) ?;
647
+ let field_dest = ecx. project_field ( & variant_dest, i. as_usize ( ) ) ?;
648
+ try_write_constant ( ecx, & field_dest, field, ty, state, map) ?;
632
649
}
650
+ ecx. write_discriminant ( variant_idx, dest) ?;
651
+ }
652
+
653
+ // Unsupported for now.
654
+ ty:: Array ( _, _)
633
655
634
- // Do not attempt to support indirection in constants.
635
- ty:: Ref ( ..) | ty:: RawPtr ( ..) | ty:: FnPtr ( ..) | ty:: Str | ty:: Slice ( _) => None ,
636
-
637
- ty:: Never
638
- | ty:: Foreign ( ..)
639
- | ty:: Alias ( ..)
640
- | ty:: Param ( _)
641
- | ty:: Bound ( ..)
642
- | ty:: Placeholder ( ..)
643
- | ty:: Closure ( ..)
644
- | ty:: Generator ( ..)
645
- | ty:: Dynamic ( ..) => None ,
646
-
647
- ty:: Error ( _)
648
- | ty:: Infer ( ..)
649
- | ty:: GeneratorWitness ( ..)
650
- | ty:: GeneratorWitnessMIR ( ..) => bug ! ( ) ,
656
+ // Do not attempt to support indirection in constants.
657
+ | ty:: Ref ( ..) | ty:: RawPtr ( ..) | ty:: FnPtr ( ..) | ty:: Str | ty:: Slice ( _)
658
+
659
+ | ty:: Never
660
+ | ty:: Foreign ( ..)
661
+ | ty:: Alias ( ..)
662
+ | ty:: Param ( _)
663
+ | ty:: Bound ( ..)
664
+ | ty:: Placeholder ( ..)
665
+ | ty:: Closure ( ..)
666
+ | ty:: Generator ( ..)
667
+ | ty:: Dynamic ( ..) => throw_inval ! ( ConstPropNonsense ) ,
668
+
669
+ ty:: Error ( _) | ty:: Infer ( ..) | ty:: GeneratorWitness ( ..) | ty:: GeneratorWitnessMIR ( ..) => {
670
+ bug ! ( )
651
671
}
652
672
}
673
+
674
+ Ok ( ( ) )
653
675
}
654
676
655
677
impl < ' mir , ' tcx >
@@ -667,8 +689,13 @@ impl<'mir, 'tcx>
667
689
) {
668
690
match & statement. kind {
669
691
StatementKind :: Assign ( box ( _, rvalue) ) => {
670
- OperandCollector { state, visitor : self , map : & results. analysis . 0 . map }
671
- . visit_rvalue ( rvalue, location) ;
692
+ OperandCollector {
693
+ state,
694
+ visitor : self ,
695
+ ecx : & mut results. analysis . 0 . ecx ,
696
+ map : & results. analysis . 0 . map ,
697
+ }
698
+ . visit_rvalue ( rvalue, location) ;
672
699
}
673
700
_ => ( ) ,
674
701
}
@@ -686,7 +713,12 @@ impl<'mir, 'tcx>
686
713
// Don't overwrite the assignment if it already uses a constant (to keep the span).
687
714
}
688
715
StatementKind :: Assign ( box ( place, _) ) => {
689
- if let Some ( value) = self . try_make_constant ( place, state, & results. analysis . 0 . map ) {
716
+ if let Some ( value) = self . try_make_constant (
717
+ & mut results. analysis . 0 . ecx ,
718
+ place,
719
+ state,
720
+ & results. analysis . 0 . map ,
721
+ ) {
690
722
self . patch . assignments . insert ( location, value) ;
691
723
}
692
724
}
@@ -701,8 +733,13 @@ impl<'mir, 'tcx>
701
733
terminator : & ' mir Terminator < ' tcx > ,
702
734
location : Location ,
703
735
) {
704
- OperandCollector { state, visitor : self , map : & results. analysis . 0 . map }
705
- . visit_terminator ( terminator, location) ;
736
+ OperandCollector {
737
+ state,
738
+ visitor : self ,
739
+ ecx : & mut results. analysis . 0 . ecx ,
740
+ map : & results. analysis . 0 . map ,
741
+ }
742
+ . visit_terminator ( terminator, location) ;
706
743
}
707
744
}
708
745
@@ -757,6 +794,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> {
757
794
struct OperandCollector < ' tcx , ' map , ' locals , ' a > {
758
795
state : & ' a State < FlatSet < Scalar > > ,
759
796
visitor : & ' a mut Collector < ' tcx , ' locals > ,
797
+ ecx : & ' map mut InterpCx < ' tcx , ' tcx , DummyMachine > ,
760
798
map : & ' map Map ,
761
799
}
762
800
@@ -769,15 +807,17 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
769
807
location : Location ,
770
808
) {
771
809
if let PlaceElem :: Index ( local) = elem
772
- && let Some ( value) = self . visitor . try_make_constant ( local. into ( ) , self . state , self . map )
810
+ && let Some ( value) = self . visitor . try_make_constant ( self . ecx , local. into ( ) , self . state , self . map )
773
811
{
774
812
self . visitor . patch . before_effect . insert ( ( location, local. into ( ) ) , value) ;
775
813
}
776
814
}
777
815
778
816
fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
779
817
if let Some ( place) = operand. place ( ) {
780
- if let Some ( value) = self . visitor . try_make_constant ( place, self . state , self . map ) {
818
+ if let Some ( value) =
819
+ self . visitor . try_make_constant ( self . ecx , place, self . state , self . map )
820
+ {
781
821
self . visitor . patch . before_effect . insert ( ( location, place) , value) ;
782
822
} else if !place. projection . is_empty ( ) {
783
823
// Try to propagate into `Index` projections.
@@ -802,8 +842,9 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
802
842
}
803
843
804
844
fn enforce_validity ( _ecx : & InterpCx < ' mir , ' tcx , Self > , _layout : TyAndLayout < ' tcx > ) -> bool {
805
- unimplemented ! ( )
845
+ false
806
846
}
847
+
807
848
fn alignment_check_failed (
808
849
_ecx : & InterpCx < ' mir , ' tcx , Self > ,
809
850
_has : Align ,
0 commit comments