Skip to content

Commit 8d52aae

Browse files
authored
Rollup merge of rust-lang#136089 - jwong101:box-default-debug-stack-usage, r=Amanieu
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. Another option would be to mark `Box::write` as `#[inline(always)]`, and change it's implementation to to avoid calling `MaybeUninit::write` (which creates a `MaybeUninit<T>` on the stack) and to use `ptr::write` instead. Fixes: rust-lang#136043
2 parents e67d449 + 9700567 commit 8d52aae

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

library/alloc/src/boxed.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1689,7 +1689,20 @@ impl<T: Default> Default for Box<T> {
16891689
/// Creates a `Box<T>`, with the `Default` value for T.
16901690
#[inline]
16911691
fn default() -> Self {
1692-
Box::write(Box::new_uninit(), T::default())
1692+
let mut x: Box<mem::MaybeUninit<T>> = Box::new_uninit();
1693+
unsafe {
1694+
// SAFETY: `x` is valid for writing and has the same layout as `T`.
1695+
// If `T::default()` panics, dropping `x` will just deallocate the Box as `MaybeUninit<T>`
1696+
// does not have a destructor.
1697+
//
1698+
// We use `ptr::write` as `MaybeUninit::write` creates
1699+
// extra stack copies of `T` in debug mode.
1700+
//
1701+
// See https://github.com/rust-lang/rust/issues/136043 for more context.
1702+
ptr::write(&raw mut *x as *mut T, T::default());
1703+
// SAFETY: `x` was just initialized above.
1704+
x.assume_init()
1705+
}
16931706
}
16941707
}
16951708

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ compile-flags: -Copt-level=0
2+
3+
// Test to make sure that `<Box<T>>::default` does not create too many copies of `T` on the stack.
4+
// in debug mode. This regressed in dd0620b86721ae8cae86736443acd3f72ba6fc32 to
5+
// four `T` allocas.
6+
//
7+
// See https://github.com/rust-lang/rust/issues/136043 for more context.
8+
//
9+
// FIXME: This test only wants to ensure that there are at most two allocas of `T` created, instead
10+
// of checking for exactly two.
11+
12+
#![crate_type = "lib"]
13+
14+
#[allow(dead_code)]
15+
pub struct Thing([u8; 1000000]);
16+
17+
impl Default for Thing {
18+
fn default() -> Self {
19+
Thing([0; 1000000])
20+
}
21+
}
22+
23+
// CHECK-COUNT-2: %{{.*}} = alloca {{.*}}1000000
24+
// CHECK-NOT: %{{.*}} = alloca {{.*}}1000000
25+
#[no_mangle]
26+
pub fn box_default_single_copy() -> Box<Thing> {
27+
Box::default()
28+
}

0 commit comments

Comments
 (0)