@@ -345,11 +345,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
345345 Some ( self . insert ( Value :: Constant { value, disambiguator } ) )
346346 }
347347
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+
348353 fn insert_scalar ( & mut self , scalar : Scalar , ty : Ty < ' tcx > ) -> VnIndex {
349354 self . insert_constant ( Const :: from_scalar ( self . tcx , scalar, ty) )
350355 . expect ( "scalars are deterministic" )
351356 }
352357
358+ fn insert_tuple ( & mut self , values : Vec < VnIndex > ) -> VnIndex {
359+ self . insert ( Value :: Aggregate ( AggregateTy :: Tuple , VariantIdx :: from_u32 ( 0 ) , values) )
360+ }
361+
353362 #[ instrument( level = "trace" , skip( self ) , ret) ]
354363 fn eval_to_const ( & mut self , value : VnIndex ) -> Option < OpTy < ' tcx > > {
355364 use Value :: * ;
@@ -767,10 +776,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
767776 }
768777
769778 // 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) ,
774780 Rvalue :: Cast ( kind, ref mut value, to) => {
775781 let from = value. ty ( self . local_decls , self . tcx ) ;
776782 let value = self . simplify_operand ( value, location) ?;
@@ -785,17 +791,36 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
785791 Value :: Cast { kind, value, from, to }
786792 }
787793 Rvalue :: BinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
794+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
788795 let lhs = self . simplify_operand ( lhs, location) ;
789796 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)
791805 }
792806 Rvalue :: CheckedBinaryOp ( op, box ( ref mut lhs, ref mut rhs) ) => {
807+ let ty = lhs. ty ( self . local_decls , self . tcx ) ;
793808 let lhs = self . simplify_operand ( lhs, location) ;
794809 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)
796818 }
797819 Rvalue :: UnaryOp ( op, ref mut arg) => {
798820 let arg = self . simplify_operand ( arg, location) ?;
821+ if let Some ( value) = self . simplify_unary ( op, arg) {
822+ return Some ( value) ;
823+ }
799824 Value :: UnaryOp ( op, arg)
800825 }
801826 Rvalue :: Discriminant ( ref mut place) => {
@@ -894,6 +919,150 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
894919
895920 Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
896921 }
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+ }
8971066}
8981067
8991068fn op_to_prop_const < ' tcx > (
0 commit comments