mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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.
This commit is contained in:
+18
-7
@@ -289,6 +289,7 @@ struct RcInner<T: ?Sized> {
|
||||
}
|
||||
|
||||
/// Calculate layout for `RcInner<T>` 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,25 @@ impl<T: Default> Default for Rc<T> {
|
||||
/// ```
|
||||
#[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(),
|
||||
)
|
||||
let raw = Rc::get_mut_unchecked(&mut rc);
|
||||
|
||||
// Note that `ptr::write` here is used specifically instead of
|
||||
// `MaybeUninit::write` to avoid creating an extra stack copy of `T`
|
||||
// in debug mode. See #136043 for more context.
|
||||
ptr::write(raw.as_mut_ptr(), T::default());
|
||||
}
|
||||
|
||||
// SAFETY: this allocation was just initialized above.
|
||||
unsafe { rc.assume_init() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-11
@@ -392,6 +392,7 @@ struct ArcInner<T: ?Sized> {
|
||||
}
|
||||
|
||||
/// Calculate layout for `ArcInner<T>` 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,25 @@ impl<T: Default> Default for Arc<T> {
|
||||
/// assert_eq!(*x, 0);
|
||||
/// ```
|
||||
fn default() -> Arc<T> {
|
||||
// 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(),
|
||||
)
|
||||
let raw = Arc::get_mut_unchecked(&mut arc);
|
||||
|
||||
// Note that `ptr::write` here is used specifically instead of
|
||||
// `MaybeUninit::write` to avoid creating an extra stack copy of `T`
|
||||
// in debug mode. See #136043 for more context.
|
||||
ptr::write(raw.as_mut_ptr(), T::default());
|
||||
}
|
||||
|
||||
// SAFETY: this allocation was just initialized above.
|
||||
unsafe { arc.assume_init() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
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_from_array(x: u64) -> Arc<[u64]> {
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user