Skip to content

Commit 597973d

Browse files
committed
rustc: extend the niche-filling enum optimization past 2 variants.
1 parent 6d48802 commit 597973d

File tree

4 files changed

+173
-120
lines changed

4 files changed

+173
-120
lines changed

src/librustc/ty/layout.rs

+129-102
Original file line numberDiff line numberDiff line change
@@ -805,19 +805,19 @@ pub enum Variants {
805805
variants: Vec<CachedLayout>,
806806
},
807807

808-
/// Two cases distinguished by a niche (a value invalid for a type):
808+
/// Multiple cases distinguished by a niche (values invalid for a type):
809809
/// the variant `dataful_variant` contains a niche at an arbitrary
810-
/// offset (field 0 of the enum), which is set to `niche_value`
811-
/// for the other variant.
810+
/// offset (field 0 of the enum), which for a variant with discriminant
811+
/// `d` is set to `(d - niche_variants.start).wrapping_add(niche_start)`.
812812
///
813813
/// For example, `Option<(usize, &T)>` is represented such that
814814
/// `None` has a null pointer for the second tuple field, and
815815
/// `Some` is the identity function (with a non-null reference).
816816
NicheFilling {
817817
dataful_variant: usize,
818-
niche_variant: usize,
818+
niche_variants: RangeInclusive<usize>,
819819
niche: Scalar,
820-
niche_value: u128,
820+
niche_start: u128,
821821
variants: Vec<CachedLayout>,
822822
}
823823
}
@@ -1372,11 +1372,11 @@ impl<'a, 'tcx> CachedLayout {
13721372
}).collect::<Result<Vec<_>, _>>()
13731373
}).collect::<Result<Vec<_>, _>>()?;
13741374

