From deea7a91c905b43fbe548126cf389e894b3b72eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 Oct 2025 14:27:49 +0200 Subject: [PATCH 1/2] perf experiment: try to replace box_new with write_via_move --- library/alloc/src/boxed.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ae43fbfe1d69e..1416af0f500f2 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -259,7 +259,12 @@ impl Box { #[rustc_diagnostic_item = "box_new"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new(x: T) -> Self { - return box_new(x); + let mut b = Box::new_uninit(); + let ptr = mem::MaybeUninit::as_mut_ptr(&mut *b); + // SAFETY: we just allocated the box to store `x`. + unsafe { core::intrinsics::write_via_move(ptr, x) }; + // SAFETY: we just initialized `b`. + unsafe { b.assume_init() } } /// Constructs a new box with uninitialized contents. From 4bda0704818afdab910fb7ffa7a00bfdd07fc0bc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 Oct 2025 22:32:25 +0200 Subject: [PATCH 2/2] crazy hacks to try and do vec! without box_new --- .../rustc_hir_analysis/src/check/intrinsic.rs | 15 ++++++++++++++- .../src/lower_intrinsics.rs | 6 ++++++ library/alloc/src/boxed.rs | 18 ++++++++++-------- library/alloc/src/lib.rs | 1 + library/alloc/src/macros.rs | 18 ++++++++++++++---- library/core/src/intrinsics/mod.rs | 10 +++++++--- library/core/src/ptr/mod.rs | 5 ++++- 7 files changed, 56 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index a6659912e3fb9..67bbc5ca6b299 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -215,6 +215,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub + | sym::write_via_move // tidy-alphabetical-end => hir::Safety::Safe, _ => hir::Safety::Unsafe, @@ -551,7 +552,19 @@ pub(crate) fn check_intrinsic_type( sym::read_via_copy => (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), sym::write_via_move => { - (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) + // The lifetime for argument and return type. + let lft = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }; + let lft = ty::Region::new_bound(tcx, ty::INNERMOST, lft); + + let t = param(0); + let mut_ref_t = Ty::new_mut_ref(tcx, lft, t); + // MaybeUninit + // let maybe_uninit = tcx.require_lang_item(LangItem::MaybeUninit, span); + // let maybe_uninit = tcx.adt_def(maybe_uninit); + // let args = tcx.mk_args_from_iter([GenericArg::from(t)].into_iter()); + // let maybe_uninit_t = Ty::new_adt(tcx, maybe_uninit, args); + + (1, 0, vec![mut_ref_t, t], mut_ref_t) } sym::typed_swap_nonoverlapping => { diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index fc8092fd5832b..efde55fbc796f 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -210,6 +210,12 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { terminator.source_info, StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))), )); + // The intrinsic returns the argument pointer. + block.statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new((*destination, Rvalue::Use(ptr.node)))), + )); + terminator.kind = TerminatorKind::Goto { target }; } sym::discriminant_value => { diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1416af0f500f2..bd4ed6354fdf4 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -233,14 +233,14 @@ pub struct Box< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, >(Unique, A); -/// Constructs a `Box` by calling the `exchange_malloc` lang item and moving the argument into -/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies. -/// -/// This is the surface syntax for `box ` expressions. +/// This function is not actually safe, it has the preconditions of `Box::from_raw`. +/// It is meant for the `vec!` macro where we can't use an unsafe block as that would also +/// wrap the user-defined `$x`. #[doc(hidden)] -#[rustc_intrinsic] #[unstable(feature = "liballoc_internals", issue = "none")] -pub fn box_new(x: T) -> Box; +pub fn vec_macro_slice_helper_unsafe(b: &mut [T; N]) -> Box<[T]> { + unsafe { Box::from_raw(b) } +} impl Box { /// Allocates memory on the heap and then places `x` into it. @@ -261,8 +261,10 @@ impl Box { pub fn new(x: T) -> Self { let mut b = Box::new_uninit(); let ptr = mem::MaybeUninit::as_mut_ptr(&mut *b); - // SAFETY: we just allocated the box to store `x`. - unsafe { core::intrinsics::write_via_move(ptr, x) }; + // SAFETY: We rely on the unstable property of the Rust semantics that references don't + // require the pointee to be valid (if the pointee is inhabited, which we know it is). + let dest = unsafe { &mut *ptr }; + core::intrinsics::write_via_move(dest, x); // SAFETY: we just initialized `b`. unsafe { b.assume_init() } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 87ad5b0ce30e6..d7b030028e15d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -235,4 +235,5 @@ pub mod wtf8; pub mod __export { pub use core::format_args; pub use core::hint::must_use; + pub use core::intrinsics::write_via_move; } diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 1e6e2ae8c3675..b4ba2428a9ef1 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -38,7 +38,7 @@ #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_macro"] -#[allow_internal_unstable(rustc_attrs, liballoc_internals)] +#[allow_internal_unstable(rustc_attrs, liballoc_internals, core_intrinsics)] macro_rules! vec { () => ( $crate::vec::Vec::new() @@ -48,9 +48,19 @@ macro_rules! vec { ); ($($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 the `write_via_move` intrinsic produces a dramatic improvement in stack usage + // for unoptimized programs using this code path to construct large Vecs. + $crate::boxed::vec_macro_slice_helper_unsafe($crate::__export::write_via_move( + // SAFETY: We rely on the unstable property of the Rust semantics that references + // don't require the pointee to be valid (if the pointee is inhabited, which we know + // it is). + // We also can't let-bind this as that would change teh temporary scope behavior of + // the macro. + unsafe { + $crate::boxed::Box::leak($crate::boxed::Box::new_uninit()).assume_init_mut() + }, + [$($x),+], + )) ) ); } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c397e762d5589..7e5e675776137 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2168,8 +2168,12 @@ pub const unsafe fn unchecked_funnel_shr( #[rustc_intrinsic] pub const unsafe fn read_via_copy(ptr: *const T) -> T; -/// This is an implementation detail of [`crate::ptr::write`] and should -/// not be used anywhere else. See its comments for why this exists. +/// This is an implementation detail of [`crate::ptr::write`] and the `vec!` macro and should not be +/// used anywhere else. See its comments for why this exists. +/// +/// The signature is very carefully chosen to make the intrinsic safe, and to allow it to be used +/// in `vec!` in tail position. `ptr` does not *actually* have to point to something already +/// initializes. /// /// This intrinsic can *only* be called where the pointer is a local without /// projections (`write_via_move(ptr, x)`, not `write_via_move(*ptr, x)`) so @@ -2177,7 +2181,7 @@ pub const unsafe fn read_via_copy(ptr: *const T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const unsafe fn write_via_move(ptr: *mut T, value: T); +pub const fn write_via_move(ptr: &mut T, value: T) -> &mut T; /// Returns the value of the discriminant for the variant in 'v'; /// if `T` has no discriminant, returns `0`. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index fd067d19fcd98..1ffdc0546ccd1 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1941,7 +1941,10 @@ pub const unsafe fn write(dst: *mut T, src: T) { is_zst: bool = T::IS_ZST, ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst) ); - intrinsics::write_via_move(dst, src) + // We rely on the unstable property of the Rust semantics that references don't + // require the pointee to be valid (if the pointee is inhabited, which we know it is). + let ptr = &mut *dst; + intrinsics::write_via_move(ptr, src); } }