add write_box_via_move intrinsic and use it for vec!
This allows us to get rid of box_new entirely
This commit is contained in:
parent
93d45480aa
commit
5e65109f21
81 changed files with 1097 additions and 1400 deletions
|
|
@ -479,23 +479,6 @@ unsafe impl const Allocator for Global {
|
|||
}
|
||||
}
|
||||
|
||||
/// The allocator for `Box`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `size` and `align` must satisfy the conditions in [`Layout::from_size_align`].
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[lang = "exchange_malloc"]
|
||||
#[inline]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub(crate) unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
|
||||
match Global.allocate(layout) {
|
||||
Ok(ptr) => ptr.as_mut_ptr(),
|
||||
Err(_) => handle_alloc_error(layout),
|
||||
}
|
||||
}
|
||||
|
||||
// # Allocation error handler
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ use core::task::{Context, Poll};
|
|||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use crate::alloc::handle_alloc_error;
|
||||
use crate::alloc::{AllocError, Allocator, Global, Layout, exchange_malloc};
|
||||
use crate::alloc::{AllocError, Allocator, Global, Layout};
|
||||
use crate::raw_vec::RawVec;
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use crate::str::from_boxed_utf8_unchecked;
|
||||
|
|
@ -236,14 +236,34 @@ pub struct Box<
|
|||
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
|
||||
>(Unique<T>, A);
|
||||
|
||||
/// Constructs a `Box<T>` by calling the `exchange_malloc` lang item and moving the argument into
|
||||
/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies.
|
||||
/// Monomorphic function for allocating an uninit `Box`.
|
||||
///
|
||||
/// This is the surface syntax for `box <expr>` expressions.
|
||||
/// # Safety
|
||||
///
|
||||
/// size and align need to be safe for `Layout::from_size_align_unchecked`.
|
||||
#[inline]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
unsafe fn box_new_uninit(size: usize, align: usize) -> *mut u8 {
|
||||
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
|
||||
match Global.allocate(layout) {
|
||||
Ok(ptr) => ptr.as_mut_ptr(),
|
||||
Err(_) => handle_alloc_error(layout),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for `vec!`.
|
||||
///
|
||||
/// This is unsafe, but has to be marked as safe or else we couldn't use it in `vec!`.
|
||||
#[doc(hidden)]
|
||||
#[rustc_intrinsic]
|
||||
#[unstable(feature = "liballoc_internals", issue = "none")]
|
||||
pub fn box_new<T>(x: T) -> Box<T>;
|
||||
#[inline(always)]
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
pub fn box_assume_init_into_vec_unsafe<T, const N: usize>(
|
||||
b: Box<MaybeUninit<[T; N]>>,
|
||||
) -> crate::vec::Vec<T> {
|
||||
unsafe { (b.assume_init() as Box<[T]>).into_vec() }
|
||||
}
|
||||
|
||||
impl<T> Box<T> {
|
||||
/// Allocates memory on the heap and then places `x` into it.
|
||||
|
|
@ -262,9 +282,10 @@ impl<T> Box<T> {
|
|||
#[rustc_diagnostic_item = "box_new"]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn new(x: T) -> Self {
|
||||
// SAFETY: the size and align of a valid type `T` are always valid for `Layout`.
|
||||
// This is `Box::new_uninit` but inlined to avoid build time regressions.
|
||||
// SAFETY: The size and align of a valid type `T` are always valid for `Layout`.
|
||||
let ptr = unsafe {
|
||||
exchange_malloc(<T as SizedTypeProperties>::SIZE, <T as SizedTypeProperties>::ALIGN)
|
||||
box_new_uninit(<T as SizedTypeProperties>::SIZE, <T as SizedTypeProperties>::ALIGN)
|
||||
} as *mut T;
|
||||
// Nothing below can panic so we do not have to worry about deallocating `ptr`.
|
||||
// SAFETY: we just allocated the box to store `x`.
|
||||
|
|
@ -288,9 +309,21 @@ impl<T> Box<T> {
|
|||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "new_uninit", since = "1.82.0")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
|
||||
Self::new_uninit_in(Global)
|
||||
// This is the same as `Self::new_uninit_in(Global)`, but manually inlined (just like
|
||||
// `Box::new`).
|
||||
|
||||
// SAFETY:
|
||||
// - The size and align of a valid type `T` are always valid for `Layout`.
|
||||
// - If `allocate` succeeds, the returned pointer exactly matches what `Box` needs.
|
||||
unsafe {
|
||||
mem::transmute(box_new_uninit(
|
||||
<T as SizedTypeProperties>::SIZE,
|
||||
<T as SizedTypeProperties>::ALIGN,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new `Box` with uninitialized contents, with the memory
|
||||
|
|
@ -1150,10 +1183,12 @@ impl<T, A: Allocator> Box<mem::MaybeUninit<T>, A> {
|
|||
/// assert_eq!(*five, 5)
|
||||
/// ```
|
||||
#[stable(feature = "new_uninit", since = "1.82.0")]
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub unsafe fn assume_init(self) -> Box<T, A> {
|
||||
let (raw, alloc) = Box::into_raw_with_allocator(self);
|
||||
unsafe { Box::from_raw_in(raw as *mut T, alloc) }
|
||||
// This is used in the `vec!` macro, so we optimize for minimal IR generation
|
||||
// even in debug builds.
|
||||
// SAFETY: `Box<T>` and `Box<MaybeUninit<T>>` have the same layout.
|
||||
unsafe { core::intrinsics::transmute_unchecked(self) }
|
||||
}
|
||||
|
||||
/// Writes the value and converts to `Box<T, A>`.
|
||||
|
|
|
|||
15
library/alloc/src/intrinsics.rs
Normal file
15
library/alloc/src/intrinsics.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//! Intrinsics that cannot be moved to `core` because they depend on `alloc` types.
|
||||
#![unstable(feature = "liballoc_internals", issue = "none")]
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::boxed::Box;
|
||||
|
||||
/// Writes `x` into `b`.
|
||||
///
|
||||
/// This is needed for `vec!`, which can't afford any extra copies of the argument (or else debug
|
||||
/// builds regress), has to be written fully as a call chain without `let` (or else this breaks inference
|
||||
/// of e.g. unsizing coercions), and can't use an `unsafe` block as that would then also
|
||||
/// include the user-provided `$x`.
|
||||
#[rustc_intrinsic]
|
||||
pub fn write_box_via_move<T>(b: Box<MaybeUninit<T>>, x: T) -> Box<MaybeUninit<T>>;
|
||||
|
|
@ -224,6 +224,7 @@ pub mod collections;
|
|||
#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))]
|
||||
pub mod ffi;
|
||||
pub mod fmt;
|
||||
pub mod intrinsics;
|
||||
#[cfg(not(no_rc))]
|
||||
pub mod rc;
|
||||
pub mod slice;
|
||||
|
|
|
|||
|
|
@ -47,10 +47,16 @@ macro_rules! vec {
|
|||
$crate::vec::from_elem($elem, $n)
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
<[_]>::into_vec(
|
||||
// Using the intrinsic produces a dramatic improvement in stack usage for
|
||||
// unoptimized programs using this code path to construct large Vecs.
|
||||
$crate::boxed::box_new([$($x),+])
|
||||
// Using `write_box_via_move` produces a dramatic improvement in stack usage for unoptimized
|
||||
// programs using this code path to construct large Vecs. We can't use `write_via_move`
|
||||
// because this entire invocation has to remain a call chain without `let` bindings, or else
|
||||
// inference and temporary lifetimes change and things break (see `vec-macro-rvalue-scope`,
|
||||
// `vec-macro-coercions`, and `autoderef-vec-box-fn-36786` tests).
|
||||
//
|
||||
// `box_assume_init_into_vec_unsafe` isn't actually safe but the way we use it here is. We
|
||||
// can't use an unsafe block as that would also wrap `$x`.
|
||||
$crate::boxed::box_assume_init_into_vec_unsafe(
|
||||
$crate::intrinsics::write_box_via_move($crate::boxed::Box::new_uninit(), [$($x),+])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue