@@ -9,9 +9,7 @@ use rustc_hir::def::DefKind;
9
9
use rustc_hir:: HirId ;
10
10
use rustc_index:: bit_set:: BitSet ;
11
11
use rustc_index:: vec:: IndexVec ;
12
- use rustc_middle:: mir:: visit:: {
13
- MutVisitor , MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor ,
14
- } ;
12
+ use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
15
13
use rustc_middle:: mir:: {
16
14
AssertKind , BasicBlock , BinOp , Body , Constant , ConstantKind , Local , LocalDecl , LocalKind ,
17
15
Location , Operand , Place , Rvalue , SourceInfo , SourceScope , SourceScopeData , Statement ,
@@ -28,12 +26,12 @@ use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
28
26
use rustc_target:: spec:: abi:: Abi ;
29
27
use rustc_trait_selection:: traits;
30
28
31
- use crate :: MirPass ;
29
+ use crate :: MirLint ;
32
30
use rustc_const_eval:: const_eval:: ConstEvalErr ;
33
31
use rustc_const_eval:: interpret:: {
34
- self , compile_time_machine, AllocId , ConstAllocation , ConstValue , CtfeValidationMode , Frame ,
35
- ImmTy , Immediate , InterpCx , InterpResult , LocalState , LocalValue , MemPlace , MemoryKind , OpTy ,
36
- Operand as InterpOperand , PlaceTy , Scalar , ScalarMaybeUninit , StackPopCleanup , StackPopUnwind ,
32
+ self , compile_time_machine, AllocId , ConstAllocation , Frame , ImmTy , InterpCx , InterpResult ,
33
+ LocalState , LocalValue , MemPlace , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy , Scalar ,
34
+ ScalarMaybeUninit , StackPopCleanup , StackPopUnwind ,
37
35
} ;
38
36
39
37
/// The maximum number of bytes that we'll allocate space for a local or the return value.
@@ -61,15 +59,8 @@ macro_rules! throw_machine_stop_str {
61
59
62
60
pub struct ConstProp ;
63
61
64
- impl < ' tcx > MirPass < ' tcx > for ConstProp {
65
- fn is_enabled ( & self , _sess : & rustc_session:: Session ) -> bool {
66
- // FIXME(#70073): Unlike the other passes in "optimizations", this one emits errors, so it
67
- // runs even when MIR optimizations are disabled. We should separate the lint out from the
68
- // transform and move the lint as early in the pipeline as possible.
69
- true
70
- }
71
-
72
- fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
62
+ impl < ' tcx > MirLint < ' tcx > for ConstProp {
63
+ fn run_lint ( & self , tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) {
73
64
// will be evaluated by miri and produce its errors there
74
65
if body. source . promoted . is_some ( ) {
75
66
return ;
@@ -630,32 +621,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
630
621
Some ( ( ) )
631
622
}
632
623
633
- fn propagate_operand ( & mut self , operand : & mut Operand < ' tcx > ) {
634
- match * operand {
635
- Operand :: Copy ( l) | Operand :: Move ( l) => {
636
- if let Some ( value) = self . get_const ( l) && self . should_const_prop ( & value) {
637
- // FIXME(felix91gr): this code only handles `Scalar` cases.
638
- // For now, we're not handling `ScalarPair` cases because
639
- // doing so here would require a lot of code duplication.
640
- // We should hopefully generalize `Operand` handling into a fn,
641
- // and use it to do const-prop here and everywhere else
642
- // where it makes sense.
643
- if let interpret:: Operand :: Immediate ( interpret:: Immediate :: Scalar (
644
- ScalarMaybeUninit :: Scalar ( scalar) ,
645
- ) ) = * value
646
- {
647
- * operand = self . operand_from_scalar (
648
- scalar,
649
- value. layout . ty ,
650
- self . source_info . unwrap ( ) . span ,
651
- ) ;
652
- }
653
- }
654
- }
655
- Operand :: Constant ( _) => ( ) ,
656
- }
657
- }
658
-
659
624
fn const_prop (
660
625
& mut self ,
661
626
rvalue : & Rvalue < ' tcx > ,
@@ -728,191 +693,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
728
693
return None ;
729
694
}
730
695
731
- if self . tcx . sess . mir_opt_level ( ) >= 4 {
732
- self . eval_rvalue_with_identities ( rvalue, place)
733
- } else {
734
- self . use_ecx ( |this| this. ecx . eval_rvalue_into_place ( rvalue, place) )
735
- }
736
- }
737
-
738
- // Attempt to use albegraic identities to eliminate constant expressions
739
- fn eval_rvalue_with_identities (
740
- & mut self ,
741
- rvalue : & Rvalue < ' tcx > ,
742
- place : Place < ' tcx > ,
743
- ) -> Option < ( ) > {
744
- self . use_ecx ( |this| match rvalue {
745
- Rvalue :: BinaryOp ( op, box ( left, right) )
746
- | Rvalue :: CheckedBinaryOp ( op, box ( left, right) ) => {
747
- let l = this. ecx . eval_operand ( left, None ) ;
748
- let r = this. ecx . eval_operand ( right, None ) ;
749
-
750
- let const_arg = match ( l, r) {
751
- ( Ok ( ref x) , Err ( _) ) | ( Err ( _) , Ok ( ref x) ) => this. ecx . read_immediate ( x) ?,
752
- ( Err ( e) , Err ( _) ) => return Err ( e) ,
753
- ( Ok ( _) , Ok ( _) ) => return this. ecx . eval_rvalue_into_place ( rvalue, place) ,
754
- } ;
755
-
756
- let arg_value = const_arg. to_scalar ( ) ?. to_bits ( const_arg. layout . size ) ?;
757
- let dest = this. ecx . eval_place ( place) ?;
758
-
759
- match op {
760
- BinOp :: BitAnd if arg_value == 0 => this. ecx . write_immediate ( * const_arg, & dest) ,
761
- BinOp :: BitOr
762
- if arg_value == const_arg. layout . size . truncate ( u128:: MAX )
763
- || ( const_arg. layout . ty . is_bool ( ) && arg_value == 1 ) =>
764
- {
765
- this. ecx . write_immediate ( * const_arg, & dest)
766
- }
767
- BinOp :: Mul if const_arg. layout . ty . is_integral ( ) && arg_value == 0 => {
768
- if let Rvalue :: CheckedBinaryOp ( _, _) = rvalue {
769
- let val = Immediate :: ScalarPair (
770
- const_arg. to_scalar ( ) ?. into ( ) ,
771
- Scalar :: from_bool ( false ) . into ( ) ,
772
- ) ;
773
- this. ecx . write_immediate ( val, & dest)
774
- } else {
775
- this. ecx . write_immediate ( * const_arg, & dest)
776
- }
777
- }
778
- _ => this. ecx . eval_rvalue_into_place ( rvalue, place) ,
779
- }
780
- }
781
- _ => this. ecx . eval_rvalue_into_place ( rvalue, place) ,
782
- } )
783
- }
784
-
785
- /// Creates a new `Operand::Constant` from a `Scalar` value
786
- fn operand_from_scalar ( & self , scalar : Scalar , ty : Ty < ' tcx > , span : Span ) -> Operand < ' tcx > {
787
- Operand :: Constant ( Box :: new ( Constant {
788
- span,
789
- user_ty : None ,
790
- literal : ty:: Const :: from_scalar ( self . tcx , scalar, ty) . into ( ) ,
791
- } ) )
792
- }
793
-
794
- fn replace_with_const (
795
- & mut self ,
796
- rval : & mut Rvalue < ' tcx > ,
797
- value : & OpTy < ' tcx > ,
798
- source_info : SourceInfo ,
799
- ) {
800
- if let Rvalue :: Use ( Operand :: Constant ( c) ) = rval {
801
- match c. literal {
802
- ConstantKind :: Ty ( c) if matches ! ( c. val( ) , ConstKind :: Unevaluated ( ..) ) => { }
803
- _ => {
804
- trace ! ( "skipping replace of Rvalue::Use({:?} because it is already a const" , c) ;
805
- return ;
806
- }
807
- }
808
- }
809
-
810
- trace ! ( "attempting to replace {:?} with {:?}" , rval, value) ;
811
- if let Err ( e) = self . ecx . const_validate_operand (
812
- value,
813
- vec ! [ ] ,
814
- // FIXME: is ref tracking too expensive?
815
- // FIXME: what is the point of ref tracking if we do not even check the tracked refs?
816
- & mut interpret:: RefTracking :: empty ( ) ,
817
- CtfeValidationMode :: Regular ,
818
- ) {
819
- trace ! ( "validation error, attempt failed: {:?}" , e) ;
820
- return ;
821
- }
822
-
823
- // FIXME> figure out what to do when try_read_immediate fails
824
- let imm = self . use_ecx ( |this| this. ecx . try_read_immediate ( value) ) ;
825
-
826
- if let Some ( Ok ( imm) ) = imm {
827
- match * imm {
828
- interpret:: Immediate :: Scalar ( ScalarMaybeUninit :: Scalar ( scalar) ) => {
829
- * rval = Rvalue :: Use ( self . operand_from_scalar (
830
- scalar,
831
- value. layout . ty ,
832
- source_info. span ,
833
- ) ) ;
834
- }
835
- Immediate :: ScalarPair (
836
- ScalarMaybeUninit :: Scalar ( _) ,
837
- ScalarMaybeUninit :: Scalar ( _) ,
838
- ) => {
839
- // Found a value represented as a pair. For now only do const-prop if the type
840
- // of `rvalue` is also a tuple with two scalars.
841
- // FIXME: enable the general case stated above ^.
842
- let ty = value. layout . ty ;
843
- // Only do it for tuples
844
- if let ty:: Tuple ( types) = ty. kind ( ) {
845
- // Only do it if tuple is also a pair with two scalars
846
- if let [ ty1, ty2] = types[ ..] {
847
- let alloc = self . use_ecx ( |this| {
848
- let ty_is_scalar = |ty| {
849
- this. ecx . layout_of ( ty) . ok ( ) . map ( |layout| layout. abi . is_scalar ( ) )
850
- == Some ( true )
851
- } ;
852
- if ty_is_scalar ( ty1) && ty_is_scalar ( ty2) {
853
- let alloc = this
854
- . ecx
855
- . intern_with_temp_alloc ( value. layout , |ecx, dest| {
856
- ecx. write_immediate ( * imm, dest)
857
- } )
858
- . unwrap ( ) ;
859
- Ok ( Some ( alloc) )
860
- } else {
861
- Ok ( None )
862
- }
863
- } ) ;
864
-
865
- if let Some ( Some ( alloc) ) = alloc {
866
- // Assign entire constant in a single statement.
867
- // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`.
868
- * rval = Rvalue :: Use ( Operand :: Constant ( Box :: new ( Constant {
869
- span : source_info. span ,
870
- user_ty : None ,
871
- literal : self
872
- . ecx
873
- . tcx
874
- . mk_const ( ty:: ConstS {
875
- ty,
876
- val : ty:: ConstKind :: Value ( ConstValue :: ByRef {
877
- alloc,
878
- offset : Size :: ZERO ,
879
- } ) ,
880
- } )
881
- . into ( ) ,
882
- } ) ) ) ;
883
- }
884
- }
885
- }
886
- }
887
- // Scalars or scalar pairs that contain undef values are assumed to not have
888
- // successfully evaluated and are thus not propagated.
889
- _ => { }
890
- }
891
- }
892
- }
893
-
894
- /// Returns `true` if and only if this `op` should be const-propagated into.
895
- fn should_const_prop ( & mut self , op : & OpTy < ' tcx > ) -> bool {
896
- let mir_opt_level = self . tcx . sess . mir_opt_level ( ) ;
897
-
898
- if mir_opt_level == 0 {
899
- return false ;
900
- }
901
-
902
- if !self . tcx . consider_optimizing ( || format ! ( "ConstantPropagation - OpTy: {:?}" , op) ) {
903
- return false ;
904
- }
905
-
906
- match * * op {
907
- interpret:: Operand :: Immediate ( Immediate :: Scalar ( ScalarMaybeUninit :: Scalar ( s) ) ) => {
908
- s. try_to_int ( ) . is_ok ( )
909
- }
910
- interpret:: Operand :: Immediate ( Immediate :: ScalarPair (
911
- ScalarMaybeUninit :: Scalar ( l) ,
912
- ScalarMaybeUninit :: Scalar ( r) ,
913
- ) ) => l. try_to_int ( ) . is_ok ( ) && r. try_to_int ( ) . is_ok ( ) ,
914
- _ => false ,
915
- }
696
+ self . use_ecx ( |this| this. ecx . eval_rvalue_into_place ( rvalue, place) )
916
697
}
917
698
}
918
699
@@ -1047,52 +828,30 @@ impl Visitor<'_> for CanConstProp {
1047
828
}
1048
829
}
1049
830
1050
- impl < ' tcx > MutVisitor < ' tcx > for ConstPropagator < ' _ , ' tcx > {
1051
- fn tcx ( & self ) -> TyCtxt < ' tcx > {
1052
- self . tcx
1053
- }
1054
-
1055
- fn visit_body ( & mut self , body : & mut Body < ' tcx > ) {
1056
- for ( bb, data) in body. basic_blocks_mut ( ) . iter_enumerated_mut ( ) {
831
+ impl < ' tcx > Visitor < ' tcx > for ConstPropagator < ' _ , ' tcx > {
832
+ fn visit_body ( & mut self , body : & Body < ' tcx > ) {
833
+ for ( bb, data) in body. basic_blocks ( ) . iter_enumerated ( ) {
1057
834
self . visit_basic_block_data ( bb, data) ;
1058
835
}
1059
836
}
1060
837
1061
- fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
838
+ fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
1062
839
self . super_operand ( operand, location) ;
1063
-
1064
- // Only const prop copies and moves on `mir_opt_level=3` as doing so
1065
- // currently slightly increases compile time in some cases.
1066
- if self . tcx . sess . mir_opt_level ( ) >= 3 {
1067
- self . propagate_operand ( operand)
1068
- }
1069
840
}
1070
841
1071
- fn visit_constant ( & mut self , constant : & mut Constant < ' tcx > , location : Location ) {
842
+ fn visit_constant ( & mut self , constant : & Constant < ' tcx > , location : Location ) {
1072
843
trace ! ( "visit_constant: {:?}" , constant) ;
1073
844
self . super_constant ( constant, location) ;
1074
845
self . eval_constant ( constant, self . source_info . unwrap ( ) ) ;
1075
846
}
1076
847
1077
- fn visit_statement ( & mut self , statement : & mut Statement < ' tcx > , location : Location ) {
848
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
1078
849
trace ! ( "visit_statement: {:?}" , statement) ;
1079
850
let source_info = statement. source_info ;
1080
851
self . source_info = Some ( source_info) ;
1081
- if let StatementKind :: Assign ( box ( place, ref mut rval) ) = statement. kind {
852
+ if let StatementKind :: Assign ( box ( place, ref rval) ) = statement. kind {
1082
853
let can_const_prop = self . ecx . machine . can_const_prop [ place. local ] ;
1083
854
if let Some ( ( ) ) = self . const_prop ( rval, source_info, place) {
1084
- // This will return None if the above `const_prop` invocation only "wrote" a
1085
- // type whose creation requires no write. E.g. a generator whose initial state
1086
- // consists solely of uninitialized memory (so it doesn't capture any locals).
1087
- if let Some ( ref value) = self . get_const ( place) && self . should_const_prop ( value) {
1088
- trace ! ( "replacing {:?} with {:?}" , rval, value) ;
1089
- self . replace_with_const ( rval, value, source_info) ;
1090
- if can_const_prop == ConstPropMode :: FullConstProp
1091
- || can_const_prop == ConstPropMode :: OnlyInsideOwnBlock
1092
- {
1093
- trace ! ( "propagated into {:?}" , place) ;
1094
- }
1095
- }
1096
855
match can_const_prop {
1097
856
ConstPropMode :: OnlyInsideOwnBlock => {
1098
857
trace ! (
@@ -1159,12 +918,12 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
1159
918
self . super_statement ( statement, location) ;
1160
919
}
1161
920
1162
- fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
921
+ fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
1163
922
let source_info = terminator. source_info ;
1164
923
self . source_info = Some ( source_info) ;
1165
924
self . super_terminator ( terminator, location) ;
1166
- match & mut terminator. kind {
1167
- TerminatorKind :: Assert { expected, ref msg, ref mut cond, .. } => {
925
+ match & terminator. kind {
926
+ TerminatorKind :: Assert { expected, ref msg, ref cond, .. } => {
1168
927
if let Some ( ref value) = self . eval_operand ( & cond, source_info) {
1169
928
trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
1170
929
let expected = ScalarMaybeUninit :: from ( Scalar :: from_bool ( * expected) ) ;
@@ -1231,25 +990,9 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
1231
990
msg,
1232
991
) ;
1233
992
}
1234
- } else {
1235
- if self . should_const_prop ( value) {
1236
- if let ScalarMaybeUninit :: Scalar ( scalar) = value_const {
1237
- * cond = self . operand_from_scalar (
1238
- scalar,
1239
- self . tcx . types . bool ,
1240
- source_info. span ,
1241
- ) ;
1242
- }
1243
- }
1244
993
}
1245
994
}
1246
995
}
1247
- TerminatorKind :: SwitchInt { ref mut discr, .. } => {
1248
- // FIXME: This is currently redundant with `visit_operand`, but sadly
1249
- // always visiting operands currently causes a perf regression in LLVM codegen, so
1250
- // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`.
1251
- self . propagate_operand ( discr)
1252
- }
1253
996
// None of these have Operands to const-propagate.
1254
997
TerminatorKind :: Goto { .. }
1255
998
| TerminatorKind :: Resume
@@ -1262,12 +1005,9 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
1262
1005
| TerminatorKind :: GeneratorDrop
1263
1006
| TerminatorKind :: FalseEdge { .. }
1264
1007
| TerminatorKind :: FalseUnwind { .. }
1008
+ | TerminatorKind :: SwitchInt { .. }
1009
+ | TerminatorKind :: Call { .. }
1265
1010
| TerminatorKind :: InlineAsm { .. } => { }
1266
- // Every argument in our function calls have already been propagated in `visit_operand`.
1267
- //
1268
- // NOTE: because LLVM codegen gives slight performance regressions with it, so this is
1269
- // gated on `mir_opt_level=3`.
1270
- TerminatorKind :: Call { .. } => { }
1271
1011
}
1272
1012
1273
1013
// We remove all Locals which are restricted in propagation to their containing blocks and
0 commit comments