@@ -14,8 +14,7 @@ use rustc_middle::mir::visit::{
14
14
} ;
15
15
use rustc_middle:: mir:: * ;
16
16
use rustc_middle:: ty:: layout:: { LayoutError , LayoutOf , LayoutOfHelpers , TyAndLayout } ;
17
- use rustc_middle:: ty:: GenericArgs ;
18
- use rustc_middle:: ty:: { self , ConstKind , Instance , ParamEnv , Ty , TyCtxt , TypeVisitableExt } ;
17
+ use rustc_middle:: ty:: { self , GenericArgs , Instance , ParamEnv , Ty , TyCtxt , TypeVisitableExt } ;
19
18
use rustc_span:: { def_id:: DefId , Span , DUMMY_SP } ;
20
19
use rustc_target:: abi:: { self , Align , HasDataLayout , Size , TargetDataLayout } ;
21
20
use rustc_target:: spec:: abi:: Abi as CallAbi ;
@@ -407,51 +406,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
407
406
ecx. machine . written_only_inside_own_block_locals . remove ( & local) ;
408
407
}
409
408
410
- /// Returns the value, if any, of evaluating `c`.
411
- fn eval_constant ( & mut self , c : & Constant < ' tcx > ) -> Option < OpTy < ' tcx > > {
412
- // FIXME we need to revisit this for #67176
413
- if c. has_param ( ) {
414
- return None ;
415
- }
416
-
417
- // No span, we don't want errors to be shown.
418
- self . ecx . eval_mir_constant ( & c. literal , None , None ) . ok ( )
419
- }
420
-
421
- /// Returns the value, if any, of evaluating `place`.
422
- fn eval_place ( & mut self , place : Place < ' tcx > ) -> Option < OpTy < ' tcx > > {
423
- trace ! ( "eval_place(place={:?})" , place) ;
424
- self . ecx . eval_place_to_op ( place, None ) . ok ( )
425
- }
426
-
427
- /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
428
- /// or `eval_place`, depending on the variant of `Operand` used.
429
- fn eval_operand ( & mut self , op : & Operand < ' tcx > ) -> Option < OpTy < ' tcx > > {
430
- match * op {
431
- Operand :: Constant ( ref c) => self . eval_constant ( c) ,
432
- Operand :: Move ( place) | Operand :: Copy ( place) => self . eval_place ( place) ,
433
- }
434
- }
435
-
436
409
fn propagate_operand ( & mut self , operand : & mut Operand < ' tcx > ) {
437
- match * operand {
438
- Operand :: Copy ( l) | Operand :: Move ( l) => {
439
- if let Some ( value) = self . get_const ( l) && self . should_const_prop ( & value) {
440
- // FIXME(felix91gr): this code only handles `Scalar` cases.
441
- // For now, we're not handling `ScalarPair` cases because
442
- // doing so here would require a lot of code duplication.
443
- // We should hopefully generalize `Operand` handling into a fn,
444
- // and use it to do const-prop here and everywhere else
445
- // where it makes sense.
446
- if let interpret:: Operand :: Immediate ( interpret:: Immediate :: Scalar (
447
- scalar,
448
- ) ) = * value
449
- {
450
- * operand = self . operand_from_scalar ( scalar, value. layout . ty ) ;
451
- }
452
- }
453
- }
454
- Operand :: Constant ( _) => ( ) ,
410
+ if let Some ( place) = operand. place ( ) && let Some ( op) = self . replace_with_const ( place) {
411
+ * operand = op;
455
412
}
456
413
}
457
414
@@ -579,93 +536,45 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
579
536
} ) )
580
537
}
581
538
582
- fn replace_with_const ( & mut self , place : Place < ' tcx > , rval : & mut Rvalue < ' tcx > ) {
539
+ fn replace_with_const ( & mut self , place : Place < ' tcx > ) -> Option < Operand < ' tcx > > {
583
540
// This will return None if the above `const_prop` invocation only "wrote" a
584
541
// type whose creation requires no write. E.g. a generator whose initial state
585
542
// consists solely of uninitialized memory (so it doesn't capture any locals).
586
- let Some ( ref value) = self . get_const ( place) else { return } ;
587
- if !self . should_const_prop ( value) {
588
- return ;
589
- }
590
- trace ! ( "replacing {:?}={:?} with {:?}" , place, rval, value) ;
591
-
592
- if let Rvalue :: Use ( Operand :: Constant ( c) ) = rval {
593
- match c. literal {
594
- ConstantKind :: Ty ( c) if matches ! ( c. kind( ) , ConstKind :: Unevaluated ( ..) ) => { }
595
- _ => {
596
- trace ! ( "skipping replace of Rvalue::Use({:?} because it is already a const" , c) ;
597
- return ;
598
- }
599
- }
543
+ let value = self . get_const ( place) ?;
544
+ if !self . tcx . consider_optimizing ( || format ! ( "ConstantPropagation - {value:?}" ) ) {
545
+ return None ;
600
546
}
547
+ trace ! ( "replacing {:?} with {:?}" , place, value) ;
601
548
602
- trace ! ( "attempting to replace {:?} with {:?}" , rval, value) ;
603
549
// FIXME> figure out what to do when read_immediate_raw fails
604
- let imm = self . ecx . read_immediate_raw ( value) . ok ( ) ;
550
+ let imm = self . ecx . read_immediate_raw ( & value) . ok ( ) ? ;
605
551
606
- if let Some ( Right ( imm) ) = imm {
607
- match * imm {
608
- interpret:: Immediate :: Scalar ( scalar) => {
609
- * rval = Rvalue :: Use ( self . operand_from_scalar ( scalar, value. layout . ty ) ) ;
610
- }
611
- Immediate :: ScalarPair ( ..) => {
612
- // Found a value represented as a pair. For now only do const-prop if the type
613
- // of `rvalue` is also a tuple with two scalars.
614
- // FIXME: enable the general case stated above ^.
615
- let ty = value. layout . ty ;
616
- // Only do it for tuples
617
- if let ty:: Tuple ( types) = ty. kind ( ) {
618
- // Only do it if tuple is also a pair with two scalars
619
- if let [ ty1, ty2] = types[ ..] {
620
- let ty_is_scalar = |ty| {
621
- self . ecx . layout_of ( ty) . ok ( ) . map ( |layout| layout. abi . is_scalar ( ) )
622
- == Some ( true )
623
- } ;
624
- let alloc = if ty_is_scalar ( ty1) && ty_is_scalar ( ty2) {
625
- let alloc = self
626
- . ecx
627
- . intern_with_temp_alloc ( value. layout , |ecx, dest| {
628
- ecx. write_immediate ( * imm, dest)
629
- } )
630
- . unwrap ( ) ;
631
- Some ( alloc)
632
- } else {
633
- None
634
- } ;
635
-
636
- if let Some ( alloc) = alloc {
637
- // Assign entire constant in a single statement.
638
- // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`.
639
- let const_val = ConstValue :: ByRef { alloc, offset : Size :: ZERO } ;
640
- let literal = ConstantKind :: Val ( const_val, ty) ;
641
- * rval = Rvalue :: Use ( Operand :: Constant ( Box :: new ( Constant {
642
- span : DUMMY_SP ,
643
- user_ty : None ,
644
- literal,
645
- } ) ) ) ;
646
- }
647
- }
648
- }
649
- }
650
- // Scalars or scalar pairs that contain undef values are assumed to not have
651
- // successfully evaluated and are thus not propagated.
652
- _ => { }
552
+ let Right ( imm) = imm else { return None } ;
553
+ match * imm {
554
+ Immediate :: Scalar ( scalar) if scalar. try_to_int ( ) . is_ok ( ) => {
555
+ Some ( self . operand_from_scalar ( scalar, value. layout . ty ) )
653
556
}
654
- }
655
- }
656
-
657
- /// Returns `true` if and only if this `op` should be const-propagated into.
658
- fn should_const_prop ( & mut self , op : & OpTy < ' tcx > ) -> bool {
659
- if !self . tcx . consider_optimizing ( || format ! ( "ConstantPropagation - OpTy: {:?}" , op) ) {
660
- return false ;
661
- }
662
-
663
- match * * op {
664
- interpret:: Operand :: Immediate ( Immediate :: Scalar ( s) ) => s. try_to_int ( ) . is_ok ( ) ,
665
- interpret:: Operand :: Immediate ( Immediate :: ScalarPair ( l, r) ) => {
666
- l. try_to_int ( ) . is_ok ( ) && r. try_to_int ( ) . is_ok ( )
557
+ Immediate :: ScalarPair ( l, r) if l. try_to_int ( ) . is_ok ( ) && r. try_to_int ( ) . is_ok ( ) => {
558
+ let alloc = self
559
+ . ecx
560
+ . intern_with_temp_alloc ( value. layout , |ecx, dest| {
561
+ ecx. write_immediate ( * imm, dest)
562
+ } )
563
+ . ok ( ) ?;
564
+
565
+ let literal = ConstantKind :: Val (
566
+ ConstValue :: ByRef { alloc, offset : Size :: ZERO } ,
567
+ value. layout . ty ,
568
+ ) ;
569
+ Some ( Operand :: Constant ( Box :: new ( Constant {
570
+ span : DUMMY_SP ,
571
+ user_ty : None ,
572
+ literal,
573
+ } ) ) )
667
574
}
668
- _ => false ,
575
+ // Scalars or scalar pairs that contain undef values are assumed to not have
576
+ // successfully evaluated and are thus not propagated.
577
+ _ => None ,
669
578
}
670
579
}
671
580
@@ -810,12 +719,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
810
719
811
720
fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
812
721
self . super_operand ( operand, location) ;
813
-
814
- // Only const prop copies and moves on `mir_opt_level=3` as doing so
815
- // currently slightly increases compile time in some cases.
816
- if self . tcx . sess . mir_opt_level ( ) >= 3 {
817
- self . propagate_operand ( operand)
818
- }
722
+ self . propagate_operand ( operand)
819
723
}
820
724
821
725
fn process_projection_elem (
@@ -825,8 +729,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
825
729
) -> Option < PlaceElem < ' tcx > > {
826
730
if let PlaceElem :: Index ( local) = elem
827
731
&& let Some ( value) = self . get_const ( local. into ( ) )
828
- && self . should_const_prop ( & value)
829
- && let interpret:: Operand :: Immediate ( interpret:: Immediate :: Scalar ( scalar) ) = * value
732
+ && let interpret:: Operand :: Immediate ( Immediate :: Scalar ( scalar) ) = * value
830
733
&& let Ok ( offset) = scalar. to_target_usize ( & self . tcx )
831
734
&& let Some ( min_length) = offset. checked_add ( 1 )
832
735
{
@@ -852,7 +755,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
852
755
ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
853
756
ConstPropMode :: OnlyInsideOwnBlock | ConstPropMode :: FullConstProp => {
854
757
if let Some ( ( ) ) = self . eval_rvalue_with_identities ( rvalue, * place) {
855
- self . replace_with_const ( * place, rvalue) ;
758
+ // If this was already an evaluated constant, keep it.
759
+ if let Rvalue :: Use ( Operand :: Constant ( c) ) = rvalue
760
+ && let ConstantKind :: Val ( ..) = c. literal
761
+ {
762
+ trace ! ( "skipping replace of Rvalue::Use({:?} because it is already a const" , c) ;
763
+ } else if let Some ( operand) = self . replace_with_const ( * place) {
764
+ * rvalue = Rvalue :: Use ( operand) ;
765
+ }
856
766
} else {
857
767
// Const prop failed, so erase the destination, ensuring that whatever happens
858
768
// from here on, does not know about the previous value.
@@ -919,45 +829,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
919
829
}
920
830
}
921
831
922
- fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
923
- self . super_terminator ( terminator, location) ;
924
-
925
- match & mut terminator. kind {
926
- TerminatorKind :: Assert { expected, ref mut cond, .. } => {
927
- if let Some ( ref value) = self . eval_operand ( & cond)
928
- && let Ok ( value_const) = self . ecx . read_scalar ( & value)
929
- && self . should_const_prop ( value)
930
- {
931
- trace ! ( "assertion on {:?} should be {:?}" , value, expected) ;
932
- * cond = self . operand_from_scalar ( value_const, self . tcx . types . bool ) ;
933
- }
934
- }
935
- TerminatorKind :: SwitchInt { ref mut discr, .. } => {
936
- // FIXME: This is currently redundant with `visit_operand`, but sadly
937
- // always visiting operands currently causes a perf regression in LLVM codegen, so
938
- // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`.
939
- self . propagate_operand ( discr)
940
- }
941
- // None of these have Operands to const-propagate.
942
- TerminatorKind :: Goto { .. }
943
- | TerminatorKind :: Resume
944
- | TerminatorKind :: Terminate
945
- | TerminatorKind :: Return
946
- | TerminatorKind :: Unreachable
947
- | TerminatorKind :: Drop { .. }
948
- | TerminatorKind :: Yield { .. }
949
- | TerminatorKind :: GeneratorDrop
950
- | TerminatorKind :: FalseEdge { .. }
951
- | TerminatorKind :: FalseUnwind { .. }
952
- | TerminatorKind :: InlineAsm { .. } => { }
953
- // Every argument in our function calls have already been propagated in `visit_operand`.
954
- //
955
- // NOTE: because LLVM codegen gives slight performance regressions with it, so this is
956
- // gated on `mir_opt_level=3`.
957
- TerminatorKind :: Call { .. } => { }
958
- }
959
- }
960
-
961
832
fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
962
833
self . super_basic_block_data ( block, data) ;
963
834
0 commit comments