Skip to content

Commit 8e1ff77

Browse files
committed
Make Layout::new::<T>() a constant instead of multiple NullOps
1 parent acb6273 commit 8e1ff77

File tree

3 files changed

+65
-43
lines changed

3 files changed

+65
-43
lines changed

library/alloc/src/raw_vec.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,9 @@ impl<T, A: Allocator> RawVec<T, A> {
300300
if T::IS_ZST || self.cap.0 == 0 {
301301
None
302302
} else {
303-
// We could use Layout::array here which ensures the absence of isize and usize overflows
304-
// and could hypothetically handle differences between stride and size, but this memory
305-
// has already been allocated so we know it can't overflow and currently Rust does not
306-
// support such types. So we can do better by skipping some checks and avoid an unwrap.
307-
const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
308-
unsafe {
309-
let align = mem::align_of::<T>();
310-
let size = mem::size_of::<T>().unchecked_mul(self.cap.0);
311-
let layout = Layout::from_size_align_unchecked(size, align);
312-
Some((self.ptr.cast().into(), layout))
313-
}
303+
// SAFETY: This memory has already been allocated so we know it can't overflow
304+
let layout = unsafe { Layout::array_unchecked::<T>(self.cap.0) };
305+
Some((self.ptr.cast().into(), layout))
314306
}
315307
}
316308

library/core/src/alloc/layout.rs

+53-32
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@
44
// collections, resulting in having to optimize down excess IR multiple times.
55
// Your performance intuition is useless. Run perf.
66

7+
use crate::assert_unsafe_precondition;
78
use crate::cmp;
89
use crate::error::Error;
910
use crate::fmt;
10-
use crate::mem;
11+
use crate::intrinsics;
12+
use crate::mem::{self, SizedTypeProperties};
1113
use crate::ptr::{Alignment, NonNull};
1214

