Skip to content

Commit b203a26

Browse files
committed
rustc: generalize layout::Variants::NicheFilling to niches other than 0.
1 parent 0190f27 commit b203a26

File tree

6 files changed

+146
-86
lines changed

6 files changed

+146
-86
lines changed

src/librustc/ty/layout.rs

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -790,17 +790,18 @@ pub enum Variants {
790790
variants: Vec<CachedLayout>,
791791
},
792792

793-
/// Two cases distinguished by a niche: the case with discriminant
794-
/// `nndiscr` is represented by the struct `nonnull`, where field `0`
795-
/// is known to be nonnull due to its type; if that field is null, then
796-
/// it represents the other case, which is known to be zero sized.
793+
/// Two cases distinguished by a niche (a value invalid for a type):
794+
/// the variant `dataful_variant` contains a niche at an arbitrary
795+
/// offset (field 0 of the enum), which is set to `niche_value`
796+
/// for the other variant.
797797
///
798-
/// For example, `std::option::Option` instantiated at a safe pointer type
799-
/// is represented such that `None` is a null pointer and `Some` is the
800-
/// identity function.
798+
/// For example, `Option<(usize, &T)>` is represented such that
799+
/// `None` has a null pointer for the second tuple field, and
800+
/// `Some` is the identity function (with a non-null reference).
801801
NicheFilling {
802-
nndiscr: u64,
803-
discr: Primitive,
802+
dataful_variant: usize,
803+
niche: Primitive,
804+
niche_value: u128,
804805
variants: Vec<CachedLayout>,
805806
}
806807
}
@@ -1323,7 +1324,7 @@ impl<'a, 'tcx> CachedLayout {
13231324
}
13241325

