Skip to content

Commit 1e508c4

Browse files
committed
Auto merge of #50860 - nox:big-niches-for-big-doggos-🐕, r=eddyb
Find the largest niche when computing layouts Otherwise we end up with `Option<Option<(&(), bool)>>` unnecessarily large.
2 parents 538fea5 + b7db68f commit 1e508c4

File tree

2 files changed

+77
-37
lines changed

2 files changed

+77
-37
lines changed

src/librustc/ty/layout.rs

+74-37
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use syntax_pos::DUMMY_SP;
1818
use std::cmp;
1919
use std::fmt;
2020
use std::i128;
21+
use std::iter;
2122
use std::mem;
2223

2324
use ich::StableHashingContext;
@@ -813,11 +814,15 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
813814
if let Some(i) = dataful_variant {
814815
let count = (niche_variants.end() - niche_variants.start() + 1) as u128;
815816
for (field_index, &field) in variants[i].iter().enumerate() {
816-
let (offset, niche, niche_start) =
817-
match self.find_niche(field, count)? {
818-
Some(niche) => niche,
819-
None => continue
820-
};
817+
let niche = match self.find_niche(field)? {
818+
Some(niche) => niche,
819+
_ => continue,
820+
};
821+
let (niche_start, niche_scalar) = match niche.reserve(self, count) {
822+
Some(pair) => pair,
823+
None => continue,
824+
};
825+
821826
let mut align = dl.aggregate_align;
822827
let st = variants.iter().enumerate().map(|(j, v)| {
823828
let mut st = univariant_uninterned(v,
@@ -829,21 +834,27 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
829834
Ok(st)
830835
}).collect::<Result<Vec<_>, _>>()?;
831836

832-
let offset = st[i].fields.offset(field_index) + offset;
837+
let offset = st[i].fields.offset(field_index) + niche.offset;
833838
let size = st[i].size;
834839

835840
let mut abi = match st[i].abi {
836-
Abi::Scalar(_) => Abi::Scalar(niche.clone()),
841+
Abi::Scalar(_) => Abi::Scalar(niche_scalar.clone()),
837842
Abi::ScalarPair(ref first, ref second) => {
838843
// We need to use scalar_unit to reset the
839844
// valid range to the maximal one for that
840845
// primitive, because only the niche is
841846
// guaranteed to be initialised, not the
842847
// other primitive.
843848
if offset.bytes() == 0 {
844-
Abi::ScalarPair(niche.clone(), scalar_unit(second.value))
849+
Abi::ScalarPair(
850+
niche_scalar.clone(),
851+
scalar_unit(second.value),
852+
)
845853
} else {
846-
Abi::ScalarPair(scalar_unit(first.value), niche.clone())
854+
Abi::ScalarPair(
855+
scalar_unit(first.value),
856+
niche_scalar.clone(),
857+
)
847858
}
848859
}
849860
_ => Abi::Aggregate { sized: true },
@@ -857,7 +868,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
857868
variants: Variants::NicheFilling {
858869
dataful_variant: i,
859870
niche_variants,
860-
niche,
871+
niche: niche_scalar,
861872
niche_start,
862873
variants: st,
863874
},
@@ -1674,40 +1685,56 @@ impl<'a, 'tcx, C> TyLayoutMethods<'tcx, C> for Ty<'tcx>
16741685
}
16751686
}
16761687

1688+
struct Niche {
1689+
offset: Size,
1690+
scalar: Scalar,
1691+
available: u128,
1692+
}
1693+
1694+
impl Niche {
1695+
fn reserve<'a, 'tcx>(
1696+
&self,
1697+
cx: LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>>,
1698+
count: u128,
1699+
) -> Option<(u128, Scalar)> {
1700+
if count > self.available {
1701+
return None;
1702+
}
1703+
let Scalar { value, valid_range: ref v } = self.scalar;
1704+
let bits = value.size(cx).bits();
1705+
assert!(bits <= 128);
1706+
let max_value = !0u128 >> (128 - bits);
1707+
let start = v.end().wrapping_add(1) & max_value;
1708+
let end = v.end().wrapping_add(count) & max_value;
1709+
Some((start, Scalar { value, valid_range: *v.start()..=end }))
1710+
}
1711+
}
1712+
16771713
impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
16781714
/// Find the offset of a niche leaf field, starting from
1679-
/// the given type and recursing through aggregates, which
1680-
/// has at least `count` consecutive invalid values.
1681-
/// The tuple is `(offset, scalar, niche_value)`.
1715+
/// the given type and recursing through aggregates.
16821716
// FIXME(eddyb) traverse already optimized enums.
1683-
fn find_niche(self, layout: TyLayout<'tcx>, count: u128)
1684-
-> Result<Option<(Size, Scalar, u128)>, LayoutError<'tcx>>
1685-
{
1686-
let scalar_component = |scalar: &Scalar, offset| {
1717+
fn find_niche(self, layout: TyLayout<'tcx>) -> Result<Option<Niche>, LayoutError<'tcx>> {
1718+
let scalar_niche = |scalar: &Scalar, offset| {
16871719
let Scalar { value, valid_range: ref v } = *scalar;
16881720

16891721
let bits = value.size(self).bits();
16901722
assert!(bits <= 128);
16911723
let max_value = !0u128 >> (128 - bits);
16921724

16931725
// Find out how many values are outside the valid range.
1694-
let niches = if v.start() <= v.end() {
1726+
let available = if v.start() <= v.end() {
16951727
v.start() + (max_value - v.end())
16961728
} else {
16971729
v.start() - v.end() - 1
16981730
};
16991731

1700-
// Give up if we can't fit `count` consecutive niches.
1701-
if count > niches {
1732+
// Give up if there is no niche value available.
1733+
if available == 0 {
17021734
return None;
17031735
}
17041736

1705-
let niche_start = v.end().wrapping_add(1) & max_value;
1706-
let niche_end = v.end().wrapping_add(count) & max_value;
1707-
Some((offset, Scalar {
1708-
value,
1709-
valid_range: *v.start()..=niche_end
1710-
}, niche_start))
1737+
Some(Niche { offset, scalar: scalar.clone(), available })
17111738
};
17121739

17131740
// Locals variables which live across yields are stored
@@ -1719,15 +1746,19 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
17191746

17201747
match layout.abi {
17211748
Abi::Scalar(ref scalar) => {
1722-
return Ok(scalar_component(scalar, Size::from_bytes(0)));
1749+
return Ok(scalar_niche(scalar, Size::from_bytes(0)));
17231750
}
17241751
Abi::ScalarPair(ref a, ref b) => {
1725-
return Ok(scalar_component(a, Size::from_bytes(0)).or_else(|| {
1726-
scalar_component(b, a.value.size(self).abi_align(b.value.align(self)))
1727-
}));
1752+
// HACK(nox): We iter on `b` and then `a` because `max_by_key`
1753+
// returns the last maximum.
1754+
let niche = iter::once((b, a.value.size(self).abi_align(b.value.align(self))))
1755+
.chain(iter::once((a, Size::from_bytes(0))))
1756+
.filter_map(|(scalar, offset)| scalar_niche(scalar, offset))
1757+
.max_by_key(|niche| niche.available);
1758+
return Ok(niche);
17281759
}
17291760
Abi::Vector { ref element, .. } => {
1730-
return Ok(scalar_component(element, Size::from_bytes(0)));
1761+
return Ok(scalar_niche(element, Size::from_bytes(0)));
17311762
}
17321763
_ => {}
17331764
}
@@ -1742,17 +1773,23 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
17421773
}
17431774
if let FieldPlacement::Array { .. } = layout.fields {
17441775
if layout.fields.count() > 0 {
1745-
return self.find_niche(layout.field(self, 0)?, count);
1776+
return self.find_niche(layout.field(self, 0)?);
1777+
} else {
1778+
return Ok(None);
17461779
}
17471780
}
1781+
let mut niche = None;
1782+
let mut available = 0;
17481783
for i in 0..layout.fields.count() {
1749-
let r = self.find_niche(layout.field(self, i)?, count)?;
1750-
if let Some((offset, scalar, niche_value)) = r {
1751-
let offset = layout.fields.offset(i) + offset;
1752-
return Ok(Some((offset, scalar, niche_value)));
1784+
if let Some(mut c) = self.find_niche(layout.field(self, i)?)? {
1785+
if c.available > available {
1786+
available = c.available;
1787+
c.offset += layout.fields.offset(i);
1788+
niche = Some(c);
1789+
}
17531790
}
17541791
}
1755-
Ok(None)
1792+
Ok(niche)
17561793
}
17571794
}
17581795

src/test/run-pass/type-sizes.rs

+3
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,7 @@ pub fn main() {
116116
assert_eq!(size_of::<EnumWithMaybeUninhabitedVariant<!>>(),
117117
size_of::<EnumWithMaybeUninhabitedVariant<()>>());
118118
assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>());
119+
120+
assert_eq!(size_of::<Option<Option<(bool, &())>>>(), size_of::<(bool, &())>());
121+
assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>());
119122
}

0 commit comments

Comments
 (0)