13-
// While this function is used in one place and its implementation
14-
// could be inlined, the previous attempts to do so made rustc
15-
// slower:
16-
//
17-
// * https://github.com/rust-lang/rust/pull/72189
18-
// * https://github.com/rust-lang/rust/pull/79827
19-
const fn size_align<T>() -> (usize, usize) {
20-
(mem::size_of::<T>(), mem::align_of::<T>())
21-
}
22-
2315
/// Layout of a block of memory.
2416
///
2517
/// An instance of `Layout` describes a particular layout of memory.
@@ -150,11 +142,7 @@ impl Layout {
150142
#[must_use]
151143
#[inline]
152144
pub const fn new<T>() -> Self {
153-
let (size, align) = size_align::<T>();
154-
// SAFETY: if the type is instantiated, rustc already ensures that its
155-
// layout is valid. Use the unchecked constructor to avoid inserting a
156-
// panicking codepath that needs to be optimized out.
157-
unsafe { Layout::from_size_align_unchecked(size, align) }
145+
T::LAYOUT
158146
}
159147

160148
/// Produces layout describing a record that could be used to
@@ -425,43 +413,76 @@ impl Layout {
425413
Layout::from_size_alignment(new_size, self.align)
426414
}
427415

428-
/// Creates a layout describing the record for a `[T; n]`.
416+
/// Creates a layout describing the record for a `[T; len]`.
429417
///
430418
/// On arithmetic overflow or when the total size would exceed
431419
/// `isize::MAX`, returns `LayoutError`.
432420
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
433421
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
434422
#[inline]
435-
pub const fn array<T>(n: usize) -> Result<Self, LayoutError> {
423+
pub const fn array<T>(len: usize) -> Result<Self, LayoutError> {
436424
// Reduce the amount of code we need to monomorphize per `T`.
437-
return inner(mem::size_of::<T>(), Alignment::of::<T>(), n);
425+
return inner(T::LAYOUT, len);
438426

439427
#[inline]
440-
const fn inner(
441-
element_size: usize,
442-
align: Alignment,
443-
n: usize,
444-
) -> Result<Layout, LayoutError> {
428+
const fn inner(element: Layout, len: usize) -> Result<Layout, LayoutError> {
445429
// We need to check two things about the size:
446430
// - That the total size won't overflow a `usize`, and
447431
// - That the total size still fits in an `isize`.
448432
// By using division we can check them both with a single threshold.
449433
// That'd usually be a bad idea, but thankfully here the element size
450434
// and alignment are constants, so the compiler will fold all of it.
451-
if element_size != 0 && n > Layout::max_size_for_align(align) / element_size {
435+
if element.size != 0 && len > Layout::max_size_for_align(element.align) / element.size {
452436
return Err(LayoutError);
453437
}
454438

455-
// SAFETY: We just checked that we won't overflow `usize` when we multiply.
439+
// SAFETY: We just checked above that the `array_size` will not
440+
// exceed `isize::MAX` even when rounded up to the alignment.
441+
unsafe { Ok(Layout::array_of_unchecked(element, len)) }
442+
}
443+
}
444+
445+
/// Creates a layout describing the record for a `[T; n]`, without checking
446+
/// whether the `len` is an allowed length.
447+
///
448+
/// # Safety
449+
///
450+
/// `len` must be small enough that the total size *in bytes* of the
451+
/// resulting layout is `isize::MAX` or less.
452+
///
453+
/// Equivalently, either `T` is a ZST or `len <= isize::MAX / size_of::<T>()`.
454+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
455+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
456+
#[inline]
457+
pub const unsafe fn array_unchecked<T>(len: usize) -> Self {
458+
// SAFETY: `T` is a Rust type, so its size is a multiple of align,
459+
// and our safety precondition ensures that len is small enough.
460+
unsafe { Self::array_of_unchecked(T::LAYOUT, len) }
461+
}
462+
463+
/// # Safety
464+
/// - The element layout must be that of a rust type, with size a multiple of align
465+
/// - The total size of the resulting array must be at most `isize::MAX`
466+
#[inline]
467+
const unsafe fn array_of_unchecked(element: Layout, len: usize) -> Layout {
468+
assert_unsafe_precondition!(
469+
check_language_ub,
470+
"array_of_unchecked element must have size an integer multiple of \
471+
alignment and the total length must be be isize::MAX or less",
472+
(
473+
size: usize = element.size,
474+
align: usize = element.align.as_usize(),
475+
len: usize = len,
476+
) => size % align == 0 && (size as u128) * (len as u128) <= (isize::MAX as u128),
477+
);
478+
479+
// SAFETY: Out preconditions are exactly what's needed here
480+
unsafe {
456481
// This is a useless hint inside this function, but after inlining this helps
457482
// deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's
458483
// allocation path) before/after this multiplication.
459-
let array_size = unsafe { element_size.unchecked_mul(n) };
460-
461-
// SAFETY: We just checked above that the `array_size` will not
462-
// exceed `isize::MAX` even when rounded up to the alignment.
463-
// And `Alignment` guarantees it's a power of two.
464-
unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) }
484+
let bytes = intrinsics::unchecked_mul(element.size, len);
485+
Self { size: bytes, align: element.align }
465486
}
466487
}
467488
}

library/core/src/mem/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
#![stable(feature = "rust1", since = "1.0.0")]
77

8+
use crate::alloc::Layout;
89
use crate::clone;
910
use crate::cmp;
1011
use crate::fmt;
@@ -1235,6 +1236,14 @@ pub trait SizedTypeProperties: Sized {
12351236
#[doc(hidden)]
12361237
#[unstable(feature = "sized_type_properties", issue = "none")]
12371238
const IS_ZST: bool = size_of::<Self>() == 0;
1239+
1240+
#[doc(hidden)]
1241+
#[unstable(feature = "sized_type_properties", issue = "none")]
1242+
const LAYOUT: Layout =
1243+
// SAFETY: if the type is instantiated, rustc already ensures that its
1244+
// layout is valid. Use the unchecked constructor to avoid inserting a
1245+
// panicking codepath that needs to be optimized out.
1246+
unsafe { Layout::from_size_align_unchecked(size_of::<Self>(), align_of::<Self>()) };
12381247
}
12391248
#[doc(hidden)]
12401249
#[unstable(feature = "sized_type_properties", issue = "none")]

0 commit comments

Comments
 (0)