@@ -338,11 +338,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
338338 Some ( self . insert ( Value :: Constant { value, disambiguator } ) )
339339 }
340340
341+ fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
342+ // Booleans are deterministic.
343+ self . insert ( Value :: Constant { value : Const :: from_bool ( self . tcx , flag) , disambiguator : 0 } )
344+ }
345+
341346 fn insert_scalar ( & mut self , scalar : Scalar , ty : Ty < ' tcx > ) -> VnIndex {
342347 self . insert_constant ( Const :: from_scalar ( self . tcx , scalar, ty) )
343348 . expect ( "scalars are deterministic" )
344349 }
345350
351+ fn insert_tuple ( & mut self , values : Vec < VnIndex > ) -> VnIndex {
352+ self . insert ( Value :: Aggregate ( AggregateTy :: Tuple , VariantIdx :: from_u32 ( 0 ) , values) )
353+ }
354+
346355 #[ instrument( level = "trace" , skip( self ) , ret) ]
347356 fn eval_to_const ( & mut self , value : VnIndex ) -> Option < OpTy < ' tcx > > {
348357 use Value :: * ;
@@ -775,14 +784,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
775784 Value :: Cast { kind, value, from, to }
776785 }
777786 Rvalue :: BinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
787+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
778788 let lhs = self . simplify_operand ( lhs, location) ;
779789 let rhs = self . simplify_operand ( rhs, location) ;
780- Value :: BinaryOp ( op, lhs?, rhs?)
790+ let lhs = lhs?;
791+ let rhs = rhs?;
792+ if let Some ( value) = self . simplify_binary ( op, false , ty, lhs, rhs) {
793+ return Some ( value) ;
794+ }
795+ Value :: BinaryOp ( op, lhs, rhs)
781796 }
782797 Rvalue :: CheckedBinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
798+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
783799 let lhs = self . simplify_operand ( lhs, location) ;
784800 let rhs = self . simplify_operand ( rhs, location) ;
785- Value :: CheckedBinaryOp ( op, lhs?, rhs?)
801+ let lhs = lhs?;
802+ let rhs = rhs?;
803+ if let Some ( value) = self . simplify_binary ( op, true , ty, lhs, rhs) {
804+ return Some ( value) ;
805+ }
806+ Value :: CheckedBinaryOp ( op, lhs, rhs)
786807 }
787808 Rvalue :: UnaryOp ( op, ref mut arg) => {
788809 let arg = self . simplify_operand ( arg, location) ?;
@@ -884,6 +905,92 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
884905
885906 Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
886907 }
908+
909+ #[ instrument( level = "trace" , skip( self ) , ret) ]
910+ fn simplify_binary (
911+ & mut self ,
912+ op : BinOp ,
913+ checked : bool ,
914+ lhs_ty : Ty < ' tcx > ,
915+ lhs : VnIndex ,
916+ rhs : VnIndex ,
917+ ) -> Option < VnIndex > {
918+ // Floats are weird enough that none of the logic below applies.
919+ let reasonable_ty =
920+ lhs_ty. is_integral ( ) || lhs_ty. is_bool ( ) || lhs_ty. is_char ( ) || lhs_ty. is_any_ptr ( ) ;
921+ if !reasonable_ty {
922+ return None ;
923+ }
924+
925+ let layout = self . ecx . layout_of ( lhs_ty) . ok ( ) ?;
926+
927+ let as_bits = |value| {
928+ let constant = self . evaluated [ value] . as_ref ( ) ?;
929+ let scalar = self . ecx . read_scalar ( constant) . ok ( ) ?;
930+ scalar. to_bits ( constant. layout . size ) . ok ( )
931+ } ;
932+
933+ // Represent the values as `Ok(bits)` or `Err(VnIndex)`.
934+ let a = as_bits ( lhs) . ok_or ( lhs) ;
935+ let b = as_bits ( rhs) . ok_or ( rhs) ;
936+ let result = match ( op, a, b) {
937+ // Neutral elements.
938+ ( BinOp :: Add | BinOp :: BitOr | BinOp :: BitXor , Ok ( 0 ) , Err ( p) )
939+ | (
940+ BinOp :: Add
941+ | BinOp :: BitOr
942+ | BinOp :: BitXor
943+ | BinOp :: Sub
944+ | BinOp :: Offset
945+ | BinOp :: Shl
946+ | BinOp :: Shr ,
947+ Err ( p) ,
948+ Ok ( 0 ) ,
949+ )
950+ | ( BinOp :: Mul , Ok ( 1 ) , Err ( p) )
951+ | ( BinOp :: Mul | BinOp :: Div , Err ( p) , Ok ( 1 ) ) => p,
952+ // Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size.
953+ ( BinOp :: BitAnd , Err ( p) , Ok ( ones) ) | ( BinOp :: BitAnd , Ok ( ones) , Err ( p) )
954+ if ones == layout. size . truncate ( u128:: MAX )
955+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
956+ {
957+ p
958+ }
959+ // Absorbing elements.
960+ ( BinOp :: Mul | BinOp :: BitAnd , _, Ok ( 0 ) )
961+ | ( BinOp :: Rem , _, Ok ( 1 ) )
962+ | (
963+ BinOp :: Mul | BinOp :: Div | BinOp :: Rem | BinOp :: BitAnd | BinOp :: Shl | BinOp :: Shr ,
964+ Ok ( 0 ) ,
965+ _,
966+ ) => self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty) ,
967+ // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
968+ ( BinOp :: BitOr , _, Ok ( ones) ) | ( BinOp :: BitOr , Ok ( ones) , _)
969+ if ones == layout. size . truncate ( u128:: MAX )
970+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
971+ {
972+ self . insert_scalar ( Scalar :: from_uint ( ones, layout. size ) , lhs_ty)
973+ }
974+ // Sub/Xor with itself.
975+ ( BinOp :: Sub | BinOp :: BitXor , a, b) if a == b => {
976+ self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty)
977+ }
978+ // Comparison:
979+ // - if both operands can be computed as bits, just compare the bits;
980+ // - if we proved that both operands have the same value, we can insert true/false;
981+ // - otherwise, do nothing, as we do not try to prove inequality.
982+ ( BinOp :: Eq , a, b) if ( a. is_ok ( ) && b. is_ok ( ) ) || a == b => self . insert_bool ( a == b) ,
983+ ( BinOp :: Ne , a, b) if ( a. is_ok ( ) && b. is_ok ( ) ) || a == b => self . insert_bool ( a != b) ,
984+ _ => return None ,
985+ } ;
986+
987+ if checked {
988+ let false_val = self . insert_bool ( false ) ;
989+ Some ( self . insert_tuple ( vec ! [ result, false_val] ) )
990+ } else {
991+ Some ( result)
992+ }
993+ }
887994}
888995
889996fn op_to_prop_const < ' tcx > (
0 commit comments