@@ -180,7 +180,8 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
180180 Ptr ( MemPlace < Prov > ) ,
181181
182182 /// To support alloc-free locals, we are able to write directly to a local. The offset indicates
183- /// where in the local this place is located; if it is `None`, no projection has been applied.
183+ /// where in the local this place is located; if it is `None`, no projection has been applied
184+ /// and the type of the place is exactly the type of the local.
184185 /// Such projections are meaningful even if the offset is 0, since they can change layouts.
185186 /// (Without that optimization, we'd just always be a `MemPlace`.)
186187 /// `Local` places always refer to the current stack frame, so they are unstable under
@@ -557,6 +558,40 @@ where
557558 Ok ( place)
558559 }
559560
561+ /// Given a place, returns either the underlying mplace or a reference to where the value of
562+ /// this place is stored.
563+ fn as_mplace_or_mutable_local (
564+ & mut self ,
565+ place : & PlaceTy < ' tcx , M :: Provenance > ,
566+ ) -> InterpResult <
567+ ' tcx ,
568+ Either < MPlaceTy < ' tcx , M :: Provenance > , ( & mut Immediate < M :: Provenance > , TyAndLayout < ' tcx > ) > ,
569+ > {
570+ Ok ( match place. to_place ( ) . as_mplace_or_local ( ) {
571+ Left ( mplace) => Left ( mplace) ,
572+ Right ( ( local, offset, locals_addr, layout) ) => {
573+ if offset. is_some ( ) {
574+ // This has been projected to a part of this local, or had the type changed.
575+ // FIMXE: there are cases where we could still avoid allocating an mplace.
576+ Left ( place. force_mplace ( self ) ?)
577+ } else {
578+ debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
579+ debug_assert_eq ! ( self . layout_of_local( self . frame( ) , local, None ) ?, layout) ;
580+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
581+ Operand :: Indirect ( mplace) => {
582+ // The local is in memory.
583+ Left ( MPlaceTy { mplace : * mplace, layout } )
584+ }
585+ Operand :: Immediate ( local_val) => {
586+ // The local still has the optimized representation.
587+ Right ( ( local_val, layout) )
588+ }
589+ }
590+ }
591+ }
592+ } )
593+ }
594+
560595 /// Write an immediate to a place
561596 #[ inline( always) ]
562597 #[ instrument( skip( self ) , level = "trace" ) ]
@@ -608,60 +643,20 @@ where
608643 ) -> InterpResult < ' tcx > {
609644 assert ! ( dest. layout( ) . is_sized( ) , "Cannot write unsized immediate data" ) ;
610645
611- // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
612- // but not factored as a separate function.
613- let mplace = match dest. to_place ( ) . as_mplace_or_local ( ) {
614- Right ( ( local, offset, locals_addr, layout) ) => {
615- if offset. is_some ( ) {
616- // This has been projected to a part of this local. We could have complicated
617- // logic to still keep this local as an `Operand`... but it's much easier to
618- // just fall back to the indirect path.
619- dest. force_mplace ( self ) ?
620- } else {
621- debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
622- match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
623- Operand :: Immediate ( local_val) => {
624- // Local can be updated in-place.
625- * local_val = src;
626- // Double-check that the value we are storing and the local fit to each other.
627- // (*After* doing the update for borrow checker reasons.)
628- if cfg ! ( debug_assertions) {
629- let local_layout =
630- self . layout_of_local ( & self . frame ( ) , local, None ) ?;
631- match ( src, local_layout. abi ) {
632- ( Immediate :: Scalar ( scalar) , Abi :: Scalar ( s) ) => {
633- assert_eq ! ( scalar. size( ) , s. size( self ) )
634- }
635- (
636- Immediate :: ScalarPair ( a_val, b_val) ,
637- Abi :: ScalarPair ( a, b) ,
638- ) => {
639- assert_eq ! ( a_val. size( ) , a. size( self ) ) ;
640- assert_eq ! ( b_val. size( ) , b. size( self ) ) ;
641- }
642- ( Immediate :: Uninit , _) => { }
643- ( src, abi) => {
644- bug ! (
645- "value {src:?} cannot be written into local with type {} (ABI {abi:?})" ,
646- local_layout. ty
647- )
648- }
649- } ;
650- }
651- return Ok ( ( ) ) ;
652- }
653- Operand :: Indirect ( mplace) => {
654- // The local is in memory, go on below.
655- MPlaceTy { mplace : * mplace, layout }
656- }
657- }
646+ match self . as_mplace_or_mutable_local ( & dest. to_place ( ) ) ? {
647+ Right ( ( local_val, local_layout) ) => {
648+ // Local can be updated in-place.
649+ * local_val = src;
650+ // Double-check that the value we are storing and the local fit to each other.
651+ if cfg ! ( debug_assertions) {
652+ src. assert_matches_abi ( local_layout. abi , self ) ;
658653 }
659654 }
660- Left ( mplace) => mplace , // already referring to memory
661- } ;
662-
663- // This is already in memory, write there.
664- self . write_immediate_to_mplace_no_validate ( src , mplace . layout , mplace . mplace )
655+ Left ( mplace) => {
656+ self . write_immediate_to_mplace_no_validate ( src , mplace . layout , mplace . mplace ) ? ;
657+ }
658+ }
659+ Ok ( ( ) )
665660 }
666661
667662 /// Write an immediate to memory.
@@ -673,6 +668,9 @@ where
673668 layout : TyAndLayout < ' tcx > ,
674669 dest : MemPlace < M :: Provenance > ,
675670 ) -> InterpResult < ' tcx > {
671+ if cfg ! ( debug_assertions) {
672+ value. assert_matches_abi ( layout. abi , self ) ;
673+ }
676674 // Note that it is really important that the type here is the right one, and matches the
677675 // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
678676 // to handle padding properly, which is only correct if we never look at this data with the
@@ -723,35 +721,18 @@ where
723721 & mut self ,
724722 dest : & impl Writeable < ' tcx , M :: Provenance > ,
725723 ) -> InterpResult < ' tcx > {
726- let mplace = match dest. to_place ( ) . as_mplace_or_local ( ) {
727- Left ( mplace) => mplace,
728- Right ( ( local, offset, locals_addr, layout) ) => {
729- if offset. is_some ( ) {
730- // This has been projected to a part of this local. We could have complicated
731- // logic to still keep this local as an `Operand`... but it's much easier to
732- // just fall back to the indirect path.
733- // FIXME: share the logic with `write_immediate_no_validate`.
734- dest. force_mplace ( self ) ?
735- } else {
736- debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
737- match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
738- Operand :: Immediate ( local) => {
739- * local = Immediate :: Uninit ;
740- return Ok ( ( ) ) ;
741- }
742- Operand :: Indirect ( mplace) => {
743- // The local is in memory, go on below.
744- MPlaceTy { mplace : * mplace, layout }
745- }
746- }
747- }
724+ match self . as_mplace_or_mutable_local ( & dest. to_place ( ) ) ? {
725+ Right ( ( local_val, _local_layout) ) => {
726+ * local_val = Immediate :: Uninit ;
748727 }
749- } ;
750- let Some ( mut alloc) = self . get_place_alloc_mut ( & mplace) ? else {
751- // Zero-sized access
752- return Ok ( ( ) ) ;
753- } ;
754- alloc. write_uninit ( ) ?;
728+ Left ( mplace) => {
729+ let Some ( mut alloc) = self . get_place_alloc_mut ( & mplace) ? else {
730+ // Zero-sized access
731+ return Ok ( ( ) ) ;
732+ } ;
733+ alloc. write_uninit ( ) ?;
734+ }
735+ }
755736 Ok ( ( ) )
756737 }
757738
0 commit comments