13251326
for (field_index, field) in variants[i].iter().enumerate() {
1326-
if let Some((offset, discr)) = field.non_zero_field(cx)? {
1327+
if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
13271328
let mut st = vec![
13281329
univariant_uninterned(&variants[0],
13291330
&def.repr, StructKind::AlwaysSized)?,
@@ -1342,23 +1343,23 @@ impl<'a, 'tcx> CachedLayout {
13421343
..
13431344
} = st[i];
13441345

1345-
let mut discr_align = discr.align(dl);
1346-
if offset.bytes() == 0 && discr.size(dl) == size {
1347-
abi = Abi::Scalar(discr);
1346+
let mut niche_align = niche.align(dl);
1347+
if offset.bytes() == 0 && niche.size(dl) == size {
1348+
abi = Abi::Scalar(niche);
13481349
} else if let Abi::Aggregate { ref mut packed, .. } = abi {
1349-
if offset.abi_align(discr_align) != offset {
1350+
if offset.abi_align(niche_align) != offset {
13501351
*packed = true;
1351-
discr_align = dl.i8_align;
1352+
niche_align = dl.i8_align;
13521353
}
13531354
}
1354-
align = align.max(discr_align);
1355-
primitive_align = primitive_align.max(discr_align);
1355+
align = align.max(niche_align);
1356+
primitive_align = primitive_align.max(niche_align);
13561357

13571358
return Ok(tcx.intern_layout(CachedLayout {
13581359
variants: Variants::NicheFilling {
1359-
nndiscr: i as u64,
1360-
1361-
discr,
1360+
dataful_variant: i,
1361+
niche,
1362+
niche_value,
13621363
variants: st,
13631364
},
13641365
fields: FieldPlacement::Arbitrary {
@@ -2048,7 +2049,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
20482049

20492050
// Discriminant field for enums (where applicable).
20502051
Variants::Tagged { discr, .. } |
2051-
Variants::NicheFilling { discr, .. } => {
2052+
Variants::NicheFilling { niche: discr, .. } => {
20522053
return cx.layout_of([discr.to_ty(tcx)][i]);
20532054
}
20542055
}
@@ -2084,30 +2085,48 @@ impl<'a, 'tcx> TyLayout<'tcx> {
20842085
(self.size, self.align)
20852086
}
20862087

2087-
/// Find the offset of a non-zero leaf field, starting from
2088+
/// Find the offset of a niche leaf field, starting from
20882089
/// the given type and recursing through aggregates.
2089-
/// The tuple is `(offset, primitive, source_path)`.
2090+
/// The tuple is `(offset, primitive, niche_value)`.
20902091
// FIXME(eddyb) track value ranges and traverse already optimized enums.
2091-
fn non_zero_field<C>(&self, cx: C)
2092-
-> Result<Option<(Size, Primitive)>, LayoutError<'tcx>>
2092+
fn find_niche<C>(&self, cx: C)
2093+
-> Result<Option<(Size, Primitive, u128)>, LayoutError<'tcx>>
20932094
where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
20942095
HasTyCtxt<'tcx>
20952096
{
20962097
let tcx = cx.tcx();
20972098
match (&self.variants, self.abi, &self.ty.sty) {
20982099
// FIXME(eddyb) check this via value ranges on scalars.
2100+
(_, Abi::Scalar(Int(I1, _)), _) => {
2101+
Ok(Some((Size::from_bytes(0), Int(I8, false), 2)))
2102+
}
2103+
(_, Abi::Scalar(Int(I32, _)), &ty::TyChar) => {
2104+
Ok(Some((Size::from_bytes(0), Int(I32, false), 0x10FFFF+1)))
2105+
}
20992106
(_, Abi::Scalar(Pointer), &ty::TyRef(..)) |
21002107
(_, Abi::Scalar(Pointer), &ty::TyFnPtr(..)) => {
2101-
Ok(Some((Size::from_bytes(0), Pointer)))
2108+
Ok(Some((Size::from_bytes(0), Pointer, 0)))
21022109
}
21032110
(_, Abi::Scalar(Pointer), &ty::TyAdt(def, _)) if def.is_box() => {
2104-
Ok(Some((Size::from_bytes(0), Pointer)))
2111+
Ok(Some((Size::from_bytes(0), Pointer, 0)))
21052112
}
21062113

21072114
// FIXME(eddyb) check this via value ranges on scalars.
2108-
(&Variants::Tagged { discr, .. }, _, &ty::TyAdt(def, _)) => {
2109-
if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) {
2110-
Ok(Some((self.fields.offset(0), discr)))
2115+
(&Variants::Tagged { discr, ref discr_range, .. }, _, _) => {
2116+
// FIXME(eddyb) support negative/wrap-around discriminant ranges.
2117+
if discr_range.start < discr_range.end {
2118+
if discr_range.start > 0 {
2119+
Ok(Some((self.fields.offset(0), discr, 0)))
2120+
} else {
2121+
let bits = discr.size(tcx).bits();
2122+
assert!(bits <= 128);
2123+
let max_value = !0u128 >> (128 - bits);
2124+
if discr_range.end < max_value {
2125+
Ok(Some((self.fields.offset(0), discr, discr_range.end + 1)))
2126+
} else {
2127+
Ok(None)
2128+
}
2129+
}
21112130
} else {
21122131
Ok(None)
21132132
}
@@ -2118,7 +2137,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
21182137
let field = self.field(cx, 0)?;
21192138
let offset = self.fields.offset(0);
21202139
if let Abi::Scalar(value) = field.abi {
2121-
Ok(Some((offset, value)))
2140+
Ok(Some((offset, value, 0)))
21222141
} else {
21232142
Ok(None)
21242143
}
@@ -2128,13 +2147,14 @@ impl<'a, 'tcx> TyLayout<'tcx> {
21282147
_ => {
21292148
if let FieldPlacement::Array { count, .. } = self.fields {
21302149
if count > 0 {
2131-
return self.field(cx, 0)?.non_zero_field(cx);
2150+
return self.field(cx, 0)?.find_niche(cx);
21322151
}
21332152
}
21342153
for i in 0..self.fields.count() {
2135-
let r = self.field(cx, i)?.non_zero_field(cx)?;
2136-
if let Some((offset, primitive)) = r {
2137-
return Ok(Some((self.fields.offset(i) + offset, primitive)));
2154+
let r = self.field(cx, i)?.find_niche(cx)?;
2155+
if let Some((offset, primitive, niche_value)) = r {
2156+
let offset = self.fields.offset(i) + offset;
2157+
return Ok(Some((offset, primitive, niche_value)));
21382158
}
21392159
}
21402160
Ok(None)
@@ -2165,13 +2185,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
21652185
variants.hash_stable(hcx, hasher);
21662186
}
21672187
NicheFilling {
2168-
nndiscr,
2188+
dataful_variant,
2189+
ref niche,
2190+
niche_value,
21692191
ref variants,
2170-
ref discr,
21712192
} => {
2172-
nndiscr.hash_stable(hcx, hasher);
2193+
dataful_variant.hash_stable(hcx, hasher);
2194+
niche.hash_stable(hcx, hasher);
2195+
niche_value.hash_stable(hcx, hasher);
21732196
variants.hash_stable(hcx, hasher);
2174-
discr.hash_stable(hcx, hasher);
21752197
}
21762198
}
21772199
}

src/librustc_trans/debuginfo/metadata.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,17 +1191,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
11911191
}
11921192
}).collect()
11931193
}
1194-
layout::Variants::NicheFilling {
1195-
nndiscr,
1196-
discr,
1197-
..
1198-
} => {
1199-
let variant = self.layout.for_variant(nndiscr as usize);
1194+
layout::Variants::NicheFilling { dataful_variant, .. } => {
1195+
let variant = self.layout.for_variant(dataful_variant);
12001196
// Create a description of the non-null variant
12011197
let (variant_type_metadata, member_description_factory) =
12021198
describe_enum_variant(cx,
12031199
variant,
1204-
&adt.variants[nndiscr as usize],
1200+
&adt.variants[dataful_variant],
12051201
OptimizedDiscriminant,
12061202
self.containing_scope,
12071203
self.span);
@@ -1239,8 +1235,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
12391235
compute_field_path(cx, &mut name,
12401236
self.layout,
12411237
self.layout.fields.offset(0),
1242-
discr.size(cx));
1243-
name.push_str(&adt.variants[(1 - nndiscr) as usize].name.as_str());
1238+
self.layout.field(cx, 0).size);
1239+
name.push_str(&adt.variants[1 - dataful_variant].name.as_str());
12441240

12451241
// Create the (singleton) list of descriptions of union members.
12461242
vec![

src/librustc_trans/mir/constant.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,22 +1122,29 @@ fn trans_const_adt<'a, 'tcx>(
11221122
},
11231123
_ => 0,
11241124
};
1125-
let discr_ty = l.field(ccx, 0).ty;
1126-
let discr = C_int(ccx.layout_of(discr_ty).llvm_type(ccx), discr as i64);
1125+
let discr_field = l.field(ccx, 0);
1126+
let discr = C_int(discr_field.llvm_type(ccx), discr as i64);
11271127
if let layout::Abi::Scalar(_) = l.abi {
11281128
Const::new(discr, t)
11291129
} else {
1130-
let discr = Const::new(discr, discr_ty);
1130+
let discr = Const::new(discr, discr_field.ty);
11311131
build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
11321132
}
11331133
}
1134-
layout::Variants::NicheFilling { nndiscr, .. } => {
1135-
if variant_index as u64 == nndiscr {
1136-
build_const_struct(ccx, l.for_variant(variant_index), vals, None)
1134+
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
1135+
if variant_index == dataful_variant {
1136+
build_const_struct(ccx, l.for_variant(dataful_variant), vals, None)
11371137
} else {
1138-
// Always use null even if it's not the `discrfield`th
1139-
// field; see #8506.
1140-
Const::new(C_null(ccx.layout_of(t).llvm_type(ccx)), t)
1138+
let niche = l.field(ccx, 0);
1139+
let niche_llty = niche.llvm_type(ccx);
1140+
// FIXME(eddyb) Check the actual primitive type here.
1141+
let niche_llval = if niche_value == 0 {
1142+
// HACK(eddyb) Using `C_null` as it works on all types.
1143+
C_null(niche_llty)
1144+
} else {
1145+
C_uint_big(niche_llty, niche_value)
1146+
};
1147+
build_const_struct(ccx, l, &[Const::new(niche_llval, niche.ty)], None)
11411148
}
11421149
}
11431150
}

src/librustc_trans/mir/lvalue.rs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc::mir::tcx::LvalueTy;
1616
use rustc_data_structures::indexed_vec::Idx;
1717
use base;
1818
use builder::Builder;
19-
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null};
19+
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big};
2020
use consts;
2121
use type_of::LayoutLlvmExt;
2222
use type_::Type;
@@ -72,10 +72,6 @@ impl Alignment {
7272
}
7373
}
7474

75-
fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
76-
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
77-
}
78-
7975
#[derive(Copy, Clone, Debug)]
8076
pub struct LvalueRef<'tcx> {
8177
/// Pointer to the contents of the lvalue
@@ -325,51 +321,60 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
325321
};
326322
bcx.intcast(lldiscr, cast_to, signed)
327323
}
328-
layout::Variants::NicheFilling { nndiscr, .. } => {
329-
let cmp = if nndiscr == 0 { llvm::IntEQ } else { llvm::IntNE };
330-
let zero = C_null(discr.layout.llvm_type(bcx.ccx));
331-
bcx.intcast(bcx.icmp(cmp, lldiscr, zero), cast_to, false)
324+
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
325+
let niche_llty = discr.layout.llvm_type(bcx.ccx);
326+
// FIXME(eddyb) Check the actual primitive type here.
327+
let niche_llval = if niche_value == 0 {
328+
// HACK(eddyb) Using `C_null` as it works on all types.
329+
C_null(niche_llty)
330+
} else {
331+
C_uint_big(niche_llty, niche_value)
332+
};
333+
let cmp = if dataful_variant == 0 { llvm::IntEQ } else { llvm::IntNE };
334+
bcx.intcast(bcx.icmp(cmp, lldiscr, niche_llval), cast_to, false)
332335
}
333336
}
334337
}
335338

