about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Wong <joshuawong@anticentri.st>2025-01-26 03:36:50 -0500
committerJoshua Wong <joshuawong@anticentri.st>2025-01-26 03:48:27 -0500
commit97005678c38fd391c9b502d011cc3f3d4434a18a (patch)
tree1ba4600fb6a72a6a204c9428db946c8f67f20179
parent80faf203515bf3be214ad3ffc019b55b026b1220 (diff)
downloadrust-97005678c38fd391c9b502d011cc3f3d4434a18a.tar.gz
rust-97005678c38fd391c9b502d011cc3f3d4434a18a.zip
reduce `Box::default` stack copies in debug mode
The `Box::new(T::default())` implementation of `Box::default` only
had two stack copies in debug mode, compared to the current version,
which has four. By avoiding creating any `MaybeUninit<T>`'s and just writing
`T` directly to the `Box` pointer, the stack usage in debug mode remains
the same as the old version.
-rw-r--r--library/alloc/src/boxed.rs15
-rw-r--r--tests/codegen/box-default-debug-copies.rs5
2 files changed, 18 insertions, 2 deletions
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 1b5e44a9134..8f43ebc3888 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -1730,7 +1730,20 @@ impl<T: Default> Default for Box<T> {
     /// Creates a `Box<T>`, with the `Default` value for T.
     #[inline]
     fn default() -> Self {
-        Box::write(Box::new_uninit(), T::default())
+        let mut x: Box<mem::MaybeUninit<T>> = Box::new_uninit();
+        unsafe {
+            // SAFETY: `x` is valid for writing and has the same layout as `T`.
+            // If `T::default()` panics, dropping `x` will just deallocate the Box as `MaybeUninit<T>`
+            // does not have a destructor.
+            //
+            // We use `ptr::write` as `MaybeUninit::write` creates
+            // extra stack copies of `T` in debug mode.
+            //
+            // See https://github.com/rust-lang/rust/issues/136043 for more context.
+            ptr::write(&raw mut *x as *mut T, T::default());
+            // SAFETY: `x` was just initialized above.
+            x.assume_init()
+        }
     }
 }
 
diff --git a/tests/codegen/box-default-debug-copies.rs b/tests/codegen/box-default-debug-copies.rs
index ed4e0c416b8..06cc41b21c0 100644
--- a/tests/codegen/box-default-debug-copies.rs
+++ b/tests/codegen/box-default-debug-copies.rs
@@ -5,6 +5,9 @@
 // four `T` allocas.
 //
 // See https://github.com/rust-lang/rust/issues/136043 for more context.
+//
+// FIXME: This test only wants to ensure that there are at most two allocas of `T` created, instead
+// of checking for exactly two.
 
 #![crate_type = "lib"]
 
@@ -17,7 +20,7 @@ impl Default for Thing {
     }
 }
 
-// CHECK-COUNT-4: %{{.*}} = alloca {{.*}}1000000
+// CHECK-COUNT-2: %{{.*}} = alloca {{.*}}1000000
 // CHECK-NOT: %{{.*}} = alloca {{.*}}1000000
 #[no_mangle]
 pub fn box_default_single_copy() -> Box<Thing> {