From ba5ff3d22d04ae0cc09d4e0cbb6cdb7517ce9a0e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Feb 2026 14:55:54 -0800 Subject: [PATCH] Simplify internals of `{Rc,Arc}::default` This commit simplifies the internal implementation of `Default` for these two pointer types to have the same performance characteristics as before (a side effect of changes in 131460) while avoid use of internal private APIs of Rc/Arc. To preserve the same codegen as before some non-generic functions needed to be tagged as `#[inline]` as well, but otherwise the same IR is produced before/after this change. The motivation of this commit is I was studying up on the state of initialization of `Arc` and `Rc` and figured it'd be nicer to reduce the use of internal APIs and instead use public stable APIs where possible, even in the implementation itself. --- library/alloc/src/rc.rs | 20 ++++++++++++------- library/alloc/src/sync.rs | 24 ++++++++++++----------- tests/codegen-llvm/issues/issue-111603.rs | 12 ++++++------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index cec41524325e0..4a3f24e6eff82 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -289,6 +289,7 @@ struct RcInner { } /// Calculate layout for `RcInner` using the inner value's layout +#[inline] fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { // Calculate layout using the given value layout. // Previously, layout was calculated on the expression @@ -2518,15 +2519,20 @@ impl Default for Rc { /// ``` #[inline] fn default() -> Self { + // First create an uninitialized allocation before creating an instance + // of `T`. This avoids having `T` on the stack and avoids the need to + // codegen a call to the destructor for `T` leading to generally better + // codegen. See #131460 for some more details. + let mut rc = Rc::new_uninit(); + + // SAFETY: this is a freshly allocated `Rc` so it's guaranteed there are + // no other strong or weak pointers other than `rc` itself. unsafe { - Self::from_inner( - Box::leak(Box::write( - Box::new_uninit(), - RcInner { strong: Cell::new(1), weak: Cell::new(1), value: T::default() }, - )) - .into(), - ) + Rc::get_mut_unchecked(&mut rc).write(T::default()); } + + // SAFETY: this allocation was just initialized above. + unsafe { rc.assume_init() } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index dc82357dd146b..2a0b2ac0fa7c9 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -392,6 +392,7 @@ struct ArcInner { } /// Calculate layout for `ArcInner` using the inner value's layout +#[inline] fn arcinner_layout_for_value_layout(layout: Layout) -> Layout { // Calculate layout using the given value layout. // Previously, layout was calculated on the expression @@ -3724,19 +3725,20 @@ impl Default for Arc { /// assert_eq!(*x, 0); /// ``` fn default() -> Arc { + // First create an uninitialized allocation before creating an instance + // of `T`. This avoids having `T` on the stack and avoids the need to + // codegen a call to the destructor for `T` leading to generally better + // codegen. See #131460 for some more details. + let mut arc = Arc::new_uninit(); + + // SAFETY: this is a freshly allocated `Arc` so it's guaranteed there + // are no other strong or weak pointers other than `arc` itself. unsafe { - Self::from_inner( - Box::leak(Box::write( - Box::new_uninit(), - ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - }, - )) - .into(), - ) + Arc::get_mut_unchecked(&mut arc).write(T::default()); } + + // SAFETY: this allocation was just initialized above. + unsafe { arc.assume_init() } } } diff --git a/tests/codegen-llvm/issues/issue-111603.rs b/tests/codegen-llvm/issues/issue-111603.rs index 2ba5a3f876aed..91eb836478eb1 100644 --- a/tests/codegen-llvm/issues/issue-111603.rs +++ b/tests/codegen-llvm/issues/issue-111603.rs @@ -10,9 +10,9 @@ use std::sync::Arc; pub fn new_from_array(x: u64) -> Arc<[u64]> { // Ensure that we only generate one alloca for the array. - // CHECK: alloca + // CHECK: %[[A:.+]] = alloca // CHECK-SAME: [8000 x i8] - // CHECK-NOT: alloca + // CHECK-NOT: %[[B:.+]] = alloca let array = [x; 1000]; Arc::new(array) } @@ -20,8 +20,9 @@ pub fn new_from_array(x: u64) -> Arc<[u64]> { // CHECK-LABEL: @new_uninit #[no_mangle] pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK: %[[A:.+]] = alloca + // CHECK-SAME: [8000 x i8] + // CHECK-NOT: %[[B:.+]] = alloca let mut arc = Arc::new_uninit(); unsafe { Arc::get_mut_unchecked(&mut arc) }.write([x; 1000]); unsafe { arc.assume_init() } @@ -30,8 +31,7 @@ pub fn new_uninit(x: u64) -> Arc<[u64; 1000]> { // CHECK-LABEL: @new_uninit_slice #[no_mangle] pub fn new_uninit_slice(x: u64) -> Arc<[u64]> { - // CHECK: call alloc::sync::arcinner_layout_for_value_layout - // CHECK-NOT: call alloc::sync::arcinner_layout_for_value_layout + // CHECK-NOT: %[[B:.+]] = alloca let mut arc = Arc::new_uninit_slice(1000); for elem in unsafe { Arc::get_mut_unchecked(&mut arc) } { elem.write(x);