@@ -10,8 +10,9 @@ use rustc_macros::HashStable;
10
10
use rustc_middle:: mir;
11
11
use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt , TyAndLayout } ;
12
12
use rustc_middle:: ty:: { self , Ty } ;
13
- use rustc_target:: abi:: { Abi , Align , FieldsShape , TagEncoding } ;
14
- use rustc_target:: abi:: { HasDataLayout , Size , VariantIdx , Variants } ;
13
+ use rustc_target:: abi:: {
14
+ Abi , Align , FieldsShape , HasDataLayout , Size , TagEncoding , VariantIdx , Variants ,
15
+ } ;
15
16
16
17
use super :: {
17
18
alloc_range, mir_assign_valid_types, AllocId , AllocRef , AllocRefMut , CheckInAllocMsg ,
@@ -772,51 +773,51 @@ where
772
773
}
773
774
Place :: Ptr ( mplace) => mplace, // already referring to memory
774
775
} ;
775
- let dest = MPlaceTy { mplace, layout : dest. layout , align : dest. align } ;
776
776
777
777
// This is already in memory, write there.
778
- self . write_immediate_to_mplace_no_validate ( src, & dest)
778
+ self . write_immediate_to_mplace_no_validate ( src, dest. layout , dest . align , mplace )
779
779
}
780
780
781
781
/// Write an immediate to memory.
782
782
/// If you use this you are responsible for validating that things got copied at the
783
- /// right type .
783
+ /// right layout .
784
784
fn write_immediate_to_mplace_no_validate (
785
785
& mut self ,
786
786
value : Immediate < M :: PointerTag > ,
787
- dest : & MPlaceTy < ' tcx , M :: PointerTag > ,
787
+ layout : TyAndLayout < ' tcx > ,
788
+ align : Align ,
789
+ dest : MemPlace < M :: PointerTag > ,
788
790
) -> InterpResult < ' tcx > {
789
791
// Note that it is really important that the type here is the right one, and matches the
790
792
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
791
793
// to handle padding properly, which is only correct if we never look at this data with the
792
794
// wrong type.
793
795
794
796
let tcx = * self . tcx ;
795
- let Some ( mut alloc) = self . get_place_alloc_mut ( dest) ? else {
797
+ let Some ( mut alloc) = self . get_place_alloc_mut ( & MPlaceTy { mplace : dest, layout , align } ) ? else {
796
798
// zero-sized access
797
799
return Ok ( ( ) ) ;
798
800
} ;
799
801
800
802
match value {
801
803
Immediate :: Scalar ( scalar) => {
802
- let Abi :: Scalar ( s) = dest . layout . abi else { span_bug ! (
804
+ let Abi :: Scalar ( s) = layout. abi else { span_bug ! (
803
805
self . cur_span( ) ,
804
- "write_immediate_to_mplace: invalid Scalar layout: {:#?}" ,
805
- dest. layout
806
+ "write_immediate_to_mplace: invalid Scalar layout: {layout:#?}" ,
806
807
)
807
808
} ;
808
809
let size = s. size ( & tcx) ;
809
- assert_eq ! ( size, dest . layout. size, "abi::Scalar size does not match layout size" ) ;
810
+ assert_eq ! ( size, layout. size, "abi::Scalar size does not match layout size" ) ;
810
811
alloc. write_scalar ( alloc_range ( Size :: ZERO , size) , scalar)
811
812
}
812
813
Immediate :: ScalarPair ( a_val, b_val) => {
813
814
// We checked `ptr_align` above, so all fields will have the alignment they need.
814
815
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
815
816
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
816
- let Abi :: ScalarPair ( a, b) = dest . layout . abi else { span_bug ! (
817
+ let Abi :: ScalarPair ( a, b) = layout. abi else { span_bug ! (
817
818
self . cur_span( ) ,
818
819
"write_immediate_to_mplace: invalid ScalarPair layout: {:#?}" ,
819
- dest . layout
820
+ layout
820
821
)
821
822
} ;
822
823
let ( a_size, b_size) = ( a. size ( & tcx) , b. size ( & tcx) ) ;
@@ -858,16 +859,17 @@ where
858
859
Ok ( ( ) )
859
860
}
860
861
861
- /// Copies the data from an operand to a place. This does not support transmuting!
862
- /// Use `copy_op_transmute` if the layouts could disagree.
862
+ /// Copies the data from an operand to a place.
863
+ /// `allow_transmute` indicates whether the layouts may disagree.
863
864
#[ inline( always) ]
864
865
#[ instrument( skip( self ) , level = "debug" ) ]
865
866
pub fn copy_op (
866
867
& mut self ,
867
868
src : & OpTy < ' tcx , M :: PointerTag > ,
868
869
dest : & PlaceTy < ' tcx , M :: PointerTag > ,
870
+ allow_transmute : bool ,
869
871
) -> InterpResult < ' tcx > {
870
- self . copy_op_no_validate ( src, dest) ?;
872
+ self . copy_op_no_validate ( src, dest, allow_transmute ) ?;
871
873
872
874
if M :: enforce_validity ( self ) {
873
875
// Data got changed, better make sure it matches the type!
@@ -877,19 +879,22 @@ where
877
879
Ok ( ( ) )
878
880
}
879
881
880
- /// Copies the data from an operand to a place. This does not support transmuting!
881
- /// Use `copy_op_transmute` if the layouts could disagree.
882
+ /// Copies the data from an operand to a place.
883
+ /// `allow_transmute` indicates whether the layouts may disagree.
882
884
/// Also, if you use this you are responsible for validating that things get copied at the
883
885
/// right type.
884
886
#[ instrument( skip( self ) , level = "debug" ) ]
885
887
fn copy_op_no_validate (
886
888
& mut self ,
887
889
src : & OpTy < ' tcx , M :: PointerTag > ,
888
890
dest : & PlaceTy < ' tcx , M :: PointerTag > ,
891
+ allow_transmute : bool ,
889
892
) -> InterpResult < ' tcx > {
890
893
// We do NOT compare the types for equality, because well-typed code can
891
894
// actually "transmute" `&mut T` to `&T` in an assignment without a cast.
892
- if !mir_assign_valid_types ( * self . tcx , self . param_env , src. layout , dest. layout ) {
895
+ let layout_compat =
896
+ mir_assign_valid_types ( * self . tcx , self . param_env , src. layout , dest. layout ) ;
897
+ if !allow_transmute && !layout_compat {
893
898
span_bug ! (
894
899
self . cur_span( ) ,
895
900
"type mismatch when copying!\n src: {:?},\n dest: {:?}" ,
@@ -898,82 +903,55 @@ where
898
903
) ;
899
904
}
900
905
901
- // Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
906
+ // Let us see if the layout is simple so we take a shortcut,
907
+ // avoid force_allocation.
902
908
let src = match self . read_immediate_raw ( src, /*force*/ false ) ? {
903
909
Ok ( src_val) => {
904
910
assert ! ( !src. layout. is_unsized( ) , "cannot have unsized immediates" ) ;
911
+ assert ! (
912
+ !dest. layout. is_unsized( ) ,
913
+ "the src is sized, so the dest must also be sized"
914
+ ) ;
915
+ assert_eq ! ( src. layout. size, dest. layout. size) ;
905
916
// Yay, we got a value that we can write directly.
906
- return self . write_immediate_no_validate ( * src_val, dest) ;
917
+ return if layout_compat {
918
+ self . write_immediate_no_validate ( * src_val, dest)
919
+ } else {
920
+ // This is tricky. The problematic case is `ScalarPair`: the `src_val` was
921
+ // loaded using the offsets defined by `src.layout`. When we put this back into
922
+ // the destination, we have to use the same offsets! So (a) we make sure we
923
+ // write back to memory, and (b) we use `dest` *with the source layout*.
924
+ let dest_mem = self . force_allocation ( dest) ?;
925
+ self . write_immediate_to_mplace_no_validate (
926
+ * src_val,
927
+ src. layout ,
928
+ dest_mem. align ,
929
+ * dest_mem,
930
+ )
931
+ } ;
907
932
}
908
933
Err ( mplace) => mplace,
909
934
} ;
910
935
// Slow path, this does not fit into an immediate. Just memcpy.
911
936
trace ! ( "copy_op: {:?} <- {:?}: {}" , * dest, src, dest. layout. ty) ;
912
937
913
- let dest = self . force_allocation ( dest) ?;
938
+ let dest = self . force_allocation ( & dest) ?;
914
939
let Some ( ( dest_size, _) ) = self . size_and_align_of_mplace ( & dest) ? else {
915
940
span_bug ! ( self . cur_span( ) , "copy_op needs (dynamically) sized values" )
916
941
} ;
917
942
if cfg ! ( debug_assertions) {
918
943
let src_size = self . size_and_align_of_mplace ( & src) ?. unwrap ( ) . 0 ;
919
944
assert_eq ! ( src_size, dest_size, "Cannot copy differently-sized data" ) ;
945
+ } else {
946
+ // As a cheap approximation, we compare the fixed parts of the size.
947
+ assert_eq ! ( src. layout. size, dest. layout. size) ;
920
948
}
921
949
922
950
self . mem_copy (
923
951
src. ptr , src. align , dest. ptr , dest. align , dest_size, /*nonoverlapping*/ false ,
924
952
)
925
953
}
926
954
927
- /// Copies the data from an operand to a place. The layouts may disagree, but they must
928
- /// have the same size.
929
- pub fn copy_op_transmute (
930
- & mut self ,
931
- src : & OpTy < ' tcx , M :: PointerTag > ,
932
- dest : & PlaceTy < ' tcx , M :: PointerTag > ,
933
- ) -> InterpResult < ' tcx > {
934
- if mir_assign_valid_types ( * self . tcx , self . param_env , src. layout , dest. layout ) {
935
- // Fast path: Just use normal `copy_op`. This is faster because it tries
936
- // `read_immediate_raw` first before doing `force_allocation`.
937
- return self . copy_op ( src, dest) ;
938
- }
939
- // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want
940
- // to avoid that here.
941
- assert ! (
942
- !src. layout. is_unsized( ) && !dest. layout. is_unsized( ) ,
943
- "Cannot transmute unsized data"
944
- ) ;
945
- // We still require the sizes to match.
946
- if src. layout . size != dest. layout . size {
947
- span_bug ! (
948
- self . cur_span( ) ,
949
- "size-changing transmute, should have been caught by transmute checking: {:#?}\n dest: {:#?}" ,
950
- src,
951
- dest
952
- ) ;
953
- }
954
-
955
- // The hard case is `ScalarPair`. `src` is already read from memory in this case,
956
- // using `src.layout` to figure out which bytes to use for the 1st and 2nd field.
957
- // We have to write them to `dest` at the offsets they were *read at*, which is
958
- // not necessarily the same as the offsets in `dest.layout`!
959
- // Hence we do the copy with the source layout on both sides. We also make sure to write
960
- // into memory, because if `dest` is a local we would not even have a way to write
961
- // at the `src` offsets; the fact that we came from a different layout would
962
- // just be lost.
963
- let dest = self . force_allocation ( dest) ?;
964
- self . copy_op_no_validate (
965
- src,
966
- & PlaceTy :: from ( MPlaceTy { mplace : * dest, layout : src. layout , align : dest. align } ) ,
967
- ) ?;
968
-
969
- if M :: enforce_validity ( self ) {
970
- // Data got changed, better make sure it matches the type!
971
- self . validate_operand ( & dest. into ( ) ) ?;
972
- }
973
-
974
- Ok ( ( ) )
975
- }
976
-
977
955
/// Ensures that a place is in memory, and returns where it is.
978
956
/// If the place currently refers to a local that doesn't yet have a matching allocation,
979
957
/// create such an allocation.
@@ -997,18 +975,23 @@ where
997
975
if local_layout. is_unsized ( ) {
998
976
throw_unsup_format ! ( "unsized locals are not supported" ) ;
999
977
}
1000
- let mplace = self . allocate ( local_layout, MemoryKind :: Stack ) ?;
978
+ let mplace = * self . allocate ( local_layout, MemoryKind :: Stack ) ?;
1001
979
if !matches ! ( local_val, Immediate :: Uninit ) {
1002
980
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
1003
981
// We don't have to validate as we can assume the local
1004
982
// was already valid for its type.
1005
- self . write_immediate_to_mplace_no_validate ( local_val, & mplace) ?;
983
+ self . write_immediate_to_mplace_no_validate (
984
+ local_val,
985
+ local_layout,
986
+ local_layout. align . abi ,
987
+ mplace,
988
+ ) ?;
1006
989
}
1007
990
// Now we can call `access_mut` again, asserting it goes well,
1008
991
// and actually overwrite things.
1009
992
* M :: access_local_mut ( self , frame, local) . unwrap ( ) =
1010
- Operand :: Indirect ( * mplace) ;
1011
- * mplace
993
+ Operand :: Indirect ( mplace) ;
994
+ mplace
1012
995
}
1013
996
& mut Operand :: Indirect ( mplace) => mplace, // this already was an indirect local
1014
997
}
0 commit comments