@@ -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
@@ -555,6 +556,40 @@ where
555556 Ok ( place)
556557 }
557558
559+ /// Given a place, returns either the underlying mplace or a reference to where the value of
560+ /// this place is stored.
561+ fn as_mplace_or_mutable_local (
562+ & mut self ,
563+ place : & PlaceTy < ' tcx , M :: Provenance > ,
564+ ) -> InterpResult <
565+ ' tcx ,
566+ Either < MPlaceTy < ' tcx , M :: Provenance > , ( & mut Immediate < M :: Provenance > , TyAndLayout < ' tcx > ) > ,
567+ > {
568+ Ok ( match place. to_place ( ) . as_mplace_or_local ( ) {
569+ Left ( mplace) => Left ( mplace) ,
570+ Right ( ( local, offset, locals_addr, layout) ) => {
571+ if offset. is_some ( ) {
572+ // This has been projected to a part of this local, or had the type changed.
573+ // FIMXE: there are cases where we could still avoid allocating an mplace.
574+ Left ( place. force_mplace ( self ) ?)
575+ } else {
576+ debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
577+ debug_assert_eq ! ( self . layout_of_local( self . frame( ) , local, None ) ?, layout) ;
578+ match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
579+ Operand :: Indirect ( mplace) => {
580+ // The local is in memory.
581+ Left ( MPlaceTy { mplace : * mplace, layout } )
582+ }
583+ Operand :: Immediate ( local_val) => {
584+ // The local still has the optimized representation.
585+ Right ( ( local_val, layout) )
586+ }
587+ }
588+ }
589+ }
590+ } )
591+ }
592+
558593 /// Write an immediate to a place
559594 #[ inline( always) ]
560595 #[ instrument( skip( self ) , level = "trace" ) ]
@@ -606,60 +641,20 @@ where
606641 ) -> InterpResult < ' tcx > {
607642 assert ! ( dest. layout( ) . is_sized( ) , "Cannot write unsized immediate data" ) ;
608643
609- // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
610- // but not factored as a separate function.
611- let mplace = match dest. to_place ( ) . as_mplace_or_local ( ) {
612- Right ( ( local, offset, locals_addr, layout) ) => {
613- if offset. is_some ( ) {
614- // This has been projected to a part of this local. We could have complicated
615- // logic to still keep this local as an `Operand`... but it's much easier to
616- // just fall back to the indirect path.
617- dest. force_mplace ( self ) ?
618- } else {
619- debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
620- match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
621- Operand :: Immediate ( local_val) => {
622- // Local can be updated in-place.
623- * local_val = src;
624- // Double-check that the value we are storing and the local fit to each other.
625- // (*After* doing the update for borrow checker reasons.)
626- if cfg ! ( debug_assertions) {
627- let local_layout =
628- self . layout_of_local ( & self . frame ( ) , local, None ) ?;
629- match ( src, local_layout. abi ) {
630- ( Immediate :: Scalar ( scalar) , Abi :: Scalar ( s) ) => {
631- assert_eq ! ( scalar. size( ) , s. size( self ) )
632- }
633- (
634- Immediate :: ScalarPair ( a_val, b_val) ,
635- Abi :: ScalarPair ( a, b) ,
636- ) => {
637- assert_eq ! ( a_val. size( ) , a. size( self ) ) ;
638- assert_eq ! ( b_val. size( ) , b. size( self ) ) ;
639- }
640- ( Immediate :: Uninit , _) => { }
641- ( src, abi) => {
642- bug ! (
643- "value {src:?} cannot be written into local with type {} (ABI {abi:?})" ,
644- local_layout. ty
645- )
646- }
647- } ;
648- }
649- return Ok ( ( ) ) ;
650- }
651- Operand :: Indirect ( mplace) => {
652- // The local is in memory, go on below.
653- MPlaceTy { mplace : * mplace, layout }
654- }
655- }
644+ match self . as_mplace_or_mutable_local ( & dest. to_place ( ) ) ? {
645+ Right ( ( local_val, local_layout) ) => {
646+ // Local can be updated in-place.
647+ * local_val = src;
648+ // Double-check that the value we are storing and the local fit to each other.
649+ if cfg ! ( debug_assertions) {
650+ src. assert_matches_abi ( local_layout. abi , self ) ;
656651 }
657652 }
658- Left ( mplace) => mplace , // already referring to memory
659- } ;
660-
661- // This is already in memory, write there.
662- self . write_immediate_to_mplace_no_validate ( src , mplace . layout , mplace . mplace )
653+ Left ( mplace) => {
654+ self . write_immediate_to_mplace_no_validate ( src , mplace . layout , mplace . mplace ) ? ;
655+ }
656+ }
657+ Ok ( ( ) )
663658 }
664659
665660 /// Write an immediate to memory.
@@ -671,6 +666,9 @@ where
671666 layout : TyAndLayout < ' tcx > ,
672667 dest : MemPlace < M :: Provenance > ,
673668 ) -> InterpResult < ' tcx > {
669+ if cfg ! ( debug_assertions) {
670+ value. assert_matches_abi ( layout. abi , self ) ;
671+ }
674672 // Note that it is really important that the type here is the right one, and matches the
675673 // type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
676674 // to handle padding properly, which is only correct if we never look at this data with the
@@ -721,35 +719,18 @@ where
721719 & mut self ,
722720 dest : & impl Writeable < ' tcx , M :: Provenance > ,
723721 ) -> InterpResult < ' tcx > {
724- let mplace = match dest. to_place ( ) . as_mplace_or_local ( ) {
725- Left ( mplace) => mplace,
726- Right ( ( local, offset, locals_addr, layout) ) => {
727- if offset. is_some ( ) {
728- // This has been projected to a part of this local. We could have complicated
729- // logic to still keep this local as an `Operand`... but it's much easier to
730- // just fall back to the indirect path.
731- // FIXME: share the logic with `write_immediate_no_validate`.
732- dest. force_mplace ( self ) ?
733- } else {
734- debug_assert_eq ! ( locals_addr, self . frame( ) . locals_addr( ) ) ;
735- match self . frame_mut ( ) . locals [ local] . access_mut ( ) ? {
736- Operand :: Immediate ( local) => {
737- * local = Immediate :: Uninit ;
738- return Ok ( ( ) ) ;
739- }
740- Operand :: Indirect ( mplace) => {
741- // The local is in memory, go on below.
742- MPlaceTy { mplace : * mplace, layout }
743- }
744- }
745- }
722+ match self . as_mplace_or_mutable_local ( & dest. to_place ( ) ) ? {
723+ Right ( ( local_val, _local_layout) ) => {
724+ * local_val = Immediate :: Uninit ;
746725 }
747- } ;
748- let Some ( mut alloc) = self . get_place_alloc_mut ( & mplace) ? else {
749- // Zero-sized access
750- return Ok ( ( ) ) ;
751- } ;
752- alloc. write_uninit ( ) ?;
726+ Left ( mplace) => {
727+ let Some ( mut alloc) = self . get_place_alloc_mut ( & mplace) ? else {
728+ // Zero-sized access
729+ return Ok ( ( ) ) ;
730+ } ;
731+ alloc. write_uninit ( ) ?;
732+ }
733+ }
753734 Ok ( ( ) )
754735 }
755736
0 commit comments