Auto merge of #36151 - camlorn:struct_layout_optimization, r=eddyb

refactor to remove trans::adt and make rustc::ty::layout authoritative

I asked on IRC about optimizing struct layout by reordering fields from most-aligned to least-aligned and somehow ended up getting talked into doing this.  The goal here is to make `layout` authoritative and to remove `adt`.  The former has been accomplished by reimplementing `represent_type_uncached` and the latter is in progress.  @eddyb thought I should make the PR now.

My plan is to reserve the actual optimization for a second PR, as this work is useful by itself.
This commit is contained in:
bors 2016-09-25 18:47:00 -07:00 committed by GitHub
commit 9966397b61
21 changed files with 536 additions and 1004 deletions

View File

@ -489,6 +489,9 @@ pub struct GlobalCtxt<'tcx> {
/// Cache for layouts computed from types.
pub layout_cache: RefCell<FnvHashMap<Ty<'tcx>, &'tcx Layout>>,
/// Used to prevent layout from recursing too deeply.
pub layout_depth: Cell<usize>,
/// Map from function to the `#[derive]` mode that it's defining. Only used
/// by `rustc-macro` crates.
pub derive_macros: RefCell<NodeMap<token::InternedString>>,
@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
crate_name: token::intern_and_get_ident(crate_name),
data_layout: data_layout,
layout_cache: RefCell::new(FnvHashMap()),
layout_depth: Cell::new(0),
derive_macros: RefCell::new(NodeMap()),
}, f)
}

View File

@ -328,6 +328,42 @@ pub enum Integer {
}
impl Integer {
pub fn size(&self) -> Size {
match *self {
I1 => Size::from_bits(1),
I8 => Size::from_bytes(1),
I16 => Size::from_bytes(2),
I32 => Size::from_bytes(4),
I64 => Size::from_bytes(8),
}
}
pub fn align(&self, dl: &TargetDataLayout)-> Align {
match *self {
I1 => dl.i1_align,
I8 => dl.i8_align,
I16 => dl.i16_align,
I32 => dl.i32_align,
I64 => dl.i64_align,
}
}
pub fn to_ty<'a, 'tcx>(&self, tcx: &ty::TyCtxt<'a, 'tcx, 'tcx>,
signed: bool) -> Ty<'tcx> {
match (*self, signed) {
(I1, false) => tcx.types.u8,
(I8, false) => tcx.types.u8,
(I16, false) => tcx.types.u16,
(I32, false) => tcx.types.u32,
(I64, false) => tcx.types.u64,
(I1, true) => tcx.types.i8,
(I8, true) => tcx.types.i8,
(I16, true) => tcx.types.i16,
(I32, true) => tcx.types.i32,
(I64, true) => tcx.types.i64,
}
}
/// Find the smallest Integer type which can represent the signed value.
pub fn fit_signed(x: i64) -> Integer {
match x {
@ -350,6 +386,18 @@ impl Integer {
}
}
/// Find the smallest integer with the given alignment.
pub fn for_abi_align(dl: &TargetDataLayout, align: Align) -> Option<Integer> {
let wanted = align.abi();
for &candidate in &[I8, I16, I32, I64] {
let ty = Int(candidate);
if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
return Some(candidate);
}
}
None
}
/// Get the Integer type from an attr::IntType.
pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer {
match ity {
@ -623,6 +671,15 @@ impl<'a, 'gcx, 'tcx> Struct {
}
Ok(None)
}
pub fn offset_of_field(&self, index: usize) -> Size {
assert!(index < self.offset_after_field.len());
if index == 0 {
Size::from_bytes(0)
} else {
self.offset_after_field[index-1]
}
}
}
/// An untagged union.
@ -912,7 +969,7 @@ impl<'a, 'gcx, 'tcx> Layout {
Univariant { variant: unit, non_zero: false }
}
// Tuples.
// Tuples and closures.
ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
ty::TyTuple(tys) => {
let mut st = Struct::new(dl, false);
@ -972,10 +1029,10 @@ impl<'a, 'gcx, 'tcx> Layout {
});
}
if def.variants.len() == 1 {
if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny {
// Struct, or union, or univariant enum equivalent to a struct.
// (Typechecking will reject discriminant-sizing attrs.)
assert!(!def.is_enum() || hint == attr::ReprAny);
let fields = def.variants[0].fields.iter().map(|field| {
field.ty(tcx, substs).layout(infcx)
});
@ -1103,20 +1160,7 @@ impl<'a, 'gcx, 'tcx> Layout {
// won't be so conservative.
// Use the initial field alignment
let wanted = start_align.abi();
let mut ity = min_ity;
for &candidate in &[I16, I32, I64] {
let ty = Int(candidate);
if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
ity = candidate;
break;
}
}
// FIXME(eddyb) conservative only to avoid diverging from trans::adt.
if align.abi() != start_align.abi() {
ity = min_ity;
}
let mut ity = Integer::for_abi_align(dl, start_align).unwrap_or(min_ity);
// If the alignment is not larger than the chosen discriminant size,
// don't use the alignment as the final size.

View File

@ -608,10 +608,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
}
}
let rec_limit = tcx.sess.recursion_limit.get();
let depth = tcx.layout_depth.get();
if depth > rec_limit {
tcx.sess.fatal(
&format!("overflow representing the type `{}`", self));
}
tcx.layout_depth.set(depth+1);
let layout = Layout::compute_uncached(self, infcx)?;
if can_cache {
tcx.layout_cache.borrow_mut().insert(self, layout);
}
tcx.layout_depth.set(depth);
Ok(layout)
}

