From de91157389c4cf63ee6805e8490d0872fe2a7b33 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 00:23:19 +0200 Subject: [PATCH 1/6] add Box::try_new_zeroed_slice() Currently there is no API that allows fallible zero-allocation of a Vec. Vec.try_reserve is not appropriate for this job since it doesn't know whether it should zero or arbitrary uninitialized memory is fine. Since Box currently holds most of the zeroing/uninit/slice allocation APIs it's the best place to add yet another entry into this feature matrix. --- library/alloc/src/boxed.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 53bfe02d0e7a3..e09cc2271054e 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -589,6 +589,38 @@ impl Box<[T]> { pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } } + + /// Constructs a new boxed slice with uninitialized contents, with the memory + /// being filled with `0` bytes. Returns an error if the allocation fails + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// let values = Box::<[u32]>::try_new_zeroed_slice(3)?; + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_zeroed_slice(len: usize) -> Result]>, AllocError> { + unsafe { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + let ptr = Global.allocate_zeroed(layout)?; + Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len)) + } + } } impl Box<[T], A> { From 83b01b9f1ac0ee605271286ddb31b6f9701e1f89 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 00:24:31 +0200 Subject: [PATCH 2/6] use zeroed allocation instead of eagerly initializing the memory --- compiler/rustc_middle/src/lib.rs | 2 ++ compiler/rustc_middle/src/mir/interpret/allocation.rs | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index f2acc601d4f62..90bdf630d062d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -23,6 +23,7 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] #![feature(backtrace)] @@ -33,6 +34,7 @@ #![feature(discriminant_kind)] #![feature(never_type)] #![feature(extern_types)] +#![feature(new_uninit)] #![feature(nll)] #![feature(once_cell)] #![feature(min_specialization)] diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index a29b42b45df37..a503c86c59b0e 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -131,8 +131,7 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. pub fn uninit(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'static, Self> { - let mut bytes = Vec::new(); - bytes.try_reserve(size.bytes_usize()).map_err(|_| { + let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| { // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always // deterministic. However, we can be non-determinstic here because all uses of const @@ -146,7 +145,9 @@ impl Allocation { }); InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) })?; - bytes.resize(size.bytes_usize(), 0); + // SAFETY: This turns a Box<[MaybeUninit]> into a Vec. This is safe since the box + // was zero-allocated which is a valid value for u8. + let bytes = unsafe { bytes.assume_init().to_vec() }; Ok(Allocation { bytes, relocations: Relocations::new(), From f408d4bb8ca382bd981fedb7eb809893271f9859 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 09:32:41 +0200 Subject: [PATCH 3/6] use box->vec conversion API that doesn't reallocate --- compiler/rustc_middle/src/mir/interpret/allocation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index a503c86c59b0e..ada6e09776694 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -147,7 +147,7 @@ impl Allocation { })?; // SAFETY: This turns a Box<[MaybeUninit]> into a Vec. This is safe since the box // was zero-allocated which is a valid value for u8. - let bytes = unsafe { bytes.assume_init().to_vec() }; + let bytes = unsafe { bytes.assume_init().into_vec() }; Ok(Allocation { bytes, relocations: Relocations::new(), From 6ed2d870fc0bb8eaebdbb1cc820b264bb433fdb3 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 19:45:02 +0200 Subject: [PATCH 4/6] remove cfg gate on `use RawVec` since it is now also used in fallible code --- library/alloc/src/boxed.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index e09cc2271054e..6b2f52eb51173 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -157,7 +157,6 @@ use crate::alloc::{handle_alloc_error, WriteCloneIntoRaw}; use crate::alloc::{AllocError, Allocator, Global, Layout}; #[cfg(not(no_global_oom_handling))] use crate::borrow::Cow; -#[cfg(not(no_global_oom_handling))] use crate::raw_vec::RawVec; #[cfg(not(no_global_oom_handling))] use crate::str::from_boxed_utf8_unchecked; From 55def120a4d54123625ad05c4d6384c5044642f1 Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 19:52:08 +0200 Subject: [PATCH 5/6] replace Vec with Box<[u8]> --- compiler/rustc_middle/src/mir/interpret/allocation.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index ada6e09776694..5964efa78e9e9 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -28,7 +28,7 @@ use crate::ty; pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. - bytes: Vec, + bytes: Box<[u8]>, /// Maps from byte addresses to extra data for each pointer. /// Only the first byte of a pointer is inserted into the map; i.e., /// every entry in this map applies to `pointer_size` consecutive bytes starting @@ -112,7 +112,7 @@ impl Allocation { align: Align, mutability: Mutability, ) -> Self { - let bytes = slice.into().into_owned(); + let bytes = Box::<[u8]>::from(slice.into()); let size = Size::from_bytes(bytes.len()); Self { bytes, @@ -145,9 +145,8 @@ impl Allocation { }); InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) })?; - // SAFETY: This turns a Box<[MaybeUninit]> into a Vec. This is safe since the box - // was zero-allocated which is a valid value for u8. - let bytes = unsafe { bytes.assume_init().into_vec() }; + // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]> + let bytes = unsafe { bytes.assume_init() }; Ok(Allocation { bytes, relocations: Relocations::new(), From 1c21373b50f39269f553e736933c70841f419d2e Mon Sep 17 00:00:00 2001 From: The8472 Date: Thu, 5 Aug 2021 21:21:52 +0200 Subject: [PATCH 6/6] add Box::try_new_uninit_slice for symmetry --- library/alloc/src/boxed.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 6b2f52eb51173..722168523763a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -589,6 +589,39 @@ impl Box<[T]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } } + /// Constructs a new boxed slice with uninitialized contents. Returns an error if + /// the allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// let mut values = Box::<[u32]>::try_new_uninit_slice(3)?; + /// let values = unsafe { + /// // Deferred initialization: + /// values[0].as_mut_ptr().write(1); + /// values[1].as_mut_ptr().write(2); + /// values[2].as_mut_ptr().write(3); + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_uninit_slice(len: usize) -> Result]>, AllocError> { + unsafe { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + let ptr = Global.allocate(layout)?; + Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len)) + } + } + /// Constructs a new boxed slice with uninitialized contents, with the memory /// being filled with `0` bytes. Returns an error if the allocation fails ///