@@ -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
@@ -557,6 +558,40 @@ where
557
558
Ok ( place)
558
559
}
559
560
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
+
560
595
/// Write an immediate to a place
561
596
#[ inline( always) ]
562
597
#[ instrument( skip( self ) , level = "trace" ) ]
@@ -608,60 +643,20 @@ where
608
643
) -> InterpResult < ' tcx > {
609
644
assert ! ( dest. layout( ) . is_sized( ) , "Cannot write unsized immediate data" ) ;
610
645
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 ) ;
658
653
}
659
654
}
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 ( ( ) )
665
660
}
666
661
667
662
/// Write an immediate to memory.
@@ -673,6 +668,9 @@ where
673
668
layout : TyAndLayout < ' tcx > ,
674
669
dest : MemPlace < M :: Provenance > ,
675
670
) -> InterpResult < ' tcx > {
671
+ if cfg ! ( debug_assertions) {
672
+ value. assert_matches_abi ( layout. abi , self ) ;
673
+ }
676
674
// Note that it is really important that the type here is the right one, and matches the
677
675
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
678
676
// to handle padding properly, which is only correct if we never look at this data with the
@@ -723,35 +721,18 @@ where
723
721
& mut self ,
724
722
dest : & impl Writeable < ' tcx , M :: Provenance > ,
725
723
) -> 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 ;
748
727
}
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
+ }
755
736
Ok ( ( ) )
756
737
}
757
738
0 commit comments