@@ -14,8 +14,7 @@ use rustc_middle::mir::visit::{
1414} ;
1515use rustc_middle:: mir:: * ;
1616use 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 } ;
1918use rustc_span:: { def_id:: DefId , Span , DUMMY_SP } ;
2019use rustc_target:: abi:: { self , Align , HasDataLayout , Size , TargetDataLayout } ;
2120use rustc_target:: spec:: abi:: Abi as CallAbi ;
@@ -407,51 +406,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
407406 ecx. machine . written_only_inside_own_block_locals . remove ( & local) ;
408407 }
409408
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-
436409 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;
455412 }
456413 }
457414
@@ -579,93 +536,45 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
579536 } ) )
580537 }
581538
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 > > {
583540 // This will return None if the above `const_prop` invocation only "wrote" a
584541 // type whose creation requires no write. E.g. a generator whose initial state
585542 // 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 ;
600546 }
547+ trace ! ( "replacing {:?} with {:?}" , place, value) ;
601548
602- trace ! ( "attempting to replace {:?} with {:?}" , rval, value) ;
603549 // 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 ( ) ? ;
605551
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 ) )
653556 }
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+ } ) ) )
667574 }
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 ,
669578 }
670579 }
671580
@@ -810,12 +719,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
810719
811720 fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
812721 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)
819723 }
820724
821725 fn process_projection_elem (
@@ -825,8 +729,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
825729 ) -> Option < PlaceElem < ' tcx > > {
826730 if let PlaceElem :: Index ( local) = elem
827731 && 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
830733 && let Ok ( offset) = scalar. to_target_usize ( & self . tcx )
831734 && let Some ( min_length) = offset. checked_add ( 1 )
832735 {
@@ -852,7 +755,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
852755 ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
853756 ConstPropMode :: OnlyInsideOwnBlock | ConstPropMode :: FullConstProp => {
854757 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+ }
856766 } else {
857767 // Const prop failed, so erase the destination, ensuring that whatever happens
858768 // from here on, does not know about the previous value.
@@ -919,45 +829,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
919829 }
920830 }
921831
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-
961832 fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
962833 self . super_basic_block_data ( block, data) ;
963834
0 commit comments