@@ -345,11 +345,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
345
345
Some ( self . insert ( Value :: Constant { value, disambiguator } ) )
346
346
}
347
347
348
+ fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
349
+ // Booleans are deterministic.
350
+ self . insert ( Value :: Constant { value : Const :: from_bool ( self . tcx , flag) , disambiguator : 0 } )
351
+ }
352
+
348
353
fn insert_scalar ( & mut self , scalar : Scalar , ty : Ty < ' tcx > ) -> VnIndex {
349
354
self . insert_constant ( Const :: from_scalar ( self . tcx , scalar, ty) )
350
355
. expect ( "scalars are deterministic" )
351
356
}
352
357
358
+ fn insert_tuple ( & mut self , values : Vec < VnIndex > ) -> VnIndex {
359
+ self . insert ( Value :: Aggregate ( AggregateTy :: Tuple , VariantIdx :: from_u32 ( 0 ) , values) )
360
+ }
361
+
353
362
#[ instrument( level = "trace" , skip( self ) , ret) ]
354
363
fn eval_to_const ( & mut self , value : VnIndex ) -> Option < OpTy < ' tcx > > {
355
364
use Value :: * ;
@@ -785,14 +794,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
785
794
Value :: Cast { kind, value, from, to }
786
795
}
787
796
Rvalue :: BinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
797
+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
788
798
let lhs = self . simplify_operand ( lhs, location) ;
789
799
let rhs = self . simplify_operand ( rhs, location) ;
790
- Value :: BinaryOp ( op, lhs?, rhs?)
800
+ let lhs = lhs?;
801
+ let rhs = rhs?;
802
+ if let Some ( value) = self . simplify_binary ( op, false , ty, lhs, rhs) {
803
+ return Some ( value) ;
804
+ }
805
+ Value :: BinaryOp ( op, lhs, rhs)
791
806
}
792
807
Rvalue :: CheckedBinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
808
+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
793
809
let lhs = self . simplify_operand ( lhs, location) ;
794
810
let rhs = self . simplify_operand ( rhs, location) ;
795
- Value :: CheckedBinaryOp ( op, lhs?, rhs?)
811
+ let lhs = lhs?;
812
+ let rhs = rhs?;
813
+ if let Some ( value) = self . simplify_binary ( op, true , ty, lhs, rhs) {
814
+ return Some ( value) ;
815
+ }
816
+ Value :: CheckedBinaryOp ( op, lhs, rhs)
796
817
}
797
818
Rvalue :: UnaryOp ( op, ref mut arg) => {
798
819
let arg = self . simplify_operand ( arg, location) ?;
@@ -894,6 +915,92 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
894
915
895
916
Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
896
917
}
918
+
919
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
920
+ fn simplify_binary (
921
+ & mut self ,
922
+ op : BinOp ,
923
+ checked : bool ,
924
+ lhs_ty : Ty < ' tcx > ,
925
+ lhs : VnIndex ,
926
+ rhs : VnIndex ,
927
+ ) -> Option < VnIndex > {
928
+ // Floats are weird enough that none of the logic below applies.
929
+ let reasonable_ty =
930
+ lhs_ty. is_integral ( ) || lhs_ty. is_bool ( ) || lhs_ty. is_char ( ) || lhs_ty. is_any_ptr ( ) ;
931
+ if !reasonable_ty {
932
+ return None ;
933
+ }
934
+
935
+ let layout = self . ecx . layout_of ( lhs_ty) . ok ( ) ?;
936
+
937
+ let as_bits = |value| {
938
+ let constant = self . evaluated [ value] . as_ref ( ) ?;
939
+ let scalar = self . ecx . read_scalar ( constant) . ok ( ) ?;
940
+ scalar. to_bits ( constant. layout . size ) . ok ( )
941
+ } ;
942
+
943
+ // Represent the values as `Ok(bits)` or `Err(VnIndex)`.
944
+ let a = as_bits ( lhs) . ok_or ( lhs) ;
945
+ let b = as_bits ( rhs) . ok_or ( rhs) ;
946
+ let result = match ( op, a, b) {
947
+ // Neutral elements.
948
+ ( BinOp :: Add | BinOp :: BitOr | BinOp :: BitXor , Ok ( 0 ) , Err ( p) )
949
+ | (
950
+ BinOp :: Add
951
+ | BinOp :: BitOr
952
+ | BinOp :: BitXor
953
+ | BinOp :: Sub
954
+ | BinOp :: Offset
955
+ | BinOp :: Shl
956
+ | BinOp :: Shr ,
957
+ Err ( p) ,
958
+ Ok ( 0 ) ,
959
+ )
960
+ | ( BinOp :: Mul , Ok ( 1 ) , Err ( p) )
961
+ | ( BinOp :: Mul | BinOp :: Div , Err ( p) , Ok ( 1 ) ) => p,
962
+ // Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size.
963
+ ( BinOp :: BitAnd , Err ( p) , Ok ( ones) ) | ( BinOp :: BitAnd , Ok ( ones) , Err ( p) )
964
+ if ones == layout. size . truncate ( u128:: MAX )
965
+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
966
+ {
967
+ p
968
+ }
969
+ // Absorbing elements.
970
+ ( BinOp :: Mul | BinOp :: BitAnd , _, Ok ( 0 ) )
971
+ | ( BinOp :: Rem , _, Ok ( 1 ) )
972
+ | (
973
+ BinOp :: Mul | BinOp :: Div | BinOp :: Rem | BinOp :: BitAnd | BinOp :: Shl | BinOp :: Shr ,
974
+ Ok ( 0 ) ,
975
+ _,
976
+ ) => self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty) ,
977
+ // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
978
+ ( BinOp :: BitOr , _, Ok ( ones) ) | ( BinOp :: BitOr , Ok ( ones) , _)
979
+ if ones == layout. size . truncate ( u128:: MAX )
980
+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
981
+ {
982
+ self . insert_scalar ( Scalar :: from_uint ( ones, layout. size ) , lhs_ty)
983
+ }
984
+ // Sub/Xor with itself.
985
+ ( BinOp :: Sub | BinOp :: BitXor , a, b) if a == b => {
986
+ self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty)
987
+ }
988
+ // Comparison:
989
+ // - if both operands can be computed as bits, just compare the bits;
990
+ // - if we proved that both operands have the same value, we can insert true/false;
991
+ // - otherwise, do nothing, as we do not try to prove inequality.
992
+ ( BinOp :: Eq , a, b) if ( a. is_ok ( ) && b. is_ok ( ) ) || a == b => self . insert_bool ( a == b) ,
993
+ ( BinOp :: Ne , a, b) if ( a. is_ok ( ) && b. is_ok ( ) ) || a == b => self . insert_bool ( a != b) ,
994
+ _ => return None ,
995
+ } ;
996
+
997
+ if checked {
998
+ let false_val = self . insert_bool ( false ) ;
999
+ Some ( self . insert_tuple ( vec ! [ result, false_val] ) )
1000
+ } else {
1001
+ Some ( result)
1002
+ }
1003
+ }
897
1004
}
898
1005
899
1006
fn op_to_prop_const < ' tcx > (
0 commit comments