View File

@ -24,7 +24,7 @@ use cabi_s390x;
use cabi_mips;
use cabi_mips64;
use cabi_asmjs;
use machine::{llalign_of_min, llsize_of, llsize_of_real, llsize_of_store};
use machine::{llalign_of_min, llsize_of, llsize_of_alloc};
use type_::Type;
use type_of;
@ -102,7 +102,7 @@ impl ArgType {
// Wipe old attributes, likely not valid through indirection.
self.attrs = llvm::Attributes::default();
let llarg_sz = llsize_of_real(ccx, self.ty);
let llarg_sz = llsize_of_alloc(ccx, self.ty);
// For non-immediate arguments the callee gets its own copy of
// the value on the stack, so there are no aliases. It's also
@ -200,7 +200,7 @@ impl ArgType {
base::call_memcpy(bcx,
bcx.pointercast(dst, Type::i8p(ccx)),
bcx.pointercast(llscratch, Type::i8p(ccx)),
C_uint(ccx, llsize_of_store(ccx, self.ty)),
C_uint(ccx, llsize_of_alloc(ccx, self.ty)),
cmp::min(llalign_of_min(ccx, self.ty),
llalign_of_min(ccx, ty)) as u32);
@ -327,7 +327,7 @@ impl FnType {
if let Layout::CEnum { signed, .. } = *ccx.layout_of(ty) {
arg.signedness = Some(signed);
}
if llsize_of_real(ccx, arg.ty) == 0 {
if llsize_of_alloc(ccx, arg.ty) == 0 {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.
// The same is true for s390x-unknown-linux-gnu.
@ -358,7 +358,7 @@ impl FnType {
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyBox(ty) => {
let llty = type_of::sizing_type_of(ccx, ty);
let llsz = llsize_of_real(ccx, llty);
let llsz = llsize_of_alloc(ccx, llty);
ret.attrs.set_dereferenceable(llsz);
}
_ => {}
@ -427,7 +427,7 @@ impl FnType {
} else {
if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
let llty = type_of::sizing_type_of(ccx, inner);
let llsz = llsize_of_real(ccx, llty);
let llsz = llsize_of_alloc(ccx, llty);
arg.attrs.set_dereferenceable(llsz);
}
args.push(arg);
@ -469,8 +469,8 @@ impl FnType {
return;
}
let size = llsize_of_real(ccx, llty);
if size > llsize_of_real(ccx, ccx.int_type()) {
let size = llsize_of_alloc(ccx, llty);
if size > llsize_of_alloc(ccx, ccx.int_type()) {
arg.make_indirect(ccx);
} else if size > 0 {
// We want to pass small aggregates as immediates, but using

File diff suppressed because it is too large Load Diff

View File

@ -466,32 +466,27 @@ pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
store_fat_ptr(bcx, base, info, dst, dst_ty);
}
// This can be extended to enums and tuples in the future.
(&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => {
(&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) => {
assert_eq!(def_a, def_b);
let src_repr = adt::represent_type(bcx.ccx(), src_ty);
let src_fields = match &*src_repr {
&adt::Repr::Univariant(ref s) => &s.fields,
_ => bug!("struct has non-univariant repr"),
};
let dst_repr = adt::represent_type(bcx.ccx(), dst_ty);
let dst_fields = match &*dst_repr {
&adt::Repr::Univariant(ref s) => &s.fields,
_ => bug!("struct has non-univariant repr"),
};
let src_fields = def_a.variants[0].fields.iter().map(|f| {
monomorphize::field_ty(bcx.tcx(), substs_a, f)
});
let dst_fields = def_b.variants[0].fields.iter().map(|f| {
monomorphize::field_ty(bcx.tcx(), substs_b, f)
});
let src = adt::MaybeSizedValue::sized(src);
let dst = adt::MaybeSizedValue::sized(dst);
let iter = src_fields.iter().zip(dst_fields).enumerate();
let iter = src_fields.zip(dst_fields).enumerate();
for (i, (src_fty, dst_fty)) in iter {
if type_is_zero_size(bcx.ccx(), dst_fty) {
continue;
}
let src_f = adt::trans_field_ptr(bcx, &src_repr, src, Disr(0), i);
let dst_f = adt::trans_field_ptr(bcx, &dst_repr, dst, Disr(0), i);
let src_f = adt::trans_field_ptr(bcx, src_ty, src, Disr(0), i);
let dst_f = adt::trans_field_ptr(bcx, dst_ty, dst, Disr(0), i);
if src_fty == dst_fty {
memcpy_ty(bcx, dst_f, src_f, src_fty);
} else {
@ -1164,11 +1159,10 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
if !fcx.fn_ty.ret.is_ignore() {
let dest = fcx.llretslotptr.get().unwrap();
let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value
let repr = adt::represent_type(ccx, sig.output);
let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
let mut arg_idx = 0;
for (i, arg_ty) in sig.inputs.into_iter().enumerate() {
let lldestptr = adt::trans_field_ptr(bcx, &repr, dest_val, Disr::from(disr), i);
let lldestptr = adt::trans_field_ptr(bcx, sig.output, dest_val, Disr::from(disr), i);
let arg = &fcx.fn_ty.args[arg_idx];
arg_idx += 1;
let b = &bcx.build();
@ -1181,7 +1175,7 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
arg.store_fn_arg(b, &mut llarg_idx, lldestptr);
}
}
adt::trans_set_discr(bcx, &repr, dest, disr);
adt::trans_set_discr(bcx, sig.output, dest, disr);
}
fcx.finish(bcx, DebugLoc::None);

View File

@ -875,12 +875,6 @@ pub fn const_get_elt(v: ValueRef, us: &[c_uint])
}
}
pub fn const_to_int(v: ValueRef) -> i64 {
unsafe {
llvm::LLVMConstIntGetSExtValue(v)
}
}
pub fn const_to_uint(v: ValueRef) -> u64 {
unsafe {
llvm::LLVMConstIntGetZExtValue(v)

View File

@ -17,7 +17,6 @@ use rustc::hir::def_id::DefId;
use rustc::traits;
use rustc::mir::mir_map::MirMap;
use rustc::mir::repr as mir;
use adt;
use base;
use builder::Builder;
use common::BuilderRef_res;
@ -142,7 +141,6 @@ pub struct LocalCrateContext<'tcx> {
lltypes: RefCell<FnvHashMap<Ty<'tcx>, Type>>,
llsizingtypes: RefCell<FnvHashMap<Ty<'tcx>, Type>>,
adt_reprs: RefCell<FnvHashMap<Ty<'tcx>, Rc<adt::Repr<'tcx>>>>,
type_hashcodes: RefCell<FnvHashMap<Ty<'tcx>, String>>,
int_type: Type,
opaque_vec_type: Type,
@ -677,7 +675,6 @@ impl<'tcx> LocalCrateContext<'tcx> {
statics_to_rauw: RefCell::new(Vec::new()),
lltypes: RefCell::new(FnvHashMap()),
llsizingtypes: RefCell::new(FnvHashMap()),
adt_reprs: RefCell::new(FnvHashMap()),
type_hashcodes: RefCell::new(FnvHashMap()),
int_type: Type::from_ref(ptr::null_mut()),
opaque_vec_type: Type::from_ref(ptr::null_mut()),
@ -918,10 +915,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local().llsizingtypes
}
pub fn adt_reprs<'a>(&'a self) -> &'a RefCell<FnvHashMap<Ty<'tcx>, Rc<adt::Repr<'tcx>>>> {
&self.local().adt_reprs
}
pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
&self.shared.symbol_hasher
}
@ -994,7 +987,11 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
pub fn layout_of(&self, ty: Ty<'tcx>) -> &'tcx ty::layout::Layout {
self.tcx().infer_ctxt(None, None, traits::Reveal::All).enter(|infcx| {
ty.layout(&infcx).unwrap_or_else(|e| {
bug!("failed to get layout for `{}`: {}", ty, e);
match e {
ty::layout::LayoutError::SizeOverflow(_) =>
self.sess().fatal(&e.to_string()),
_ => bug!("failed to get layout for `{}`: {}", ty, e)
}
})
})
}

View File

@ -27,10 +27,10 @@ use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DI
use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::hir;
use {type_of, adt, machine, monomorphize};
use {type_of, machine, monomorphize};
use common::CrateContext;
use type_::Type;
use rustc::ty::{self, AdtKind, Ty};
use rustc::ty::{self, AdtKind, Ty, layout};
use session::config;
use util::nodemap::FnvHashMap;
use util::common::path2cstr;
@ -40,7 +40,6 @@ use std::ffi::CString;
use std::path::Path;
use std::ptr;
use std::rc::Rc;
use syntax;
use syntax::util::interner::Interner;
use syntax::ast;
use syntax::parse::token;
@ -1281,7 +1280,7 @@ fn prepare_union_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
// offset of zero bytes).
struct EnumMemberDescriptionFactory<'tcx> {
enum_type: Ty<'tcx>,
type_rep: Rc<adt::Repr<'tcx>>,
type_rep: &'tcx layout::Layout,
discriminant_type_metadata: Option<DIType>,
containing_scope: DIScope,
file_metadata: DIFile,
@ -1292,11 +1291,15 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>)
-> Vec<MemberDescription> {
let adt = &self.enum_type.ty_adt_def().unwrap();
let substs = match self.enum_type.sty {
ty::TyAdt(def, ref s) if def.adt_kind() == AdtKind::Enum => s,
_ => bug!("{} is not an enum", self.enum_type)
};
match *self.type_rep {
adt::General(_, ref struct_defs) => {
layout::General { ref variants, .. } => {
let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
.expect(""));
struct_defs
variants
.iter()
.enumerate()
.map(|(i, struct_def)| {
@ -1327,7 +1330,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
}
}).collect()
},
adt::Univariant(ref struct_def) => {
layout::Univariant{ ref variant, .. } => {
assert!(adt.variants.len() <= 1);
if adt.variants.is_empty() {
@ -1338,7 +1341,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
member_description_factory) =
describe_enum_variant(cx,
self.enum_type,
struct_def,
variant,
&adt.variants[0],
NoDiscriminant,
self.containing_scope,
@ -1362,16 +1365,17 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
]
}
}
adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
layout::RawNullablePointer { nndiscr: non_null_variant_index, .. } => {
// As far as debuginfo is concerned, the pointer this enum
// represents is still wrapped in a struct. This is to make the
// DWARF representation of enums uniform.
// First create a description of the artificial wrapper struct:
let non_null_variant = &adt.variants[non_null_variant_index.0 as usize];
let non_null_variant = &adt.variants[non_null_variant_index as usize];
let non_null_variant_name = non_null_variant.name.as_str();
// The llvm type and metadata of the pointer
let nnty = monomorphize::field_ty(cx.tcx(), &substs, &non_null_variant.fields[0] );
let non_null_llvm_type = type_of::type_of(cx, nnty);
let non_null_type_metadata = type_metadata(cx, nnty, self.span);
@ -1416,7 +1420,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
// Encode the information about the null variant in the union
// member's name.
let null_variant_index = (1 - non_null_variant_index.0) as usize;
let null_variant_index = (1 - non_null_variant_index) as usize;
let null_variant_name = adt.variants[null_variant_index].name;
let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
0,
@ -1434,7 +1438,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
}
]
},
adt::StructWrappedNullablePointer { nonnull: ref struct_def,
layout::StructWrappedNullablePointer { nonnull: ref struct_def,
nndiscr,
ref discrfield, ..} => {
// Create a description of the non-null variant
@ -1442,7 +1446,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
describe_enum_variant(cx,
self.enum_type,
struct_def,
&adt.variants[nndiscr.0 as usize],
&adt.variants[nndiscr as usize],
OptimizedDiscriminant,
self.containing_scope,
self.span);
@ -1457,7 +1461,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
// Encode the information about the null variant in the union
// member's name.
let null_variant_index = (1 - nndiscr.0) as usize;
let null_variant_index = (1 - nndiscr) as usize;
let null_variant_name = adt.variants[null_variant_index].name;
let discrfield = discrfield.iter()
.skip(1)
@ -1478,9 +1482,8 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
}
]
},
adt::CEnum(..) | adt::UntaggedUnion(..) => {
span_bug!(self.span, "This should be unreachable.")
}
layout::CEnum { .. } => span_bug!(self.span, "This should be unreachable."),
ref l @ _ => bug!("Not an enum layout: {:#?}", l)
}
}
}
@ -1523,16 +1526,39 @@ enum EnumDiscriminantInfo {
// full RecursiveTypeDescription.
fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
enum_type: Ty<'tcx>,
struct_def: &adt::Struct<'tcx>,
struct_def: &layout::Struct,
variant: ty::VariantDef<'tcx>,
discriminant_info: EnumDiscriminantInfo,
containing_scope: DIScope,
span: Span)
-> (DICompositeType, Type, MemberDescriptionFactory<'tcx>) {
let substs = match enum_type.sty {
ty::TyAdt(def, s) if def.adt_kind() == AdtKind::Enum => s,
ref t @ _ => bug!("{:#?} is not an enum", t)
};
let maybe_discr_and_signed: Option<(layout::Integer, bool)> = match *cx.layout_of(enum_type) {
layout::CEnum {discr, ..} => Some((discr, true)),
layout::General{discr, ..} => Some((discr, false)),
layout::Univariant { .. }
| layout::RawNullablePointer { .. }
| layout::StructWrappedNullablePointer { .. } => None,
ref l @ _ => bug!("This should be unreachable. Type is {:#?} layout is {:#?}", enum_type, l)
};
let mut field_tys = variant.fields.iter().map(|f: ty::FieldDef<'tcx>| {
monomorphize::field_ty(cx.tcx(), &substs, f)
}).collect::<Vec<_>>();
if let Some((discr, signed)) = maybe_discr_and_signed {
field_tys.insert(0, discr.to_ty(&cx.tcx(), signed));
}
let variant_llvm_type =
Type::struct_(cx, &struct_def.fields
Type::struct_(cx, &field_tys
.iter()
.map(|&t| type_of::type_of(cx, t))
.map(|t| type_of::type_of(cx, t))
.collect::<Vec<_>>()
,
struct_def.packed);
@ -1578,7 +1604,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
// Build an array of (field name, field type) pairs to be captured in the factory closure.
let args: Vec<(String, Ty)> = arg_names.iter()
.zip(&struct_def.fields)
.zip(field_tys.iter())
.map(|(s, &t)| (s.to_string(), t))
.collect();
@ -1615,7 +1641,6 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let file_metadata = unknown_file_metadata(cx);
let variants = &enum_type.ty_adt_def().unwrap().variants;
let enumerators_metadata: Vec<DIDescriptor> = variants
.iter()
.map(|v| {
@ -1630,7 +1655,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
})
.collect();
let discriminant_type_metadata = |inttype: syntax::attr::IntType| {
let discriminant_type_metadata = |inttype: layout::Integer, signed: bool| {
let disr_type_key = (enum_def_id, inttype);
let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types
.borrow()
@ -1638,12 +1663,12 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
match cached_discriminant_type_metadata {
Some(discriminant_type_metadata) => discriminant_type_metadata,
None => {
let discriminant_llvm_type = adt::ll_inttype(cx, inttype);
let discriminant_llvm_type = Type::from_integer(cx, inttype);
let (discriminant_size, discriminant_align) =
size_and_align_of(cx, discriminant_llvm_type);
let discriminant_base_type_metadata =
type_metadata(cx,
adt::ty_of_inttype(cx.tcx(), inttype),
inttype.to_ty(&cx.tcx(), signed),
syntax_pos::DUMMY_SP);
let discriminant_name = get_enum_discriminant_name(cx, enum_def_id);
@ -1670,16 +1695,17 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
};
let type_rep = adt::represent_type(cx, enum_type);
let type_rep = cx.layout_of(enum_type);
let discriminant_type_metadata = match *type_rep {
adt::CEnum(inttype, ..) => {
return FinalMetadata(discriminant_type_metadata(inttype))
layout::CEnum { discr, signed, .. } => {
return FinalMetadata(discriminant_type_metadata(discr, signed))
},
adt::RawNullablePointer { .. } |
adt::StructWrappedNullablePointer { .. } |
adt::Univariant(..) | adt::UntaggedUnion(..) => None,
adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
layout::RawNullablePointer { .. } |
layout::StructWrappedNullablePointer { .. } |
layout::Univariant { .. } => None,
layout::General { discr, .. } => Some(discriminant_type_metadata(discr, false)),
ref l @ _ => bug!("Not an enum layout: {:#?}", l)
};
let enum_llvm_type = type_of::type_of(cx, enum_type);
@ -1715,7 +1741,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
enum_llvm_type,
EnumMDF(EnumMemberDescriptionFactory {
enum_type: enum_type,
type_rep: type_rep.clone(),
type_rep: type_rep,
discriminant_type_metadata: discriminant_type_metadata,
containing_scope: containing_scope,
file_metadata: file_metadata,

View File

@ -43,7 +43,7 @@ use std::ptr;
use syntax_pos::{self, Span, Pos};
use syntax::ast;
use syntax::attr::IntType;
use rustc::ty::layout;
pub mod gdb;
mod utils;
@ -69,7 +69,7 @@ pub struct CrateDebugContext<'tcx> {
builder: DIBuilderRef,
current_debug_location: Cell<InternalDebugLocation>,
created_files: RefCell<FnvHashMap<String, DIFile>>,
created_enum_disr_types: RefCell<FnvHashMap<(DefId, IntType), DIType>>,
created_enum_disr_types: RefCell<FnvHashMap<(DefId, layout::Integer), DIType>>,
type_map: RefCell<TypeMap<'tcx>>,
namespace_map: RefCell<DefIdMap<DIScope>>,

View File

@ -513,7 +513,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
let _icx = push_ctxt("drop_structural_ty");
fn iter_variant<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
repr: &adt::Repr<'tcx>,
t: Ty<'tcx>,
av: adt::MaybeSizedValue,
variant: ty::VariantDef<'tcx>,
substs: &Substs<'tcx>)
@ -525,7 +525,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
for (i, field) in variant.fields.iter().enumerate() {
let arg = monomorphize::field_ty(tcx, substs, field);
cx = drop_ty(cx,
adt::trans_field_ptr(cx, repr, av, Disr::from(variant.disr_val), i),
adt::trans_field_ptr(cx, t, av, Disr::from(variant.disr_val), i),
arg, DebugLoc::None);
}
return cx;
@ -543,9 +543,8 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
let mut cx = cx;
match t.sty {
ty::TyClosure(_, ref substs) => {
let repr = adt::represent_type(cx.ccx(), t);
for (i, upvar_ty) in substs.upvar_tys.iter().enumerate() {
let llupvar = adt::trans_field_ptr(cx, &repr, value, Disr(0), i);
let llupvar = adt::trans_field_ptr(cx, t, value, Disr(0), i);
cx = drop_ty(cx, llupvar, upvar_ty, DebugLoc::None);
}
}
@ -562,18 +561,16 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|bb, vv| drop_ty(bb, vv, unit_ty, DebugLoc::None));
}
ty::TyTuple(ref args) => {
let repr = adt::represent_type(cx.ccx(), t);
for (i, arg) in args.iter().enumerate() {
let llfld_a = adt::trans_field_ptr(cx, &repr, value, Disr(0), i);
let llfld_a = adt::trans_field_ptr(cx, t, value, Disr(0), i);
cx = drop_ty(cx, llfld_a, *arg, DebugLoc::None);
}
}
ty::TyAdt(adt, substs) => match adt.adt_kind() {
AdtKind::Struct => {
let repr = adt::represent_type(cx.ccx(), t);
let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None);
for (i, &Field(_, field_ty)) in fields.iter().enumerate() {
let llfld_a = adt::trans_field_ptr(cx, &repr, value, Disr::from(discr), i);
let llfld_a = adt::trans_field_ptr(cx, t, value, Disr::from(discr), i);
let val = if type_is_sized(cx.tcx(), field_ty) {
llfld_a
@ -593,18 +590,16 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
AdtKind::Enum => {
let fcx = cx.fcx;
let ccx = fcx.ccx;
let repr = adt::represent_type(ccx, t);
let n_variants = adt.variants.len();
// NB: we must hit the discriminant first so that structural
// comparison know not to proceed when the discriminants differ.
match adt::trans_switch(cx, &repr, av, false) {
match adt::trans_switch(cx, t, av, false) {
(adt::BranchKind::Single, None) => {
if n_variants != 0 {
assert!(n_variants == 1);
cx = iter_variant(cx, &repr, adt::MaybeSizedValue::sized(av),
cx = iter_variant(cx, t, adt::MaybeSizedValue::sized(av),
&adt.variants[0], substs);
}
}
@ -633,10 +628,10 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
let variant_cx = fcx.new_block(&format!("enum-iter-variant-{}",
&variant.disr_val
.to_string()));
let case_val = adt::trans_case(cx, &repr, Disr::from(variant.disr_val));
let case_val = adt::trans_case(cx, t, Disr::from(variant.disr_val));
AddCase(llswitch, case_val, variant_cx.llbb);
let variant_cx = iter_variant(variant_cx,
&repr,
t,
value,
variant,
substs);

View File

@ -418,8 +418,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
let val_ty = substs.type_at(0);
match val_ty.sty {
ty::TyAdt(adt, ..) if adt.is_enum() => {
let repr = adt::represent_type(ccx, val_ty);
adt::trans_get_discr(bcx, &repr, llargs[0],
adt::trans_get_discr(bcx, val_ty, llargs[0],
Some(llret_ty), true)
}
_ => C_null(llret_ty)
@ -629,13 +628,10 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
// destructors, and the contents are SIMD
// etc.
assert!(!bcx.fcx.type_needs_drop(arg_type));
let repr = adt::represent_type(bcx.ccx(), arg_type);
let repr_ptr = &repr;
let arg = adt::MaybeSizedValue::sized(llarg);
(0..contents.len())
.map(|i| {
Load(bcx, adt::trans_field_ptr(bcx, repr_ptr, arg, Disr(0), i))
Load(bcx, adt::trans_field_ptr(bcx, arg_type, arg, Disr(0), i))
})
.collect()
}

View File

@ -24,13 +24,6 @@ pub type llalign = u32;
// ______________________________________________________________________
// compute sizeof / alignof
// Returns the number of bytes clobbered by a Store to this type.
pub fn llsize_of_store(cx: &CrateContext, ty: Type) -> llsize {
unsafe {
return llvm::LLVMStoreSizeOfType(cx.td(), ty.to_ref());
}
}
// Returns the number of bytes between successive elements of type T in an
// array of T. This is the "ABI" size. It includes any ABI-mandated padding.
pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> llsize {
@ -39,28 +32,6 @@ pub fn llsize_of_alloc(cx: &CrateContext, ty: Type) -> llsize {
}
}
// Returns, as near as we can figure, the "real" size of a type. As in, the
// bits in this number of bytes actually carry data related to the datum
// with the type. Not junk, accidentally-damaged words, or whatever.
// Note that padding of the type will be included for structs, but not for the
// other types (i.e. SIMD types).
// Rounds up to the nearest byte though, so if you have a 1-bit
// value, we return 1 here, not 0. Most of rustc works in bytes. Be warned
// that LLVM *does* distinguish between e.g. a 1-bit value and an 8-bit value
// at the codegen level! In general you should prefer `llbitsize_of_real`
// below.
pub fn llsize_of_real(cx: &CrateContext, ty: Type) -> llsize {
unsafe {
let nbits = llvm::LLVMSizeOfTypeInBits(cx.td(), ty.to_ref());
if nbits & 7 != 0 {
// Not an even number of bytes, spills into "next" byte.
1 + (nbits >> 3)
} else {
nbits >> 3
}
}
}
/// Returns the "real" size of the type in bits.
pub fn llbitsize_of_real(cx: &CrateContext, ty: Type) -> llbits {
unsafe {

View File

@ -139,9 +139,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => {
let discr_lvalue = self.trans_lvalue(&bcx, discr);
let ty = discr_lvalue.ty.to_ty(bcx.tcx());
let repr = adt::represent_type(bcx.ccx(), ty);
let discr = bcx.with_block(|bcx|
adt::trans_get_discr(bcx, &repr, discr_lvalue.llval, None, true)
adt::trans_get_discr(bcx, ty, discr_lvalue.llval, None, true)
);
let mut bb_hist = FnvHashMap();
@ -167,7 +166,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if default_bb != Some(target) {
let llbb = llblock(self, target);
let llval = bcx.with_block(|bcx| adt::trans_case(
bcx, &repr, Disr::from(adt_variant.disr_val)));
bcx, ty, Disr::from(adt_variant.disr_val)));
build::AddCase(switch, llval, llbb)
}
}
@ -701,10 +700,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// Handle both by-ref and immediate tuples.
match tuple.val {
Ref(llval) => {
let base_repr = adt::represent_type(bcx.ccx(), tuple.ty);
let base = adt::MaybeSizedValue::sized(llval);
for (n, &ty) in arg_types.iter().enumerate() {
let ptr = adt::trans_field_ptr_builder(bcx, &base_repr, base, Disr(0), n);
let ptr = adt::trans_field_ptr_builder(bcx, tuple.ty, base, Disr(0), n);
let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
let (lldata, llextra) = load_fat_ptr(bcx, ptr);
Pair(lldata, llextra)

View File

@ -23,9 +23,9 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use {abi, adt, base, Disr};
use {abi, adt, base, Disr, machine};
use callee::Callee;
use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty, type_is_sized};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
use common::{const_to_opt_int, const_to_opt_uint};
@ -441,8 +441,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
}
}
mir::ProjectionElem::Field(ref field, _) => {
let base_repr = adt::represent_type(self.ccx, tr_base.ty);
let llprojected = adt::const_get_field(&base_repr, base.llval,
let llprojected = adt::const_get_field(self.ccx, tr_base.ty, base.llval,
Disr(0), field.index());
let llextra = if is_sized {
ptr::null_mut()
@ -585,9 +584,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
}
_ => Disr(0)
};
let repr = adt::represent_type(self.ccx, dest_ty);
Const::new(
adt::trans_const(self.ccx, &repr, disr, &fields),
adt::trans_const(self.ccx, dest_ty, disr, &fields),
dest_ty
)
}
@ -658,8 +656,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
let ll_t_out = type_of::immediate_type_of(self.ccx, cast_ty);
let llval = operand.llval;
let signed = if let CastTy::Int(IntTy::CEnum) = r_t_in {
let repr = adt::represent_type(self.ccx, operand.ty);
adt::is_discr_signed(&repr)
let l = self.ccx.layout_of(operand.ty);
adt::is_discr_signed(&l)
} else {
operand.ty.is_signed()
};
@ -735,7 +733,12 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
let base = match tr_lvalue.base {
Base::Value(llval) => {
let align = type_of::align_of(self.ccx, ty);
// FIXME: may be wrong for &*(&simd_vec as &fmt::Debug)
let align = if type_is_sized(self.ccx.tcx(), ty) {
type_of::align_of(self.ccx, ty)
} else {
self.ccx.tcx().data_layout.pointer_align.abi() as machine::llalign
};
if bk == mir::BorrowKind::Mut {
consts::addr_of_mut(self.ccx, llval, align, "ref_mut")
} else {

View File

@ -152,7 +152,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::ProjectionElem::Deref => bug!(),
mir::ProjectionElem::Field(ref field, _) => {
let base_ty = tr_base.ty.to_ty(tcx);
let base_repr = adt::represent_type(ccx, base_ty);
let discr = match tr_base.ty {
LvalueTy::Ty { .. } => 0,
LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v,
@ -164,7 +163,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
} else {
adt::MaybeSizedValue::unsized_(tr_base.llval, tr_base.llextra)
};
let llprojected = adt::trans_field_ptr_builder(bcx, &base_repr, base,
let llprojected = adt::trans_field_ptr_builder(bcx, base_ty, base,
Disr(discr), field.index());
let llextra = if is_sized {
ptr::null_mut()

View File

@ -111,10 +111,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
mir::Rvalue::Aggregate(ref kind, ref operands) => {
match *kind {
mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx()));
let disr = Disr::from(adt_def.variants[variant_index].disr_val);
bcx.with_block(|bcx| {
adt::trans_set_discr(bcx, &repr, dest.llval, Disr::from(disr));
adt::trans_set_discr(bcx,
dest.ty.to_ty(bcx.tcx()), dest.llval, Disr::from(disr));
});
for (i, operand) in operands.iter().enumerate() {
let op = self.trans_operand(&bcx, operand);
@ -122,8 +122,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
if !common::type_is_zero_size(bcx.ccx(), op.ty) {
let val = adt::MaybeSizedValue::sized(dest.llval);
let field_index = active_field_index.unwrap_or(i);
let lldest_i = adt::trans_field_ptr_builder(&bcx, &repr, val,
disr, field_index);
let lldest_i = adt::trans_field_ptr_builder(&bcx,
dest.ty.to_ty(bcx.tcx()),
val, disr, field_index);
self.store_operand(&bcx, lldest_i, op);
}
}
@ -270,17 +271,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let ll_t_in = type_of::immediate_type_of(bcx.ccx(), operand.ty);
let ll_t_out = type_of::immediate_type_of(bcx.ccx(), cast_ty);
let (llval, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in {
let repr = adt::represent_type(bcx.ccx(), operand.ty);
let l = bcx.ccx().layout_of(operand.ty);
let discr = match operand.val {
OperandValue::Immediate(llval) => llval,
OperandValue::Ref(llptr) => {
bcx.with_block(|bcx| {
adt::trans_get_discr(bcx, &repr, llptr, None, true)
adt::trans_get_discr(bcx, operand.ty, llptr, None, true)
})
}
OperandValue::Pair(..) => bug!("Unexpected Pair operand")
};
(discr, adt::is_discr_signed(&repr))
(discr, adt::is_discr_signed(&l))
} else {
(operand.immediate(), operand.ty.is_signed())
};

View File

@ -62,11 +62,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
mir::StatementKind::SetDiscriminant{ref lvalue, variant_index} => {
let ty = self.monomorphized_lvalue_ty(lvalue);
let repr = adt::represent_type(bcx.ccx(), ty);
let lvalue_transed = self.trans_lvalue(&bcx, lvalue);
bcx.with_block(|bcx|
adt::trans_set_discr(bcx,
&repr,
ty,
lvalue_transed.llval,
Disr::from(variant_index))
);

View File

@ -18,6 +18,7 @@ use context::CrateContext;
use util::nodemap::FnvHashMap;
use syntax::ast;
use rustc::ty::layout;
use std::ffi::CString;
use std::fmt;
@ -299,6 +300,26 @@ impl Type {
llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64
}
}
pub fn from_integer(cx: &CrateContext, i: layout::Integer) -> Type {
use rustc::ty::layout::Integer::*;
match i {
I1 => Type::i1(cx),
I8 => Type::i8(cx),
I16 => Type::i16(cx),
I32 => Type::i32(cx),
I64 => Type::i64(cx),
}
}
pub fn from_primitive(ccx: &CrateContext, p: layout::Primitive) -> Type {
match p {
layout::Int(i) => Type::from_integer(ccx, i),
layout::F32 => Type::f32(ccx),
layout::F64 => Type::f64(ccx),
layout::Pointer => bug!("It is not possible to convert Pointer directly to Type.")
}
}
}
/* Memory-managed object interface to type handles. */

View File

@ -22,17 +22,6 @@ use type_::Type;
use syntax::ast;
// LLVM doesn't like objects that are too big. Issue #17913
fn ensure_array_fits_in_address_space<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llet: Type,
size: machine::llsize,
scapegoat: Ty<'tcx>) {
let esz = machine::llsize_of_alloc(ccx, llet);
match esz.checked_mul(size) {
Some(n) if n < ccx.obj_size_bound() => {}
_ => { ccx.report_overbig_object(scapegoat) }
}
}
// A "sizing type" is an LLVM type, the size and alignment of which are
// guaranteed to be equivalent to what you would get out of `type_of()`. It's
@ -81,7 +70,6 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
ty::TyArray(ty, size) => {
let llty = sizing_type_of(cx, ty);
let size = size as u64;
ensure_array_fits_in_address_space(cx, llty, size, t);
Type::array(&llty, size)
}
@ -98,13 +86,11 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
}
let llet = type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
}
ty::TyTuple(..) | ty::TyAdt(..) | ty::TyClosure(..) => {
let repr = adt::represent_type(cx, t);
adt::sizing_type_of(cx, &repr, false)
adt::sizing_type_of(cx, t, false)
}
ty::TyProjection(..) | ty::TyInfer(..) | ty::TyParam(..) |
@ -242,8 +228,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
ty::TyClosure(..) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache.
let repr = adt::represent_type(cx, t);
adt::incomplete_type_of(cx, &repr, "closure")
adt::incomplete_type_of(cx, t, "closure")
}
ty::TyBox(ty) |
@ -266,11 +251,6 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
ty::TyArray(ty, size) => {
let size = size as u64;
// we must use `sizing_type_of` here as the type may
// not be fully initialized.
let szty = sizing_type_of(cx, ty);
ensure_array_fits_in_address_space(cx, szty, size, t);
let llty = in_memory_type_of(cx, ty);
Type::array(&llty, size)
}
@ -290,8 +270,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
}
ty::TyTuple(ref tys) if tys.is_empty() => Type::nil(cx),
ty::TyTuple(..) => {
let repr = adt::represent_type(cx, t);
adt::type_of(cx, &repr)
adt::type_of(cx, t)
}
ty::TyAdt(..) if t.is_simd() => {
let e = t.simd_type(cx.tcx());
@ -302,7 +281,6 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
}
let llet = in_memory_type_of(cx, e);
let n = t.simd_size(cx.tcx()) as u64;
ensure_array_fits_in_address_space(cx, llet, n, t);
Type::vector(&llet, n)
}
ty::TyAdt(def, substs) => {
@ -310,9 +288,8 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
// fill it in *after* placing it into the type cache. This
// avoids creating more than one copy of the enum when one
// of the enum's variants refers to the enum itself.
let repr = adt::represent_type(cx, t);
let name = llvm_type_name(cx, def.did, substs);
adt::incomplete_type_of(cx, &repr, &name[..])
adt::incomplete_type_of(cx, t, &name[..])
}
ty::TyInfer(..) |
@ -329,8 +306,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
// If this was an enum or struct, fill in the type now.
match t.sty {
ty::TyAdt(..) | ty::TyClosure(..) if !t.is_simd() => {
let repr = adt::represent_type(cx, t);
adt::finish_type_of(cx, &repr, &mut llty);
adt::finish_type_of(cx, t, &mut llty);
}
_ => ()
}
@ -340,8 +316,8 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
pub fn align_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>)
-> machine::llalign {
let llty = sizing_type_of(cx, t);
machine::llalign_of_min(cx, llty)
let layout = cx.layout_of(t);
layout.align(&cx.tcx().data_layout).abi() as machine::llalign
}
fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,

View File

@ -22,12 +22,12 @@ pub enum E {
B(f32)
}
// CHECK: @VAR2 = constant {{.*}} { i32 0, i32 666, {{.*}} }, section ".test_two"
// CHECK: @VAR2 = constant {{.*}} { i32 0, i32 666 }, section ".test_two"
#[no_mangle]
#[link_section = ".test_two"]
pub static VAR2: E = E::A(666);
// CHECK: @VAR3 = constant {{.*}} { i32 1, float 1.000000e+00, {{.*}} }, section ".test_three"
// CHECK: @VAR3 = constant {{.*}} { i32 1, float 1.000000e+00 }, section ".test_three"
#[no_mangle]
#[link_section = ".test_three"]
pub static VAR3: E = E::B(1.);