@@ -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 :: * ;
@@ -767,10 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
767
776
}
768
777
769
778
// Operations.
770
- Rvalue :: Len ( ref mut place) => {
771
- let place = self . simplify_place_value ( place, location) ?;
772
- Value :: Len ( place)
773
- }
779
+ Rvalue :: Len ( ref mut place) => return self . simplify_len ( place, location) ,
774
780
Rvalue :: Cast ( kind, ref mut value, to) => {
775
781
let from = value. ty ( self . local_decls , self . tcx ) ;
776
782
let value = self . simplify_operand ( value, location) ?;
@@ -785,17 +791,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
785
791
Value :: Cast { kind, value, from, to }
786
792
}
787
793
Rvalue :: BinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
794
+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
788
795
let lhs = self . simplify_operand ( lhs, location) ;
789
796
let rhs = self . simplify_operand ( rhs, location) ;
790
- Value :: BinaryOp ( op, lhs?, rhs?)
797
+ // Only short-circuit options after we called `simplify_operand`
798
+ // on both operands for side effect.
799
+ let lhs = lhs?;
800
+ let rhs = rhs?;
801
+ if let Some ( value) = self . simplify_binary ( op, false , ty, lhs, rhs) {
802
+ return Some ( value) ;
803
+ }
804
+ Value :: BinaryOp ( op, lhs, rhs)
791
805
}
792
806
Rvalue :: CheckedBinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
807
+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
793
808
let lhs = self . simplify_operand ( lhs, location) ;
794
809
let rhs = self . simplify_operand ( rhs, location) ;
795
- Value :: CheckedBinaryOp ( op, lhs?, rhs?)
810
+ // Only short-circuit options after we called `simplify_operand`
811
+ // on both operands for side effect.
812
+ let lhs = lhs?;
813
+ let rhs = rhs?;
814
+ if let Some ( value) = self . simplify_binary ( op, true , ty, lhs, rhs) {
815
+ return Some ( value) ;
816
+ }
817
+ Value :: CheckedBinaryOp ( op, lhs, rhs)
796
818
}
797
819
Rvalue :: UnaryOp ( op, ref mut arg) => {
798
820
let arg = self . simplify_operand ( arg, location) ?;
821
+ if let Some ( value) = self . simplify_unary ( op, arg) {
822
+ return Some ( value) ;
823
+ }
799
824
Value :: UnaryOp ( op, arg)
800
825
}
801
826
Rvalue :: Discriminant ( ref mut place) => {
@@ -894,6 +919,150 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
894
919
895
920
Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
896
921
}
922
+
923
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
924
+ fn simplify_unary ( & mut self , op : UnOp , value : VnIndex ) -> Option < VnIndex > {
925
+ let value = match ( op, self . get ( value) ) {
926
+ ( UnOp :: Not , Value :: UnaryOp ( UnOp :: Not , inner) ) => return Some ( * inner) ,
927
+ ( UnOp :: Neg , Value :: UnaryOp ( UnOp :: Neg , inner) ) => return Some ( * inner) ,
928
+ ( UnOp :: Not , Value :: BinaryOp ( BinOp :: Eq , lhs, rhs) ) => {
929
+ Value :: BinaryOp ( BinOp :: Ne , * lhs, * rhs)
930
+ }
931
+ ( UnOp :: Not , Value :: BinaryOp ( BinOp :: Ne , lhs, rhs) ) => {
932
+ Value :: BinaryOp ( BinOp :: Eq , * lhs, * rhs)
933
+ }
934
+ _ => return None ,
935
+ } ;
936
+
937
+ Some ( self . insert ( value) )
938
+ }
939
+
940
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
941
+ fn simplify_binary (
942
+ & mut self ,
943
+ op : BinOp ,
944
+ checked : bool ,
945
+ lhs_ty : Ty < ' tcx > ,
946
+ lhs : VnIndex ,
947
+ rhs : VnIndex ,
948
+ ) -> Option < VnIndex > {
949
+ // Floats are weird enough that none of the logic below applies.
950
+ let reasonable_ty =
951
+ lhs_ty. is_integral ( ) || lhs_ty. is_bool ( ) || lhs_ty. is_char ( ) || lhs_ty. is_any_ptr ( ) ;
952
+ if !reasonable_ty {
953
+ return None ;
954
+ }
955
+
956
+ let layout = self . ecx . layout_of ( lhs_ty) . ok ( ) ?;
957
+
958
+ let as_bits = |value| {
959
+ let constant = self . evaluated [ value] . as_ref ( ) ?;
960
+ if layout. abi . is_scalar ( ) {
961
+ let scalar = self . ecx . read_scalar ( constant) . ok ( ) ?;
962
+ scalar. to_bits ( constant. layout . size ) . ok ( )
963
+ } else {
964
+ // `constant` is a wide pointer. Do not evaluate to bits.
965
+ None
966
+ }
967
+ } ;
968
+
969
+ // Represent the values as `Left(bits)` or `Right(VnIndex)`.
970
+ use Either :: { Left , Right } ;
971
+ let a = as_bits ( lhs) . map_or ( Right ( lhs) , Left ) ;
972
+ let b = as_bits ( rhs) . map_or ( Right ( rhs) , Left ) ;
973
+ let result = match ( op, a, b) {
974
+ // Neutral elements.
975
+ ( BinOp :: Add | BinOp :: BitOr | BinOp :: BitXor , Left ( 0 ) , Right ( p) )
976
+ | (
977
+ BinOp :: Add
978
+ | BinOp :: BitOr
979
+ | BinOp :: BitXor
980
+ | BinOp :: Sub
981
+ | BinOp :: Offset
982
+ | BinOp :: Shl
983
+ | BinOp :: Shr ,
984
+ Right ( p) ,
985
+ Left ( 0 ) ,
986
+ )
987
+ | ( BinOp :: Mul , Left ( 1 ) , Right ( p) )
988
+ | ( BinOp :: Mul | BinOp :: Div , Right ( p) , Left ( 1 ) ) => p,
989
+ // Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size.
990
+ ( BinOp :: BitAnd , Right ( p) , Left ( ones) ) | ( BinOp :: BitAnd , Left ( ones) , Right ( p) )
991
+ if ones == layout. size . truncate ( u128:: MAX )
992
+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
993
+ {
994
+ p
995
+ }
996
+ // Absorbing elements.
997
+ ( BinOp :: Mul | BinOp :: BitAnd , _, Left ( 0 ) )
998
+ | ( BinOp :: Rem , _, Left ( 1 ) )
999
+ | (
1000
+ BinOp :: Mul | BinOp :: Div | BinOp :: Rem | BinOp :: BitAnd | BinOp :: Shl | BinOp :: Shr ,
1001
+ Left ( 0 ) ,
1002
+ _,
1003
+ ) => self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty) ,
1004
+ // Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
1005
+ ( BinOp :: BitOr , _, Left ( ones) ) | ( BinOp :: BitOr , Left ( ones) , _)
1006
+ if ones == layout. size . truncate ( u128:: MAX )
1007
+ || ( layout. ty . is_bool ( ) && ones == 1 ) =>
1008
+ {
1009
+ self . insert_scalar ( Scalar :: from_uint ( ones, layout. size ) , lhs_ty)
1010
+ }
1011
+ // Sub/Xor with itself.
1012
+ ( BinOp :: Sub | BinOp :: BitXor , a, b) if a == b => {
1013
+ self . insert_scalar ( Scalar :: from_uint ( 0u128 , layout. size ) , lhs_ty)
1014
+ }
1015
+ // Comparison:
1016
+ // - if both operands can be computed as bits, just compare the bits;
1017
+ // - if we proved that both operands have the same value, we can insert true/false;
1018
+ // - otherwise, do nothing, as we do not try to prove inequality.
1019
+ ( BinOp :: Eq , Left ( a) , Left ( b) ) => self . insert_bool ( a == b) ,
1020
+ ( BinOp :: Eq , a, b) if a == b => self . insert_bool ( true ) ,
1021
+ ( BinOp :: Ne , Left ( a) , Left ( b) ) => self . insert_bool ( a != b) ,
1022
+ ( BinOp :: Ne , a, b) if a == b => self . insert_bool ( false ) ,
1023
+ _ => return None ,
1024
+ } ;
1025
+
1026
+ if checked {
1027
+ let false_val = self . insert_bool ( false ) ;
1028
+ Some ( self . insert_tuple ( vec ! [ result, false_val] ) )
1029
+ } else {
1030
+ Some ( result)
1031
+ }
1032
+ }
1033
+
1034
+ fn simplify_len ( & mut self , place : & mut Place < ' tcx > , location : Location ) -> Option < VnIndex > {
1035
+ // Trivial case: we are fetching a statically known length.
1036
+ let place_ty = place. ty ( self . local_decls , self . tcx ) . ty ;
1037
+ if let ty:: Array ( _, len) = place_ty. kind ( ) {
1038
+ return self . insert_constant ( Const :: from_ty_const ( * len, self . tcx ) ) ;
1039
+ }
1040
+
1041
+ let mut inner = self . simplify_place_value ( place, location) ?;
1042
+
1043
+ // The length information is stored in the fat pointer.
1044
+ // Reborrowing copies length information from one pointer to the other.
1045
+ while let Value :: Address { place : borrowed, .. } = self . get ( inner)
1046
+ && let [ PlaceElem :: Deref ] = borrowed. projection [ ..]
1047
+ && let Some ( borrowed) = self . locals [ borrowed. local ]
1048
+ {
1049
+ inner = borrowed;
1050
+ }
1051
+
1052
+ // We have an unsizing cast, which assigns the length to fat pointer metadata.
1053
+ if let Value :: Cast { kind, from, to, .. } = self . get ( inner)
1054
+ && let CastKind :: PointerCoercion ( ty:: adjustment:: PointerCoercion :: Unsize ) = kind
1055
+ && let Some ( from) = from. builtin_deref ( true )
1056
+ && let ty:: Array ( _, len) = from. ty . kind ( )
1057
+ && let Some ( to) = to. builtin_deref ( true )
1058
+ && let ty:: Slice ( ..) = to. ty . kind ( )
1059
+ {
1060
+ return self . insert_constant ( Const :: from_ty_const ( * len, self . tcx ) ) ;
1061
+ }
1062
+
1063
+ // Fallback: a symbolic `Len`.
1064
+ Some ( self . insert ( Value :: Len ( inner) ) )
1065
+ }
897
1066
}
898
1067
899
1068
fn op_to_prop_const < ' tcx > (
0 commit comments