Skip to content

Commit ec3bccb

Browse files
committed
core: Remove panics from some Layout methods
`Layout` is often used at the core of allocation APIs and is as a result pretty sensitive to codegen in various circumstances. I was profiling `-C opt-level=z` with a wasm project recently and noticed that the `unwrap()` wasn't removed inside of `Layout`, causing the program to be much larger than it otherwise would be. If inlining were more aggressive LLVM would have figured out that the panic could be eliminated, but in general the methods here can't panic in the first place! As a result this commit makes the following tweaks: * Removes `unwrap()` and replaces it with `unsafe` in `Layout::new` and `Layout::for_value`. For posterity though a debug assertion was left behind. * Removes an `unwrap()` in favor of `?` in the `repeat` method. The comment indicating that the function call couldn't panic wasn't quite right in that if `alloc_size` becomes too large and if `align` is high enough it could indeed cause a panic. This'll hopefully mean that panics never get introduced into code in the first place, ensuring that `opt-level=z` is closer to `opt-level=s` in this regard.
1 parent 67712d7 commit ec3bccb

File tree

1 file changed

+14
-8
lines changed

1 file changed

+14
-8
lines changed

src/libcore/heap.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,26 @@ impl Layout {
125125
/// Constructs a `Layout` suitable for holding a value of type `T`.
126126
pub fn new<T>() -> Self {
127127
let (size, align) = size_align::<T>();
128-
Layout::from_size_align(size, align).unwrap()
128+
// Note that the align is guaranteed by rustc to be a power of two and
129+
// the size+align combo is guaranteed to fit in our address space. As a
130+
// result use the unchecked constructor here to avoid inserting code
131+
// that panics if it isn't optimized well enough.
132+
debug_assert!(Layout::from_size_align(size, align).is_some());
133+
unsafe {
134+
Layout::from_size_align_unchecked(size, align)
135+
}
129136
}
130137

131138
/// Produces layout describing a record that could be used to
132139
/// allocate backing structure for `T` (which could be a trait
133140
/// or other unsized type like a slice).
134141
pub fn for_value<T: ?Sized>(t: &T) -> Self {
135142
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
136-
Layout::from_size_align(size, align).unwrap()
143+
// See rationale in `new` for why this us using an unsafe variant below
144+
debug_assert!(Layout::from_size_align(size, align).is_some());
145+
unsafe {
146+
Layout::from_size_align_unchecked(size, align)
147+
}
137148
}
138149

139150
/// Creates a layout describing the record that can hold a value
@@ -212,12 +223,7 @@ impl Layout {
212223
pub fn repeat(&self, n: usize) -> Option<(Self, usize)> {
213224
let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?;
214225
let alloc_size = padded_size.checked_mul(n)?;
215-
216-
// We can assume that `self.align` is a power-of-two.
217-
// Furthermore, `alloc_size` has already been rounded up
218-
// to a multiple of `self.align`; therefore, the call to
219-
// `Layout::from_size_align` below should never panic.
220-
Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size))
226+
Some((Layout::from_size_align(alloc_size, self.align)?, padded_size))
221227
}
222228

223229
/// Creates a layout describing the record for `self` followed by

0 commit comments

Comments
 (0)