@@ -841,11 +841,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
841
841
842
842
let tcx = self . tcx . tcx ;
843
843
844
+ // The bits have to be saved locally before writing to dest in case src and dest overlap.
845
+ assert_eq ! ( size. bytes( ) as usize as u64 , size. bytes( ) ) ;
846
+
844
847
// This checks relocation edges on the src.
845
848
let src_bytes =
846
849
self . get_raw ( src. alloc_id ) ?. get_bytes_with_undef_and_ptr ( & tcx, src, size) ?. as_ptr ( ) ;
847
850
let dest_bytes =
848
- self . get_raw_mut ( dest. alloc_id ) ?. get_bytes_mut ( & tcx, dest, size * length) ?. as_mut_ptr ( ) ;
851
+ self . get_raw_mut ( dest. alloc_id ) ?. get_bytes_mut ( & tcx, dest, size * length) ?;
852
+
853
+ // If `dest_bytes` is empty we just optimize to not run anything for zsts.
854
+ // See #67539
855
+ if dest_bytes. is_empty ( ) {
856
+ return Ok ( ( ) ) ;
857
+ }
858
+
859
+ let dest_bytes = dest_bytes. as_mut_ptr ( ) ;
860
+
861
+ // Prepare a copy of the undef mask.
862
+ let compressed = self . get_raw ( src. alloc_id ) ?. compress_undef_range ( src, size) ;
863
+
864
+ if compressed. all_bytes_undef ( ) {
865
+ // Fast path: If all bytes are `undef` then there is nothing to copy. The target range
866
+ // is marked as undef but we otherwise omit changing the byte representation which may
867
+ // be arbitrary for undef bytes.
868
+ // This also avoids writing to the target bytes so that the backing allocation is never
869
+ // touched if the bytes stay undef for the whole interpreter execution. On contemporary
870
+ // operating system this can avoid physically allocating the page.
871
+ let dest_alloc = self . get_raw_mut ( dest. alloc_id ) ?;
872
+ dest_alloc. mark_definedness ( dest, size * length, false ) ;
873
+ dest_alloc. mark_relocation_range ( relocations) ;
874
+ return Ok ( ( ) ) ;
875
+ }
849
876
850
877
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
851
878
// behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
@@ -881,38 +908,23 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
881
908
}
882
909
}
883
910
884
- // copy definedness to the destination
885
- self . copy_undef_mask ( src, dest, size, length) ?;
911
+ // now fill in all the data
912
+ self . get_raw_mut ( dest. alloc_id ) ?. mark_compressed_undef_range (
913
+ & compressed,
914
+ dest,
915
+ size,
916
+ length,
917
+ ) ;
918
+
886
919
// copy the relocations to the destination
887
920
self . get_raw_mut ( dest. alloc_id ) ?. mark_relocation_range ( relocations) ;
888
921
889
922
Ok ( ( ) )
890
923
}
891
924
}
892
925
893
- /// Undefined bytes
926
+ /// Machine pointer introspection.
894
927
impl < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > Memory < ' mir , ' tcx , M > {
895
- // FIXME: Add a fast version for the common, nonoverlapping case
896
- fn copy_undef_mask (
897
- & mut self ,
898
- src : Pointer < M :: PointerTag > ,
899
- dest : Pointer < M :: PointerTag > ,
900
- size : Size ,
901
- repeat : u64 ,
902
- ) -> InterpResult < ' tcx > {
903
- // The bits have to be saved locally before writing to dest in case src and dest overlap.
904
- assert_eq ! ( size. bytes( ) as usize as u64 , size. bytes( ) ) ;
905
-
906
- let src_alloc = self . get_raw ( src. alloc_id ) ?;
907
- let compressed = src_alloc. compress_undef_range ( src, size) ;
908
-
909
- // now fill in all the data
910
- let dest_allocation = self . get_raw_mut ( dest. alloc_id ) ?;
911
- dest_allocation. mark_compressed_undef_range ( & compressed, dest, size, repeat) ;
912
-
913
- Ok ( ( ) )
914
- }
915
-
916
928
pub fn force_ptr (
917
929
& self ,
918
930
scalar : Scalar < M :: PointerTag > ,
0 commit comments