Skip to content

Commit 079d3eb

Browse files
committed
Take advantage of known-valid-align in layout.rs
1 parent 29554c0 commit 079d3eb

File tree

2 files changed

+37
-15
lines changed

2 files changed

+37
-15
lines changed

library/core/src/alloc/layout.rs

+27-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// Seemingly inconsequential code changes to this file can lead to measurable
2+
// performance impact on compilation times, due at least in part to the fact
3+
// that the layout code gets called from many instantiations of the various
4+
// collections, resulting in having to optimize down excess IR multiple times.
5+
// Your performance intuition is useless. Run perf.
6+
17
use crate::cmp;
28
use crate::fmt;
39
use crate::mem::{self, ValidAlign};
@@ -85,6 +91,17 @@ impl Layout {
8591
unsafe { Ok(Layout::from_size_align_unchecked(size, align)) }
8692
}
8793

94+
/// Internal helper constructor to skip revalidating alignment validity.
95+
#[inline]
96+
const fn from_size_valid_align(size: usize, align: ValidAlign) -> Result<Self, LayoutError> {
97+
// See above for the correctness of this check.
98+
if size > isize::MAX as usize - (align.as_nonzero().get() - 1) {
99+
return Err(LayoutError);
100+
}
101+
// SAFTEY: as above, this check is sufficient.
102+
Ok(Layout { size, align })
103+
}
104+
88105
/// Creates a layout, bypassing all checks.
89106
///
90107
/// # Safety
@@ -126,10 +143,9 @@ impl Layout {
126143
#[inline]
127144
pub const fn new<T>() -> Self {
128145
let (size, align) = size_align::<T>();
129-
// SAFETY: the align is guaranteed by Rust to be a power of two and
130-
// the size+align combo is guaranteed to fit in our address space. As a
131-
// result use the unchecked constructor here to avoid inserting code
132-
// that panics if it isn't optimized well enough.
146+
// SAFETY: if the type is instantiated, rustc already ensures that its
147+
// layout is valid. Use the unchecked constructor to avoid inserting a
148+
// panicking codepath that needs to be optimized out.
133149
unsafe { Layout::from_size_align_unchecked(size, align) }
134150
}
135151

@@ -141,7 +157,6 @@ impl Layout {
141157
#[inline]
142158
pub fn for_value<T: ?Sized>(t: &T) -> Self {
143159
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
144-
debug_assert!(Layout::from_size_align(size, align).is_ok());
145160
// SAFETY: see rationale in `new` for why this is using the unsafe variant
146161
unsafe { Layout::from_size_align_unchecked(size, align) }
147162
}
@@ -176,7 +191,6 @@ impl Layout {
176191
pub unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
177192
// SAFETY: we pass along the prerequisites of these functions to the caller
178193
let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) };
179-
debug_assert!(Layout::from_size_align(size, align).is_ok());
180194
// SAFETY: see rationale in `new` for why this is using the unsafe variant
181195
unsafe { Layout::from_size_align_unchecked(size, align) }
182196
}
@@ -280,8 +294,7 @@ impl Layout {
280294
// > less than or equal to `isize::MAX`)
281295
let new_size = self.size() + pad;
282296

283-
// SAFETY: self.align is already known to be valid and new_size has been
284-
// padded already.
297+
// SAFETY: padded size is guaranteed to not exceed `isize::MAX`.
285298
unsafe { Layout::from_size_align_unchecked(new_size, self.align()) }
286299
}
287300

@@ -304,7 +317,7 @@ impl Layout {
304317
let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?;
305318

306319
// The safe constructor is called here to enforce the isize size limit.
307-
Layout::from_size_align(alloc_size, self.align()).map(|layout| (layout, padded_size))
320+
Layout::from_size_valid_align(alloc_size, self.align).map(|layout| (layout, padded_size))
308321
}
309322

310323
/// Creates a layout describing the record for `self` followed by
@@ -355,14 +368,14 @@ impl Layout {
355368
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
356369
#[inline]
357370
pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
358-
let new_align = cmp::max(self.align(), next.align());
371+
let new_align = cmp::max(self.align, next.align);
359372
let pad = self.padding_needed_for(next.align());
360373

361374
let offset = self.size().checked_add(pad).ok_or(LayoutError)?;
362375
let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?;
363376

364377
// The safe constructor is called here to enforce the isize size limit.
365-
let layout = Layout::from_size_align(new_size, new_align)?;
378+
let layout = Layout::from_size_valid_align(new_size, new_align)?;
366379
Ok((layout, offset))
367380
}
368381

@@ -383,7 +396,7 @@ impl Layout {
383396
pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
384397
let size = self.size().checked_mul(n).ok_or(LayoutError)?;
385398
// The safe constructor is called here to enforce the isize size limit.
386-
Layout::from_size_align(size, self.align())
399+
Layout::from_size_valid_align(size, self.align)
387400
}
388401

389402
/// Creates a layout describing the record for `self` followed by
@@ -397,7 +410,7 @@ impl Layout {
397410
pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
398411
let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?;
399412
// The safe constructor is called here to enforce the isize size limit.
400-
Layout::from_size_align(new_size, self.align())
413+
Layout::from_size_valid_align(new_size, self.align)
401414
}
402415

403416
/// Creates a layout describing the record for a `[T; n]`.
@@ -408,7 +421,7 @@ impl Layout {
408421
pub fn array<T>(n: usize) -> Result<Self, LayoutError> {
409422
let array_size = mem::size_of::<T>().checked_mul(n).ok_or(LayoutError)?;
410423
// The safe constructor is called here to enforce the isize size limit.
411-
Layout::from_size_align(array_size, mem::align_of::<T>())
424+
Layout::from_size_valid_align(array_size, ValidAlign::of::<T>())
412425
}
413426
}
414427

library/core/src/mem/valid_align.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::convert::TryFrom;
2+
use crate::intrinsics::assert_unsafe_precondition;
23
use crate::num::NonZeroUsize;
34
use crate::{cmp, fmt, hash, mem, num};
45

@@ -26,7 +27,8 @@ impl ValidAlign {
2627
/// It must *not* be zero.
2728
#[inline]
2829
pub(crate) const unsafe fn new_unchecked(align: usize) -> Self {
29-
debug_assert!(align.is_power_of_two());
30+
// SAFETY: Precondition passed to the caller.
31+
unsafe { assert_unsafe_precondition!(align.is_power_of_two()) };
3032

3133
// SAFETY: By precondition, this must be a power of two, and
3234
// our variants encompass all possible powers of two.
@@ -46,6 +48,13 @@ impl ValidAlign {
4648
pub(crate) fn log2(self) -> u32 {
4749
self.as_nonzero().trailing_zeros()
4850
}
51+
52+
/// Returns the alignment for a type.
53+
#[inline]
54+
pub(crate) fn of<T>() -> Self {
55+
// SAFETY: rustc ensures that type alignment is always a power of two.
56+
unsafe { ValidAlign::new_unchecked(mem::align_of::<T>()) }
57+
}
4958
}
5059

5160
impl fmt::Debug for ValidAlign {

0 commit comments

Comments
 (0)