From b69724f37cca68249340e7245e5ac2832d8b2c30 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Mon, 4 Jun 2018 09:22:07 +0100 Subject: [PATCH] Optimize layout calculations in HashMap This now produces the same assembly code as the previous implementation. --- src/libstd/collections/hash/table.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index c62a409ac02c9..d997fb28d4286 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -15,6 +15,7 @@ use mem::{size_of, needs_drop}; use mem; use ops::{Deref, DerefMut}; use ptr::{self, Unique, NonNull}; +use hint; use self::BucketState::*; @@ -655,7 +656,17 @@ impl GapThenFull fn calculate_layout(capacity: usize) -> Result<(Layout, usize), LayoutErr> { let hashes = Layout::array::(capacity)?; let pairs = Layout::array::<(K, V)>(capacity)?; - hashes.extend(pairs) + hashes.extend(pairs).map(|(layout, _)| { + // LLVM seems to have trouble properly const-propagating pairs.align(), + // possibly due to the use of NonZeroUsize. This little hack allows it + // to generate optimal code. + // + // See https://github.com/rust-lang/rust/issues/51346 for more details. + ( + layout, + hashes.size() + hashes.padding_needed_for(mem::align_of::<(K, V)>()), + ) + }) } pub(crate) enum Fallibility { @@ -711,7 +722,8 @@ impl RawTable { } fn raw_bucket_at(&self, index: usize) -> RawBucket { - let (_, pairs_offset) = calculate_layout::(self.capacity()).unwrap(); + let (_, pairs_offset) = calculate_layout::(self.capacity()) + .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); let buffer = self.hashes.ptr() as *mut u8; unsafe { RawBucket { @@ -1109,7 +1121,8 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { } } - let (layout, _) = calculate_layout::(self.capacity()).unwrap(); + let (layout, _) = calculate_layout::(self.capacity()) + .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); unsafe { Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout); // Remember how everything was allocated out of one buffer