@@ -28,9 +28,9 @@ use rustc_trait_selection::traits;
2828
2929use crate :: const_eval:: error_to_const_error;
3030use crate :: interpret:: {
31- self , compile_time_machine, AllocId , Allocation , Frame , ImmTy , Immediate , InterpCx , LocalState ,
32- LocalValue , MemPlace , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy , Pointer ,
33- ScalarMaybeUninit , StackPopCleanup ,
31+ self , compile_time_machine, truncate , AllocId , Allocation , Frame , ImmTy , Immediate , InterpCx ,
32+ LocalState , LocalValue , MemPlace , Memory , MemoryKind , OpTy , Operand as InterpOperand , PlaceTy ,
33+ Pointer , ScalarMaybeUninit , StackPopCleanup ,
3434} ;
3535use crate :: transform:: { MirPass , MirSource } ;
3636
@@ -527,11 +527,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
527527 right : & Operand < ' tcx > ,
528528 source_info : SourceInfo ,
529529 ) -> Option < ( ) > {
530- let r =
531- self . use_ecx ( |this| this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?) ) ?;
530+ let r = self . use_ecx ( |this| this. ecx . read_immediate ( this. ecx . eval_operand ( right, None ) ?) ) ;
532531 let l = self . use_ecx ( |this| this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ) ;
533532 // Check for exceeding shifts *even if* we cannot evaluate the LHS.
534533 if op == BinOp :: Shr || op == BinOp :: Shl {
534+ let r = r?;
535535 // We need the type of the LHS. We cannot use `place_layout` as that is the type
536536 // of the result, which for checked binops is not the same!
537537 let left_ty = left. ty ( & self . local_decls , self . tcx ) ;
@@ -564,21 +564,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
564564 }
565565 }
566566
567- let l = l? ;
568-
569- // The remaining operators are handled through `overflowing_binary_op`.
570- if self . use_ecx ( | this| {
571- let ( _res , overflow , _ty ) = this . ecx . overflowing_binary_op ( op , l , r ) ? ;
572- Ok ( overflow )
573- } ) ? {
574- self . report_assert_as_lint (
575- lint :: builtin :: ARITHMETIC_OVERFLOW ,
576- source_info ,
577- "this arithmetic operation will overflow" ,
578- AssertKind :: Overflow ( op , l . to_const_int ( ) , r . to_const_int ( ) ) ,
579- ) ? ;
567+ if let ( Some ( l ) , Some ( r ) ) = ( l , r ) {
568+ // The remaining operators are handled through `overflowing_binary_op`.
569+ if self . use_ecx ( |this| {
570+ let ( _res , overflow , _ty ) = this. ecx . overflowing_binary_op ( op , l , r ) ? ;
571+ Ok ( overflow )
572+ } ) ? {
573+ self . report_assert_as_lint (
574+ lint :: builtin :: ARITHMETIC_OVERFLOW ,
575+ source_info ,
576+ "this arithmetic operation will overflow" ,
577+ AssertKind :: Overflow ( op , l . to_const_int ( ) , r . to_const_int ( ) ) ,
578+ ) ? ;
579+ }
580580 }
581-
582581 Some ( ( ) )
583582 }
584583
@@ -659,9 +658,74 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
659658 return None ;
660659 }
661660
661+ if self . tcx . sess . opts . debugging_opts . mir_opt_level >= 3 {
662+ self . eval_rvalue_with_identities ( rvalue, place)
663+ } else {
664+ self . use_ecx ( |this| this. ecx . eval_rvalue_into_place ( rvalue, place) )
665+ }
666+ }
667+
668+ // Attempt to use albegraic identities to eliminate constant expressions
669+ fn eval_rvalue_with_identities (
670+ & mut self ,
671+ rvalue : & Rvalue < ' tcx > ,
672+ place : Place < ' tcx > ,
673+ ) -> Option < ( ) > {
662674 self . use_ecx ( |this| {
663- trace ! ( "calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})" , rvalue, place) ;
664- this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
675+ match rvalue {
676+ Rvalue :: BinaryOp ( op, left, right) | Rvalue :: CheckedBinaryOp ( op, left, right) => {
677+ let l = this. ecx . eval_operand ( left, None ) ;
678+ let r = this. ecx . eval_operand ( right, None ) ;
679+
680+ let const_arg = match ( l, r) {
681+ ( Ok ( x) , Err ( _) ) | ( Err ( _) , Ok ( x) ) => this. ecx . read_immediate ( x) ?,
682+ ( Err ( e) , Err ( _) ) => return Err ( e) ,
683+ ( Ok ( _) , Ok ( _) ) => {
684+ this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
685+ return Ok ( ( ) ) ;
686+ }
687+ } ;
688+
689+ let arg_value =
690+ this. ecx . force_bits ( const_arg. to_scalar ( ) ?, const_arg. layout . size ) ?;
691+ let dest = this. ecx . eval_place ( place) ?;
692+
693+ match op {
694+ BinOp :: BitAnd => {
695+ if arg_value == 0 {
696+ this. ecx . write_immediate ( * const_arg, dest) ?;
697+ }
698+ }
699+ BinOp :: BitOr => {
700+ if arg_value == truncate ( u128:: MAX , const_arg. layout . size )
701+ || ( const_arg. layout . ty . is_bool ( ) && arg_value == 1 )
702+ {
703+ this. ecx . write_immediate ( * const_arg, dest) ?;
704+ }
705+ }
706+ BinOp :: Mul => {
707+ if const_arg. layout . ty . is_integral ( ) && arg_value == 0 {
708+ if let Rvalue :: CheckedBinaryOp ( _, _, _) = rvalue {
709+ let val = Immediate :: ScalarPair (
710+ const_arg. to_scalar ( ) ?. into ( ) ,
711+ Scalar :: from_bool ( false ) . into ( ) ,
712+ ) ;
713+ this. ecx . write_immediate ( val, dest) ?;
714+ } else {
715+ this. ecx . write_immediate ( * const_arg, dest) ?;
716+ }
717+ }
718+ }
719+ _ => {
720+ this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
721+ }
722+ }
723+ }
724+ _ => {
725+ this. ecx . eval_rvalue_into_place ( rvalue, place) ?;
726+ }
727+ }
728+
665729 Ok ( ( ) )
666730 } )
667731 }
0 commit comments