@@ -841,11 +841,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
841841
842842 let tcx = self . tcx . tcx ;
843843
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+
844847 // This checks relocation edges on the src.
845848 let src_bytes =
846849 self . get_raw ( src. alloc_id ) ?. get_bytes_with_undef_and_ptr ( & tcx, src, size) ?. as_ptr ( ) ;
847850 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+ }
849876
850877 // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
851878 // 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> {
881908 }
882909 }
883910
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+
886919 // copy the relocations to the destination
887920 self . get_raw_mut ( dest. alloc_id ) ?. mark_relocation_range ( relocations) ;
888921
889922 Ok ( ( ) )
890923 }
891924}
892925
893- /// Undefined bytes
926+ /// Machine pointer introspection.
894927impl < ' 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-
916928 pub fn force_ptr (
917929 & self ,
918930 scalar : Scalar < M :: PointerTag > ,
0 commit comments