Skip to content

Commit 580ac0b

Browse files
committed
Auto merge of #67658 - spastorino:do-not-copy-zsts, r=oli-obk
Avoid memory copy logic for zsts r? @oli-obk One of the included commits is work done by @HeroicKatora in #62655
2 parents d297b19 + 250a636 commit 580ac0b

File tree

2 files changed

+45
-25
lines changed

2 files changed

+45
-25
lines changed

src/librustc/mir/interpret/allocation.rs

+8
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,14 @@ pub struct AllocationDefinedness {
594594
ranges: smallvec::SmallVec<[u64; 1]>,
595595
}
596596

597+
impl AllocationDefinedness {
598+
pub fn all_bytes_undef(&self) -> bool {
599+
// The `ranges` are run-length encoded and of alternating definedness.
600+
// So if `ranges.len() > 1` then the second block is a range of defined.
601+
self.initial == false && self.ranges.len() == 1
602+
}
603+
}
604+
597605
/// Transferring the definedness mask to other allocations.
598606
impl<Tag, Extra> Allocation<Tag, Extra> {
599607
/// Creates a run-length encoding of the undef mask.

src/librustc_mir/interpret/memory.rs

+37-25
Original file line numberDiff line numberDiff line change
@@ -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.
894927
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-
916928
pub fn force_ptr(
917929
&self,
918930
scalar: Scalar<M::PointerTag>,

0 commit comments

Comments
 (0)