@@ -28,9 +28,9 @@ use rustc_trait_selection::traits;
28
28
29
29
use crate :: const_eval:: error_to_const_error;
30
30
use 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 ,
34
34
} ;
35
35
use crate :: transform:: { MirPass , MirSource } ;
36
36
@@ -527,11 +527,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
527
527
right : & Operand < ' tcx > ,
528
528
source_info : SourceInfo ,
529
529
) -> 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 ) ?) ) ;
532
531
let l = self . use_ecx ( |this| this. ecx . read_immediate ( this. ecx . eval_operand ( left, None ) ?) ) ;
533
532
// Check for exceeding shifts *even if* we cannot evaluate the LHS.
534
533
if op == BinOp :: Shr || op == BinOp :: Shl {
534
+ let r = r?;
535
535
// We need the type of the LHS. We cannot use `place_layout` as that is the type
536
536
// of the result, which for checked binops is not the same!
537
537
let left_ty = left. ty ( & self . local_decls , self . tcx ) ;
@@ -564,21 +564,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
564
564
}
565
565
}
566
566
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
+ }
580
580
}
581
-
582
581
Some ( ( ) )
583
582
}
584
583
@@ -659,9 +658,74 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
659
658
return None ;
660
659
}
661
660
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 < ( ) > {
662
674
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
+
665
729
Ok ( ( ) )
666
730
} )
667
731
}
0 commit comments