@@ -5,7 +5,7 @@ use std::fmt::Debug;
55
66use either:: Left ;
77
8- use rustc_const_eval:: interpret:: Immediate ;
8+ use rustc_const_eval:: interpret:: { ImmTy , Immediate , Projectable } ;
99use rustc_const_eval:: interpret:: {
1010 InterpCx , InterpResult , MemoryKind , OpTy , Scalar , StackPopCleanup ,
1111} ;
@@ -21,7 +21,7 @@ use rustc_middle::ty::{
2121 self , ConstInt , Instance , ParamEnv , ScalarInt , Ty , TyCtxt , TypeVisitableExt ,
2222} ;
2323use rustc_span:: Span ;
24- use rustc_target:: abi:: { HasDataLayout , Size , TargetDataLayout } ;
24+ use rustc_target:: abi:: { self , Abi , HasDataLayout , Size , TargetDataLayout } ;
2525
2626use crate :: const_prop:: CanConstProp ;
2727use crate :: const_prop:: ConstPropMachine ;
@@ -540,6 +540,188 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
540540 )
541541 }
542542 }
543+
544+ #[ instrument( level = "trace" , skip( self ) , ret) ]
545+ fn eval_rvalue (
546+ & mut self ,
547+ rvalue : & Rvalue < ' tcx > ,
548+ location : Location ,
549+ dest : & Place < ' tcx > ,
550+ ) -> Option < ( ) > {
551+ if !dest. projection . is_empty ( ) {
552+ return None ;
553+ }
554+ use rustc_middle:: mir:: Rvalue :: * ;
555+ let dest = self . use_ecx ( location, |this| this. ecx . eval_place ( * dest) ) ?;
556+ trace ! ( ?dest) ;
557+
558+ let val = match * rvalue {
559+ ThreadLocalRef ( _) => return None ,
560+
561+ Use ( ref operand) => self . eval_operand ( operand, location, Some ( dest. layout ) ) ?,
562+
563+ CopyForDeref ( place) => self . eval_place ( place, location, Some ( dest. layout ) ) ?,
564+
565+ BinaryOp ( bin_op, box ( ref left, ref right) ) => {
566+ let layout =
567+ rustc_const_eval:: util:: binop_left_homogeneous ( bin_op) . then_some ( dest. layout ) ;
568+ let left = self . eval_operand ( left, location, layout) ?;
569+ let left = self . use_ecx ( location, |this| this. ecx . read_immediate ( & left) ) ?;
570+
571+ let layout =
572+ rustc_const_eval:: util:: binop_right_homogeneous ( bin_op) . then_some ( left. layout ) ;
573+ let right = self . eval_operand ( right, location, layout) ?;
574+ let right = self . use_ecx ( location, |this| this. ecx . read_immediate ( & right) ) ?;
575+
576+ let val = self
577+ . use_ecx ( location, |this| this. ecx . wrapping_binary_op ( bin_op, & left, & right) ) ?;
578+ val. into ( )
579+ }
580+
581+ CheckedBinaryOp ( bin_op, box ( ref left, ref right) ) => {
582+ let left = self . eval_operand ( left, location, None ) ?;
583+ let left = self . use_ecx ( location, |this| this. ecx . read_immediate ( & left) ) ?;
584+
585+ let layout =
586+ rustc_const_eval:: util:: binop_right_homogeneous ( bin_op) . then_some ( left. layout ) ;
587+ let right = self . eval_operand ( right, location, layout) ?;
588+ let right = self . use_ecx ( location, |this| this. ecx . read_immediate ( & right) ) ?;
589+
590+ let ( val, overflowed) = self . use_ecx ( location, |this| {
591+ this. ecx . overflowing_binary_op ( bin_op, & left, & right)
592+ } ) ?;
593+ let tuple = Ty :: new_tup_from_iter (
594+ self . tcx ,
595+ [ val. layout . ty , self . tcx . types . bool ] . into_iter ( ) ,
596+ ) ;
597+ let tuple = self . ecx . layout_of ( tuple) . ok ( ) ?;
598+ let val =
599+ ImmTy :: from_scalar_pair ( val. to_scalar ( ) , Scalar :: from_bool ( overflowed) , tuple) ;
600+ val. into ( )
601+ }
602+
603+ UnaryOp ( un_op, ref operand) => {
604+ let operand = self . eval_operand ( operand, location, Some ( dest. layout ) ) ?;
605+ let val = self . use_ecx ( location, |this| this. ecx . read_immediate ( & operand) ) ?;
606+
607+ let val = self . use_ecx ( location, |this| this. ecx . wrapping_unary_op ( un_op, & val) ) ?;
608+ val. into ( )
609+ }
610+
611+ Aggregate ( ref kind, ref fields) => {
612+ trace ! ( ?kind) ;
613+ trace ! ( ?dest. layout) ;
614+ if dest. layout . is_zst ( ) {
615+ ImmTy :: uninit ( dest. layout ) . into ( )
616+ } else if let Abi :: Scalar ( abi:: Scalar :: Initialized { .. } ) = dest. layout . abi {
617+ let fields = fields
618+ . iter ( )
619+ . map ( |field| self . eval_operand ( field, location, None ) )
620+ . collect :: < Option < Vec < _ > > > ( ) ?;
621+ trace ! ( ?fields) ;
622+ let mut field =
623+ fields. into_iter ( ) . find ( |field| field. layout . abi . is_scalar ( ) ) ?;
624+ field. layout = dest. layout ;
625+ field
626+ } else if let Abi :: ScalarPair (
627+ abi:: Scalar :: Initialized { .. } ,
628+ abi:: Scalar :: Initialized { .. } ,
629+ ) = dest. layout . abi
630+ {
631+ let fields = fields
632+ . iter ( )
633+ . map ( |field| self . eval_operand ( field, location, None ) )
634+ . collect :: < Option < Vec < _ > > > ( ) ?;
635+ trace ! ( ?fields) ;
636+ let pair =
637+ fields. iter ( ) . find ( |field| matches ! ( field. layout. abi, Abi :: ScalarPair ( ..) ) ) ;
638+ if let Some ( pair) = pair {
639+ let mut pair = pair. clone ( ) ;
640+ pair. layout = dest. layout ;
641+ pair
642+ } else {
643+ // TODO: build a pair from two scalars
644+ return None ;
645+ }
646+ } else {
647+ return None ;
648+ }
649+ }
650+
651+ Repeat ( ref op, n) => {
652+ trace ! ( ?op, ?n) ;
653+ return None ;
654+ }
655+
656+ Len ( place) => {
657+ let src = self . eval_place ( place, location, None ) ?;
658+ let len = src. len ( & self . ecx ) . ok ( ) ?;
659+ ImmTy :: from_scalar ( Scalar :: from_target_usize ( len, self ) , dest. layout ) . into ( )
660+ }
661+
662+ Ref ( ..) | AddressOf ( ..) => return None ,
663+
664+ NullaryOp ( ref null_op, ty) => {
665+ let layout = self . use_ecx ( location, |this| this. ecx . layout_of ( ty) ) ?;
666+ let val = match null_op {
667+ NullOp :: SizeOf => layout. size . bytes ( ) ,
668+ NullOp :: AlignOf => layout. align . abi . bytes ( ) ,
669+ NullOp :: OffsetOf ( fields) => {
670+ layout. offset_of_subfield ( self , fields. iter ( ) ) . bytes ( )
671+ }
672+ } ;
673+ ImmTy :: from_scalar ( Scalar :: from_target_usize ( val, self ) , dest. layout ) . into ( )
674+ }
675+
676+ ShallowInitBox ( ..) => return None ,
677+
678+ Cast ( ref kind, ref value, to) => match kind {
679+ CastKind :: IntToInt | CastKind :: IntToFloat => {
680+ let value = self . eval_operand ( value, location, None ) ?;
681+ let value = self . ecx . read_immediate ( & value) . ok ( ) ?;
682+ let to = self . ecx . layout_of ( to) . ok ( ) ?;
683+ let res = self . ecx . int_to_int_or_float ( & value, to) . ok ( ) ?;
684+ res. into ( )
685+ }
686+ CastKind :: FloatToFloat | CastKind :: FloatToInt => {
687+ let value = self . eval_operand ( value, location, None ) ?;
688+ let value = self . ecx . read_immediate ( & value) . ok ( ) ?;
689+ let to = self . ecx . layout_of ( to) . ok ( ) ?;
690+ let res = self . ecx . float_to_float_or_int ( & value, to) . ok ( ) ?;
691+ res. into ( )
692+ }
693+ CastKind :: Transmute => {
694+ let value = self . eval_operand ( value, location, None ) ?;
695+ let to = self . ecx . layout_of ( to) . ok ( ) ?;
696+ // `offset` for immediates only supports scalar/scalar-pair ABIs,
697+ // so bail out if the target is not one.
698+ if value. as_mplace_or_imm ( ) . is_right ( ) {
699+ match ( value. layout . abi , to. abi ) {
700+ ( Abi :: Scalar ( ..) , Abi :: Scalar ( ..) ) => { }
701+ ( Abi :: ScalarPair ( ..) , Abi :: ScalarPair ( ..) ) => { }
702+ _ => return None ,
703+ }
704+ }
705+ value. offset ( Size :: ZERO , to, & self . ecx ) . ok ( ) ?
706+ }
707+ _ => return None ,
708+ } ,
709+
710+ Discriminant ( place) => {
711+ let op = self . eval_place ( place, location, None ) ?;
712+ let variant = self . use_ecx ( location, |this| this. ecx . read_discriminant ( & op) ) ?;
713+ let imm = self . use_ecx ( location, |this| {
714+ this. ecx . discriminant_for_variant ( op. layout . ty , variant)
715+ } ) ?;
716+ imm. into ( )
717+ }
718+ } ;
719+ trace ! ( ?val) ;
720+
721+ self . use_ecx ( location, |this| this. ecx . copy_op ( & val, & dest, true ) ) ?;
722+
723+ Some ( ( ) )
724+ }
543725}
544726
545727impl < ' tcx > Visitor < ' tcx > for ConstPropagator < ' _ , ' tcx > {
@@ -574,10 +756,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
574756 _ if place. is_indirect ( ) => { }
575757 ConstPropMode :: NoPropagation => self . ensure_not_propagated ( place. local ) ,
576758 ConstPropMode :: OnlyInsideOwnBlock | ConstPropMode :: FullConstProp => {
577- if self
578- . use_ecx ( location, |this| this. ecx . eval_rvalue_into_place ( rvalue, * place) )
579- . is_none ( )
580- {
759+ if self . eval_rvalue ( rvalue, location, place) . is_none ( ) {
581760 // Const prop failed, so erase the destination, ensuring that whatever happens
582761 // from here on, does not know about the previous value.
583762 // This is important in case we have
0 commit comments