@@ -180,7 +180,8 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
180
180
Ptr ( MemPlace < Prov > ) ,
181
181
182
182
/// 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.
184
185
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
185
186
/// (Without that optimization, we'd just always be a `MemPlace`.)
186
187
/// `Local` places always refer to the current stack frame, so they are unstable under
@@ -555,6 +556,40 @@ where
555
556
Ok ( place)
556
557
}
557
558
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
+
558
593
/// Write an immediate to a place
559
594
#[ inline( always) ]
560
595
#[ instrument( skip( self ) , level = "trace" ) ]
@@ -606,60 +641,20 @@ where
606
641
) -> InterpResult < ' tcx > {
607
642
assert ! ( dest. layout( ) . is_sized( ) , "Cannot write unsized immediate data" ) ;
608
643
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 ) ;
656
651
}
657
652
}
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 ( ( ) )
663
658
}
664
659
665
660
/// Write an immediate to memory.
@@ -671,6 +666,9 @@ where
671
666
layout : TyAndLayout < ' tcx > ,
672
667
dest : MemPlace < M :: Provenance > ,
673
668
) -> InterpResult < ' tcx > {
669
+ if cfg ! ( debug_assertions) {
670
+ value. assert_matches_abi ( layout. abi , self ) ;
671
+ }
674
672
// Note that it is really important that the type here is the right one, and matches the
675
673
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
676
674
// to handle padding properly, which is only correct if we never look at this data with the
@@ -721,35 +719,18 @@ where
721
719
& mut self ,
722
720
dest : & impl Writeable < ' tcx , M :: Provenance > ,
723
721
) -> 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 ;
746
725
}
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
+ }
753
734
Ok ( ( ) )
754
735
}
755
736
0 commit comments