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
This commit is contained in:
bors 2019-12-30 08:24:22 +00:00
commit 580ac0b4f1
2 changed files with 45 additions and 25 deletions

View File

@ -594,6 +594,14 @@ pub struct AllocationDefinedness {
ranges: smallvec::SmallVec<[u64; 1]>,
}
impl AllocationDefinedness {
pub fn all_bytes_undef(&self) -> bool {
// The `ranges` are run-length encoded and of alternating definedness.
// So if `ranges.len() > 1` then the second block is a range of defined.
self.initial == false && self.ranges.len() == 1
}
}
/// Transferring the definedness mask to other allocations.
impl<Tag, Extra> Allocation<Tag, Extra> {
/// Creates a run-length encoding of the undef mask.

View File

@ -841,11 +841,38 @@ pub fn copy_repeatedly(
let tcx = self.tcx.tcx;
// The bits have to be saved locally before writing to dest in case src and dest overlap.
assert_eq!(size.bytes() as usize as u64, size.bytes());
// This checks relocation edges on the src.
let src_bytes =
self.get_raw(src.alloc_id)?.get_bytes_with_undef_and_ptr(&tcx, src, size)?.as_ptr();
let dest_bytes =
self.get_raw_mut(dest.alloc_id)?.get_bytes_mut(&tcx, dest, size * length)?.as_mut_ptr();
self.get_raw_mut(dest.alloc_id)?.get_bytes_mut(&tcx, dest, size * length)?;
// If `dest_bytes` is empty we just optimize to not run anything for zsts.
// See #67539
if dest_bytes.is_empty() {
return Ok(());
}
let dest_bytes = dest_bytes.as_mut_ptr();
// Prepare a copy of the undef mask.
let compressed = self.get_raw(src.alloc_id)?.compress_undef_range(src, size);
if compressed.all_bytes_undef() {
// Fast path: If all bytes are `undef` then there is nothing to copy. The target range
// is marked as undef but we otherwise omit changing the byte representation which may
// be arbitrary for undef bytes.
// This also avoids writing to the target bytes so that the backing allocation is never
// touched if the bytes stay undef for the whole interpreter execution. On contemporary
// operating system this can avoid physically allocating the page.
let dest_alloc = self.get_raw_mut(dest.alloc_id)?;
dest_alloc.mark_definedness(dest, size * length, false);
dest_alloc.mark_relocation_range(relocations);
return Ok(());
}
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
// behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
@ -881,8 +908,14 @@ pub fn copy_repeatedly(
}
}
// copy definedness to the destination
self.copy_undef_mask(src, dest, size, length)?;
// now fill in all the data
self.get_raw_mut(dest.alloc_id)?.mark_compressed_undef_range(
&compressed,
dest,
size,
length,
);
// copy the relocations to the destination
self.get_raw_mut(dest.alloc_id)?.mark_relocation_range(relocations);
@ -890,29 +923,8 @@ pub fn copy_repeatedly(
}
}
/// Undefined bytes
/// Machine pointer introspection.
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
// FIXME: Add a fast version for the common, nonoverlapping case
fn copy_undef_mask(
&mut self,
src: Pointer<M::PointerTag>,
dest: Pointer<M::PointerTag>,
size: Size,
repeat: u64,
) -> InterpResult<'tcx> {
// The bits have to be saved locally before writing to dest in case src and dest overlap.
assert_eq!(size.bytes() as usize as u64, size.bytes());
let src_alloc = self.get_raw(src.alloc_id)?;
let compressed = src_alloc.compress_undef_range(src, size);
// now fill in all the data
let dest_allocation = self.get_raw_mut(dest.alloc_id)?;
dest_allocation.mark_compressed_undef_range(&compressed, dest, size, repeat);
Ok(())
}
pub fn force_ptr(
&self,
scalar: Scalar<M::PointerTag>,