1375-
let (inh_first, inh_second, inh_third) = {
1375+
let (inh_first, inh_second) = {
13761376
let mut inh_variants = (0..variants.len()).filter(|&v| {
13771377
variants[v].iter().all(|f| f.abi != Abi::Uninhabited)
13781378
});
1379-
(inh_variants.next(), inh_variants.next(), inh_variants.next())
1379+
(inh_variants.next(), inh_variants.next())
13801380
};
13811381
if inh_first.is_none() {
13821382
// Uninhabited because it has no variants, or only uninhabited ones.
@@ -1472,68 +1472,94 @@ impl<'a, 'tcx> CachedLayout {
14721472
let no_explicit_discriminants = def.variants.iter().enumerate()
14731473
.all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i));
14741474

1475-
if inh_second.is_some() && inh_third.is_none() &&
1476-
!def.repr.inhibit_enum_layout_opt() &&
1477-
no_explicit_discriminants {
1478-
// Nullable pointer optimization
1479-
let (a, b) = (inh_first.unwrap(), inh_second.unwrap());
1480-
for &(i, other) in &[(a, b), (b, a)] {
1481-
if !variants[other].iter().all(|f| f.is_zst()) {
1482-
continue;
1475+
// Niche-filling enum optimization.
1476+
if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants {
1477+
let mut dataful_variant = None;
1478+
let mut niche_variants = usize::max_value()..=0;
1479+
1480+
// Find one non-ZST variant.
1481+
'variants: for (v, fields) in variants.iter().enumerate() {
1482+
for f in fields {
1483+
if f.abi == Abi::Uninhabited {
1484+
continue 'variants;
1485+
}
1486+
if !f.is_zst() {
1487+
if dataful_variant.is_none() {
1488+
dataful_variant = Some(v);
1489+
continue 'variants;
1490+
} else {
1491+
dataful_variant = None;
1492+
break 'variants;
1493+
}
1494+
}
1495+
}
1496+
if niche_variants.start > v {
1497+
niche_variants.start = v;
14831498
}
1499+
niche_variants.end = v;
1500+
}
1501+
1502+
if niche_variants.start > niche_variants.end {
1503+
dataful_variant = None;
1504+
}
14841505

1506+
if let Some(i) = dataful_variant {
1507+
let count = (niche_variants.end - niche_variants.start + 1) as u128;
14851508
for (field_index, field) in variants[i].iter().enumerate() {
1486-
if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
1487-
let st = variants.iter().enumerate().map(|(j, v)| {
1488-
let mut st = univariant_uninterned(v,
1489-
&def.repr, StructKind::AlwaysSized)?;
1490-
st.variants = Variants::Single { index: j };
1491-
Ok(st)
1492-
}).collect::<Result<Vec<_>, _>>()?;
1493-
1494-
let offset = st[i].fields.offset(field_index) + offset;
1495-
let CachedLayout {
1496-
size,
1497-
mut align,
1498-
mut primitive_align,
1499-
..
1500-
} = st[i];
1501-
1502-
let mut niche_align = niche.value.align(dl);
1503-
let abi = if offset.bytes() == 0 && niche.value.size(dl) == size {
1504-
Abi::Scalar(niche.clone())
1505-
} else {
1506-
let mut packed = st[i].abi.is_packed();
1507-
if offset.abi_align(niche_align) != offset {
1508-
packed = true;
1509-
niche_align = dl.i8_align;
1510-
}
1511-
Abi::Aggregate {
1512-
sized: true,
1513-
packed
1514-
}
1509+
let (offset, niche, niche_start) =
1510+
match field.find_niche(cx, count)? {
1511+
Some(niche) => niche,
1512+
None => continue
15151513
};
1516-
align = align.max(niche_align);
1517-
primitive_align = primitive_align.max(niche_align);
1518-
1519-
return Ok(tcx.intern_layout(CachedLayout {
1520-
variants: Variants::NicheFilling {
1521-
dataful_variant: i,
1522-
niche_variant: other,
1523-
niche,
1524-
niche_value,
1525-
variants: st,
1526-
},
1527-
fields: FieldPlacement::Arbitrary {
1528-
offsets: vec![offset],
1529-
memory_index: vec![0]
1530-
},
1531-
abi,
1532-
size,
1533-
align,
1534-
primitive_align
1535-
}));
1536-
}
1514+
let st = variants.iter().enumerate().map(|(j, v)| {
1515+
let mut st = univariant_uninterned(v,
1516+
&def.repr, StructKind::AlwaysSized)?;
1517+
st.variants = Variants::Single { index: j };
1518+
Ok(st)
1519+
}).collect::<Result<Vec<_>, _>>()?;
1520+
1521+
let offset = st[i].fields.offset(field_index) + offset;
1522+
let CachedLayout {
1523+
size,
1524+
mut align,
1525+
mut primitive_align,
1526+
..
1527+
} = st[i];
1528+
1529+
let mut niche_align = niche.value.align(dl);
1530+
let abi = if offset.bytes() == 0 && niche.value.size(dl) == size {
1531+
Abi::Scalar(niche.clone())
1532+
} else {
1533+
let mut packed = st[i].abi.is_packed();
1534+
if offset.abi_align(niche_align) != offset {
1535+
packed = true;
1536+
niche_align = dl.i8_align;
1537+
}
1538+
Abi::Aggregate {
1539+
sized: true,
1540+
packed
1541+
}
1542+
};
1543+
align = align.max(niche_align);
1544+
primitive_align = primitive_align.max(niche_align);
1545+
1546+
return Ok(tcx.intern_layout(CachedLayout {
1547+
variants: Variants::NicheFilling {
1548+
dataful_variant: i,
1549+
niche_variants,
1550+
niche,
1551+
niche_start,
1552+
variants: st,
1553+
},
1554+
fields: FieldPlacement::Arbitrary {
1555+
offsets: vec![offset],
1556+
memory_index: vec![0]
1557+
},
1558+
abi,
1559+
size,
1560+
align,
1561+
primitive_align
1562+
}));
15371563
}
15381564
}
15391565
}
@@ -2272,50 +2298,50 @@ impl<'a, 'tcx> TyLayout<'tcx> {
22722298
}
22732299

22742300
/// Find the offset of a niche leaf field, starting from
2275-
/// the given type and recursing through aggregates.
2301+
/// the given type and recursing through aggregates, which
2302+
/// has at least `count` consecutive invalid values.
22762303
/// The tuple is `(offset, scalar, niche_value)`.
22772304
// FIXME(eddyb) traverse already optimized enums.
2278-
fn find_niche<C>(&self, cx: C)
2305+
fn find_niche<C>(&self, cx: C, count: u128)
22792306
-> Result<Option<(Size, Scalar, u128)>, LayoutError<'tcx>>
22802307
where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
22812308
HasTyCtxt<'tcx>
22822309
{
22832310
let scalar_component = |scalar: &Scalar, offset| {
2284-
// FIXME(eddyb) support negative/wrap-around discriminant ranges.
2285-
let Scalar { value, ref valid_range } = *scalar;
2286-
if valid_range.start < valid_range.end {
2287-
let bits = value.size(cx).bits();
2288-
assert!(bits <= 128);
2289-
let max_value = !0u128 >> (128 - bits);
2290-
if valid_range.start > 0 {
2291-
let niche = valid_range.start - 1;
2292-
Ok(Some((offset, Scalar {
2293-
value,
2294-
valid_range: niche..=valid_range.end
2295-
}, niche)))
2296-
} else if valid_range.end < max_value {
2297-
let niche = valid_range.end + 1;
2298-
Ok(Some((offset, Scalar {
2299-
value,
2300-
valid_range: valid_range.start..=niche
2301-
}, niche)))
2302-
} else {
2303-
Ok(None)
2304-
}
2311+
let Scalar { value, valid_range: ref v } = *scalar;
2312+
2313+
let bits = value.size(cx).bits();
2314+
assert!(bits <= 128);
2315+
let max_value = !0u128 >> (128 - bits);
2316+
2317+
// Find out how many values are outside the valid range.
2318+
let niches = if v.start <= v.end {
2319+
v.start + (max_value - v.end)
23052320
} else {
2306-
Ok(None)
2321+
v.start - v.end - 1
2322+
};
2323+
2324+
// Give up if we can't fit `count` consecutive niches.
2325+
if count > niches {
2326+
return None;
23072327
}
2328+
2329+
let niche_start = v.end.wrapping_add(1) & max_value;
2330+
let niche_end = v.end.wrapping_add(count) & max_value;
2331+
Some((offset, Scalar {
2332+
value,
2333+
valid_range: v.start..=niche_end
2334+
}, niche_start))
23082335
};
23092336

23102337
match self.abi {
23112338
Abi::Scalar(ref scalar) => {
2312-
return scalar_component(scalar, Size::from_bytes(0));
2339+
return Ok(scalar_component(scalar, Size::from_bytes(0)));
23132340
}
23142341
Abi::ScalarPair(ref a, ref b) => {
2315-
if let Some(result) = scalar_component(a, Size::from_bytes(0))? {
2316-
return Ok(Some(result));
2317-
}
2318-
return scalar_component(b, a.value.size(cx).abi_align(b.value.align(cx)));
2342+
return Ok(scalar_component(a, Size::from_bytes(0)).or_else(|| {
2343+
scalar_component(b, a.value.size(cx).abi_align(b.value.align(cx)))
2344+
}));
23192345
}
23202346
_ => {}
23212347
}
@@ -2328,13 +2354,13 @@ impl<'a, 'tcx> TyLayout<'tcx> {
23282354
return Ok(None);
23292355
}
23302356
}
2331-
if let FieldPlacement::Array { count, .. } = self.fields {
2332-
if count > 0 {
2333-
return self.field(cx, 0)?.find_niche(cx);
2357+
if let FieldPlacement::Array { .. } = self.fields {
2358+
if self.fields.count() > 0 {
2359+
return self.field(cx, 0)?.find_niche(cx, count);
23342360
}
23352361
}
23362362
for i in 0..self.fields.count() {
2337-
let r = self.field(cx, i)?.find_niche(cx)?;
2363+
let r = self.field(cx, i)?.find_niche(cx, count)?;
23382364
if let Some((offset, scalar, niche_value)) = r {
23392365
let offset = self.fields.offset(i) + offset;
23402366
return Ok(Some((offset, scalar, niche_value)));
@@ -2364,15 +2390,16 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
23642390
}
23652391
NicheFilling {
23662392
dataful_variant,
2367-
niche_variant,
2393+
niche_variants: RangeInclusive { start, end },
23682394
ref niche,
2369-
niche_value,
2395+
niche_start,
23702396
ref variants,
23712397
} => {
23722398
dataful_variant.hash_stable(hcx, hasher);
2373-
niche_variant.hash_stable(hcx, hasher);
2399+
start.hash_stable(hcx, hasher);
2400+
end.hash_stable(hcx, hasher);
23742401
niche.hash_stable(hcx, hasher);
2375-
niche_value.hash_stable(hcx, hasher);
2402+
niche_start.hash_stable(hcx, hasher);
23762403
variants.hash_stable(hcx, hasher);
23772404
}
23782405
}

src/librustc_trans/debuginfo/metadata.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
11571157
}
11581158
}).collect()
11591159
}
1160-
layout::Variants::NicheFilling { dataful_variant, niche_variant, .. } => {
1160+
layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => {
11611161
let variant = self.layout.for_variant(cx, dataful_variant);
11621162
// Create a description of the non-null variant
11631163
let (variant_type_metadata, member_description_factory) =
@@ -1180,6 +1180,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
11801180
let mut name = String::from("RUST$ENCODED$ENUM$");
11811181
// HACK(eddyb) the debuggers should just handle offset+size
11821182
// of discriminant instead of us having to recover its path.
1183+
// Right now it's not even going to work for `niche_start > 0`,
1184+
// and for multiple niche variants it only supports the first.
11831185
fn compute_field_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
11841186
name: &mut String,
11851187
layout: TyLayout<'tcx>,
@@ -1202,7 +1204,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
12021204
self.layout,
12031205
self.layout.fields.offset(0),
12041206
self.layout.field(cx, 0).size);
1205-
name.push_str(&adt.variants[niche_variant].name.as_str());
1207+
name.push_str(&adt.variants[niche_variants.start].name.as_str());
12061208

12071209
// Create the (singleton) list of descriptions of union members.
12081210
vec![

src/librustc_trans/mir/constant.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1163,12 +1163,19 @@ fn trans_const_adt<'a, 'tcx>(
11631163
build_const_struct(ccx, l.for_variant(ccx, variant_index), vals, Some(discr))
11641164
}
11651165
}
1166-
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
1166+
layout::Variants::NicheFilling {
1167+
dataful_variant,
1168+
ref niche_variants,
1169+
niche_start,
1170+
..
1171+
} => {
11671172
if variant_index == dataful_variant {
11681173
build_const_struct(ccx, l.for_variant(ccx, dataful_variant), vals, None)
11691174
} else {
11701175
let niche = l.field(ccx, 0);
11711176
let niche_llty = niche.llvm_type(ccx);
1177+
let niche_value = ((variant_index - niche_variants.start) as u128)
1178+
.wrapping_add(niche_start);
11721179
// FIXME(eddyb) Check the actual primitive type here.
11731180
let niche_llval = if niche_value == 0 {
11741181
// HACK(eddyb) Using `C_null` as it works on all types.

0 commit comments

Comments
 (0)