rustc: generalize layout::Variants::NicheFilling to niches other than 0.
This commit is contained in:
parent
0190f270c1
commit
b203a26efb
6 changed files with 146 additions and 86 deletions
|
@ -790,17 +790,18 @@ pub enum Variants {
|
|||
variants: Vec<CachedLayout>,
|
||||
},
|
||||
|
||||
/// Two cases distinguished by a niche: the case with discriminant
|
||||
/// `nndiscr` is represented by the struct `nonnull`, where field `0`
|
||||
/// is known to be nonnull due to its type; if that field is null, then
|
||||
/// it represents the other case, which is known to be zero sized.
|
||||
/// Two cases distinguished by a niche (a value invalid for a type):
|
||||
/// the variant `dataful_variant` contains a niche at an arbitrary
|
||||
/// offset (field 0 of the enum), which is set to `niche_value`
|
||||
/// for the other variant.
|
||||
///
|
||||
/// For example, `std::option::Option` instantiated at a safe pointer type
|
||||
/// is represented such that `None` is a null pointer and `Some` is the
|
||||
/// identity function.
|
||||
/// For example, `Option<(usize, &T)>` is represented such that
|
||||
/// `None` has a null pointer for the second tuple field, and
|
||||
/// `Some` is the identity function (with a non-null reference).
|
||||
NicheFilling {
|
||||
nndiscr: u64,
|
||||
discr: Primitive,
|
||||
dataful_variant: usize,
|
||||
niche: Primitive,
|
||||
niche_value: u128,
|
||||
variants: Vec<CachedLayout>,
|
||||
}
|
||||
}
|
||||
|
@ -1323,7 +1324,7 @@ impl<'a, 'tcx> CachedLayout {
|
|||
}
|
||||
|
||||
for (field_index, field) in variants[i].iter().enumerate() {
|
||||
if let Some((offset, discr)) = field.non_zero_field(cx)? {
|
||||
if let Some((offset, niche, niche_value)) = field.find_niche(cx)? {
|
||||
let mut st = vec![
|
||||
univariant_uninterned(&variants[0],
|
||||
&def.repr, StructKind::AlwaysSized)?,
|
||||
|
@ -1342,23 +1343,23 @@ impl<'a, 'tcx> CachedLayout {
|
|||
..
|
||||
} = st[i];
|
||||
|
||||
let mut discr_align = discr.align(dl);
|
||||
if offset.bytes() == 0 && discr.size(dl) == size {
|
||||
abi = Abi::Scalar(discr);
|
||||
let mut niche_align = niche.align(dl);
|
||||
if offset.bytes() == 0 && niche.size(dl) == size {
|
||||
abi = Abi::Scalar(niche);
|
||||
} else if let Abi::Aggregate { ref mut packed, .. } = abi {
|
||||
if offset.abi_align(discr_align) != offset {
|
||||
if offset.abi_align(niche_align) != offset {
|
||||
*packed = true;
|
||||
discr_align = dl.i8_align;
|
||||
niche_align = dl.i8_align;
|
||||
}
|
||||
}
|
||||
align = align.max(discr_align);
|
||||
primitive_align = primitive_align.max(discr_align);
|
||||
align = align.max(niche_align);
|
||||
primitive_align = primitive_align.max(niche_align);
|
||||
|
||||
return Ok(tcx.intern_layout(CachedLayout {
|
||||
variants: Variants::NicheFilling {
|
||||
nndiscr: i as u64,
|
||||
|
||||
discr,
|
||||
dataful_variant: i,
|
||||
niche,
|
||||
niche_value,
|
||||
variants: st,
|
||||
},
|
||||
fields: FieldPlacement::Arbitrary {
|
||||
|
@ -2048,7 +2049,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
|
|||
|
||||
// Discriminant field for enums (where applicable).
|
||||
Variants::Tagged { discr, .. } |
|
||||
Variants::NicheFilling { discr, .. } => {
|
||||
Variants::NicheFilling { niche: discr, .. } => {
|
||||
return cx.layout_of([discr.to_ty(tcx)][i]);
|
||||
}
|
||||
}
|
||||
|
@ -2084,30 +2085,48 @@ impl<'a, 'tcx> TyLayout<'tcx> {
|
|||
(self.size, self.align)
|
||||
}
|
||||
|
||||
/// Find the offset of a non-zero leaf field, starting from
|
||||
/// Find the offset of a niche leaf field, starting from
|
||||
/// the given type and recursing through aggregates.
|
||||
/// The tuple is `(offset, primitive, source_path)`.
|
||||
/// The tuple is `(offset, primitive, niche_value)`.
|
||||
// FIXME(eddyb) track value ranges and traverse already optimized enums.
|
||||
fn non_zero_field<C>(&self, cx: C)
|
||||
-> Result<Option<(Size, Primitive)>, LayoutError<'tcx>>
|
||||
fn find_niche<C>(&self, cx: C)
|
||||
-> Result<Option<(Size, Primitive, u128)>, LayoutError<'tcx>>
|
||||
where C: LayoutOf<Ty<'tcx>, TyLayout = Result<Self, LayoutError<'tcx>>> +
|
||||
HasTyCtxt<'tcx>
|
||||
{
|
||||
let tcx = cx.tcx();
|
||||
match (&self.variants, self.abi, &self.ty.sty) {
|
||||
// FIXME(eddyb) check this via value ranges on scalars.
|
||||
(_, Abi::Scalar(Int(I1, _)), _) => {
|
||||
Ok(Some((Size::from_bytes(0), Int(I8, false), 2)))
|
||||
}
|
||||
(_, Abi::Scalar(Int(I32, _)), &ty::TyChar) => {
|
||||
Ok(Some((Size::from_bytes(0), Int(I32, false), 0x10FFFF+1)))
|
||||
}
|
||||
(_, Abi::Scalar(Pointer), &ty::TyRef(..)) |
|
||||
(_, Abi::Scalar(Pointer), &ty::TyFnPtr(..)) => {
|
||||
Ok(Some((Size::from_bytes(0), Pointer)))
|
||||
Ok(Some((Size::from_bytes(0), Pointer, 0)))
|
||||
}
|
||||
(_, Abi::Scalar(Pointer), &ty::TyAdt(def, _)) if def.is_box() => {
|
||||
Ok(Some((Size::from_bytes(0), Pointer)))
|
||||
Ok(Some((Size::from_bytes(0), Pointer, 0)))
|
||||
}
|
||||
|
||||
// FIXME(eddyb) check this via value ranges on scalars.
|
||||
(&Variants::Tagged { discr, .. }, _, &ty::TyAdt(def, _)) => {
|
||||
if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) {
|
||||
Ok(Some((self.fields.offset(0), discr)))
|
||||
(&Variants::Tagged { discr, ref discr_range, .. }, _, _) => {
|
||||
// FIXME(eddyb) support negative/wrap-around discriminant ranges.
|
||||
if discr_range.start < discr_range.end {
|
||||
if discr_range.start > 0 {
|
||||
Ok(Some((self.fields.offset(0), discr, 0)))
|
||||
} else {
|
||||
let bits = discr.size(tcx).bits();
|
||||
assert!(bits <= 128);
|
||||
let max_value = !0u128 >> (128 - bits);
|
||||
if discr_range.end < max_value {
|
||||
Ok(Some((self.fields.offset(0), discr, discr_range.end + 1)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -2118,7 +2137,7 @@ impl<'a, 'tcx> TyLayout<'tcx> {
|
|||
let field = self.field(cx, 0)?;
|
||||
let offset = self.fields.offset(0);
|
||||
if let Abi::Scalar(value) = field.abi {
|
||||
Ok(Some((offset, value)))
|
||||
Ok(Some((offset, value, 0)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -2128,13 +2147,14 @@ impl<'a, 'tcx> TyLayout<'tcx> {
|
|||
_ => {
|
||||
if let FieldPlacement::Array { count, .. } = self.fields {
|
||||
if count > 0 {
|
||||
return self.field(cx, 0)?.non_zero_field(cx);
|
||||
return self.field(cx, 0)?.find_niche(cx);
|
||||
}
|
||||
}
|
||||
for i in 0..self.fields.count() {
|
||||
let r = self.field(cx, i)?.non_zero_field(cx)?;
|
||||
if let Some((offset, primitive)) = r {
|
||||
return Ok(Some((self.fields.offset(i) + offset, primitive)));
|
||||
let r = self.field(cx, i)?.find_niche(cx)?;
|
||||
if let Some((offset, primitive, niche_value)) = r {
|
||||
let offset = self.fields.offset(i) + offset;
|
||||
return Ok(Some((offset, primitive, niche_value)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
@ -2165,13 +2185,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Variants {
|
|||
variants.hash_stable(hcx, hasher);
|
||||
}
|
||||
NicheFilling {
|
||||
nndiscr,
|
||||
dataful_variant,
|
||||
ref niche,
|
||||
niche_value,
|
||||
ref variants,
|
||||
ref discr,
|
||||
} => {
|
||||
nndiscr.hash_stable(hcx, hasher);
|
||||
dataful_variant.hash_stable(hcx, hasher);
|
||||
niche.hash_stable(hcx, hasher);
|
||||
niche_value.hash_stable(hcx, hasher);
|
||||
variants.hash_stable(hcx, hasher);
|
||||
discr.hash_stable(hcx, hasher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1191,17 +1191,13 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
|
|||
}
|
||||
}).collect()
|
||||
}
|
||||
layout::Variants::NicheFilling {
|
||||
nndiscr,
|
||||
discr,
|
||||
..
|
||||
} => {
|
||||
let variant = self.layout.for_variant(nndiscr as usize);
|
||||
layout::Variants::NicheFilling { dataful_variant, .. } => {
|
||||
let variant = self.layout.for_variant(dataful_variant);
|
||||
// Create a description of the non-null variant
|
||||
let (variant_type_metadata, member_description_factory) =
|
||||
describe_enum_variant(cx,
|
||||
variant,
|
||||
&adt.variants[nndiscr as usize],
|
||||
&adt.variants[dataful_variant],
|
||||
OptimizedDiscriminant,
|
||||
self.containing_scope,
|
||||
self.span);
|
||||
|
@ -1239,8 +1235,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
|
|||
compute_field_path(cx, &mut name,
|
||||
self.layout,
|
||||
self.layout.fields.offset(0),
|
||||
discr.size(cx));
|
||||
name.push_str(&adt.variants[(1 - nndiscr) as usize].name.as_str());
|
||||
self.layout.field(cx, 0).size);
|
||||
name.push_str(&adt.variants[1 - dataful_variant].name.as_str());
|
||||
|
||||
// Create the (singleton) list of descriptions of union members.
|
||||
vec![
|
||||
|
|
|
@ -1122,22 +1122,29 @@ fn trans_const_adt<'a, 'tcx>(
|
|||
},
|
||||
_ => 0,
|
||||
};
|
||||
let discr_ty = l.field(ccx, 0).ty;
|
||||
let discr = C_int(ccx.layout_of(discr_ty).llvm_type(ccx), discr as i64);
|
||||
let discr_field = l.field(ccx, 0);
|
||||
let discr = C_int(discr_field.llvm_type(ccx), discr as i64);
|
||||
if let layout::Abi::Scalar(_) = l.abi {
|
||||
Const::new(discr, t)
|
||||
} else {
|
||||
let discr = Const::new(discr, discr_ty);
|
||||
let discr = Const::new(discr, discr_field.ty);
|
||||
build_const_struct(ccx, l.for_variant(variant_index), vals, Some(discr))
|
||||
}
|
||||
}
|
||||
layout::Variants::NicheFilling { nndiscr, .. } => {
|
||||
if variant_index as u64 == nndiscr {
|
||||
build_const_struct(ccx, l.for_variant(variant_index), vals, None)
|
||||
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
|
||||
if variant_index == dataful_variant {
|
||||
build_const_struct(ccx, l.for_variant(dataful_variant), vals, None)
|
||||
} else {
|
||||
// Always use null even if it's not the `discrfield`th
|
||||
// field; see #8506.
|
||||
Const::new(C_null(ccx.layout_of(t).llvm_type(ccx)), t)
|
||||
let niche = l.field(ccx, 0);
|
||||
let niche_llty = niche.llvm_type(ccx);
|
||||
// FIXME(eddyb) Check the actual primitive type here.
|
||||
let niche_llval = if niche_value == 0 {
|
||||
// HACK(eddyb) Using `C_null` as it works on all types.
|
||||
C_null(niche_llty)
|
||||
} else {
|
||||
C_uint_big(niche_llty, niche_value)
|
||||
};
|
||||
build_const_struct(ccx, l, &[Const::new(niche_llval, niche.ty)], None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use rustc::mir::tcx::LvalueTy;
|
|||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use base;
|
||||
use builder::Builder;
|
||||
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null};
|
||||
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, C_uint_big};
|
||||
use consts;
|
||||
use type_of::LayoutLlvmExt;
|
||||
use type_::Type;
|
||||
|
@ -72,10 +72,6 @@ impl Alignment {
|
|||
}
|
||||
}
|
||||
|
||||
fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
|
||||
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LvalueRef<'tcx> {
|
||||
/// Pointer to the contents of the lvalue
|
||||
|
@ -325,10 +321,17 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
|
|||
};
|
||||
bcx.intcast(lldiscr, cast_to, signed)
|
||||
}
|
||||
layout::Variants::NicheFilling { nndiscr, .. } => {
|
||||
let cmp = if nndiscr == 0 { llvm::IntEQ } else { llvm::IntNE };
|
||||
let zero = C_null(discr.layout.llvm_type(bcx.ccx));
|
||||
bcx.intcast(bcx.icmp(cmp, lldiscr, zero), cast_to, false)
|
||||
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
|
||||
let niche_llty = discr.layout.llvm_type(bcx.ccx);
|
||||
// FIXME(eddyb) Check the actual primitive type here.
|
||||
let niche_llval = if niche_value == 0 {
|
||||
// HACK(eddyb) Using `C_null` as it works on all types.
|
||||
C_null(niche_llty)
|
||||
} else {
|
||||
C_uint_big(niche_llty, niche_value)
|
||||
};
|
||||
let cmp = if dataful_variant == 0 { llvm::IntEQ } else { llvm::IntNE };
|
||||
bcx.intcast(bcx.icmp(cmp, lldiscr, niche_llval), cast_to, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -336,40 +339,42 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
|
|||
/// Set the discriminant for a new value of the given case of the given
|
||||
/// representation.
|
||||
pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) {
|
||||
let to = self.layout.ty.ty_adt_def().unwrap()
|
||||
.discriminant_for_variant(bcx.tcx(), variant_index)
|
||||
.to_u128_unchecked() as u64;
|
||||
match self.layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
assert_eq!(to, 0);
|
||||
assert_eq!(variant_index, index);
|
||||
}
|
||||
layout::Variants::Tagged { .. } => {
|
||||
let ptr = self.project_field(bcx, 0);
|
||||
let to = self.layout.ty.ty_adt_def().unwrap()
|
||||
.discriminant_for_variant(bcx.tcx(), variant_index)
|
||||
.to_u128_unchecked() as u64;
|
||||
bcx.store(C_int(ptr.layout.llvm_type(bcx.ccx), to as i64),
|
||||
ptr.llval, ptr.alignment.non_abi());
|
||||
}
|
||||
layout::Variants::NicheFilling { nndiscr, .. } => {
|
||||
if to != nndiscr {
|
||||
let use_memset = match self.layout.abi {
|
||||
layout::Abi::Scalar(_) => false,
|
||||
_ => target_sets_discr_via_memset(bcx)
|
||||
};
|
||||
if use_memset {
|
||||
// Issue #34427: As workaround for LLVM bug on
|
||||
// ARM, use memset of 0 on whole struct rather
|
||||
// than storing null to single target field.
|
||||
layout::Variants::NicheFilling { dataful_variant, niche_value, .. } => {
|
||||
if variant_index != dataful_variant {
|
||||
if bcx.sess().target.target.arch == "arm" ||
|
||||
bcx.sess().target.target.arch == "aarch64" {
|
||||
// Issue #34427: As workaround for LLVM bug on ARM,
|
||||
// use memset of 0 before assigning niche value.
|
||||
let llptr = bcx.pointercast(self.llval, Type::i8(bcx.ccx).ptr_to());
|
||||
let fill_byte = C_u8(bcx.ccx, 0);
|
||||
let (size, align) = self.layout.size_and_align();
|
||||
let size = C_usize(bcx.ccx, size.bytes());
|
||||
let align = C_u32(bcx.ccx, align.abi() as u32);
|
||||
base::call_memset(bcx, llptr, fill_byte, size, align, false);
|
||||
} else {
|
||||
let ptr = self.project_field(bcx, 0);
|
||||
bcx.store(C_null(ptr.layout.llvm_type(bcx.ccx)),
|
||||
ptr.llval, ptr.alignment.non_abi());
|
||||
}
|
||||
|
||||
let niche = self.project_field(bcx, 0);
|
||||
let niche_llty = niche.layout.llvm_type(bcx.ccx);
|
||||
// FIXME(eddyb) Check the actual primitive type here.
|
||||
let niche_llval = if niche_value == 0 {
|
||||
// HACK(eddyb) Using `C_null` as it works on all types.
|
||||
C_null(niche_llty)
|
||||
} else {
|
||||
C_uint_big(niche_llty, niche_value)
|
||||
};
|
||||
bcx.store(niche_llval, niche.llval, niche.alignment.non_abi());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
// compile-flags: -Z print-type-sizes
|
||||
|
||||
// This file illustrates how enums with a non-null field are handled,
|
||||
// modelled after cases like `Option<&u32>` and such.
|
||||
// This file illustrates how niche-filling enums are handled,
|
||||
// modelled after cases like `Option<&u32>`, `Option<bool>` and such.
|
||||
//
|
||||
// It uses NonZero directly, rather than `&_` or `Unique<_>`, because
|
||||
// the test is not set up to deal with target-dependent pointer width.
|
||||
|
@ -72,4 +72,8 @@ pub fn main() {
|
|||
let _x: MyOption<NonZero<u32>> = Default::default();
|
||||
let _y: EmbeddedDiscr = Default::default();
|
||||
let _z: MyOption<IndirectNonZero<u32>> = Default::default();
|
||||
let _a: MyOption<bool> = Default::default();
|
||||
let _b: MyOption<char> = Default::default();
|
||||
let _c: MyOption<std::cmp::Ordering> = Default::default();
|
||||
let _b: MyOption<MyOption<u8>> = Default::default();
|
||||
}
|
|
@ -19,9 +19,35 @@ print-type-size field `.val`: 4 bytes
|
|||
print-type-size field `.post`: 2 bytes
|
||||
print-type-size field `.pre`: 1 bytes
|
||||
print-type-size end padding: 1 bytes
|
||||
print-type-size type: `MyOption<char>`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 4 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
||||
print-type-size type: `MyOption<core::nonzero::NonZero<u32>>`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 4 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
||||
print-type-size type: `core::nonzero::NonZero<u32>`: 4 bytes, alignment: 4 bytes
|
||||
print-type-size field `.0`: 4 bytes
|
||||
print-type-size type: `MyOption<MyOption<u8>>`: 2 bytes, alignment: 1 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 2 bytes
|
||||
print-type-size field `.0`: 2 bytes
|
||||
print-type-size type: `MyOption<u8>`: 2 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 1 bytes
|
||||
print-type-size field `.0`: 1 bytes
|
||||
print-type-size type: `MyOption<bool>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 1 bytes
|
||||
print-type-size field `.0`: 1 bytes
|
||||
print-type-size type: `MyOption<core::cmp::Ordering>`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size variant `None`: 0 bytes
|
||||
print-type-size variant `Some`: 1 bytes
|
||||
print-type-size field `.0`: 1 bytes
|
||||
print-type-size type: `core::cmp::Ordering`: 1 bytes, alignment: 1 bytes
|
||||
print-type-size discriminant: 1 bytes
|
||||
print-type-size variant `Less`: 0 bytes
|
||||
print-type-size variant `Equal`: 0 bytes
|
||||
print-type-size variant `Greater`: 0 bytes
|
Loading…
Add table
Add a link
Reference in a new issue