336339
/// Set the discriminant for a new value of the given case of the given
337340
/// representation.
338341
pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) {
339-
let to = self.layout.ty.ty_adt_def().unwrap()
340-
.discriminant_for_variant(bcx.tcx(), variant_index)
341-
.to_u128_unchecked() as u64;
342342
match self.layout.variants {
343343
layout::Variants::Single { index } => {
344-
assert_eq!(to, 0);
345344
assert_eq!(variant_index, index);
346345
}
347346
layout::Variants::Tagged { .. } => {
348347
let ptr = self.project_field(bcx, 0);
348+
let to = self.layout.ty.ty_adt_def().unwrap()
349+
.discriminant_for_variant(bcx.tcx(), variant_index)
350+
.to_u128_unchecked() as u64;
349351
bcx.store(C_int(ptr.layout.llvm_type(bcx.ccx), to as i64),
350352
ptr.llval, ptr.alignment.non_abi());
351353
}
352-
layout::Variants::NicheFilling { nndiscr, .. } => {
353-
if to != nndiscr {
354-
let use_memset = match self.layout.abi {
355-
layout::Abi::Scalar(_) => false,
356-
_ => target_sets_discr_via_memset(bcx)
357-
};
358-
if use_memset {
359-
// Issue #34427: As workaround for LLVM bug on
360-
// ARM, use memset of 0 on whole struct rather
361-
// than storing null to single target field.
354+
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
355+
if variant_index != dataful_variant {
356+
if bcx.sess().target.target.arch == "arm" ||
357+
bcx.sess().target.target.arch == "aarch64" {
358+
// Issue #34427: As workaround for LLVM bug on ARM,
359+
// use memset of 0 before assigning niche value.
362360
let llptr = bcx.pointercast(self.llval, Type::i8(bcx.ccx).ptr_to());
363361
let fill_byte = C_u8(bcx.ccx, 0);
364362
let (size, align) = self.layout.size_and_align();
365363
let size = C_usize(bcx.ccx, size.bytes());
366364
let align = C_u32(bcx.ccx, align.abi() as u32);
367365
base::call_memset(bcx, llptr, fill_byte, size, align, false);
368-
} else {
369-
let ptr = self.project_field(bcx, 0);
370-
bcx.store(C_null(ptr.layout.llvm_type(bcx.ccx)),
371-
ptr.llval, ptr.alignment.non_abi());
372366
}
367+
368+
let niche = self.project_field(bcx, 0);
369+
let niche_llty = niche.layout.llvm_type(bcx.ccx);
370+
// FIXME(eddyb) Check the actual primitive type here.
371+
let niche_llval = if niche_value == 0 {
372+
// HACK(eddyb) Using `C_null` as it works on all types.
373+
C_null(niche_llty)
374+
} else {
375+
C_uint_big(niche_llty, niche_value)
376+
};
377+
bcx.store(niche_llval, niche.llval, niche.alignment.non_abi());
373378
}
374379
}
375380
}

src/test/ui/print_type_sizes/nullable.rs renamed to src/test/ui/print_type_sizes/niche-filling.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
// compile-flags: -Z print-type-sizes
1212

13-
// This file illustrates how enums with a non-null field are handled,
14-
// modelled after cases like `Option<&u32>` and such.
13+
// This file illustrates how niche-filling enums are handled,
14+
// modelled after cases like `Option<&u32>`, `Option<bool>` and such.
1515
//
1616
// It uses NonZero directly, rather than `&_` or `Unique<_>`, because
1717
// the test is not set up to deal with target-dependent pointer width.
@@ -72,4 +72,8 @@ pub fn main() {
7272
let _x: MyOption<NonZero<u32>> = Default::default();
7373
let _y: EmbeddedDiscr = Default::default();
7474
let _z: MyOption<IndirectNonZero<u32>> = Default::default();
75+
let _a: MyOption<bool> = Default::default();
76+
let _b: MyOption<char> = Default::default();
77+
let _c: MyOption<std::cmp::Ordering> = Default::default();
78+
let _b: MyOption<MyOption<u8>> = Default::default();
7579
}

0 commit comments

Comments
 (0)