about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-09-19 12:38:20 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-11-19 02:14:31 +0200
commit18d54aa7d52c271c8a352fe6ccdefa80b100d0d3 (patch)
tree9d36302d8d9a42b05a49aca5fd7b1b0beba2fc04 /src
parent08f9f134fd9d790c99ffab590fee264bd062f599 (diff)
downloadrust-18d54aa7d52c271c8a352fe6ccdefa80b100d0d3.tar.gz
rust-18d54aa7d52c271c8a352fe6ccdefa80b100d0d3.zip
rustc: move layout::Struct into FieldPlacement/Abi.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ty/context.rs8
-rw-r--r--src/librustc/ty/layout.rs1038
-rw-r--r--src/librustc/ty/maps/mod.rs2
-rw-r--r--src/librustc_trans/abi.rs2
-rw-r--r--src/librustc_trans/adt.rs10
-rw-r--r--src/librustc_trans/common.rs4
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs5
-rw-r--r--src/librustc_trans/mir/constant.rs10
-rw-r--r--src/librustc_trans/type_of.rs17
9 files changed, 496 insertions, 600 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 9ad5e07d8fe..f69e714a997 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -41,7 +41,7 @@ use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predic
 use ty::RegionKind;
 use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
 use ty::TypeVariants::*;
-use ty::layout::{Layout, TargetDataLayout};
+use ty::layout::{CachedLayout, TargetDataLayout};
 use ty::maps;
 use ty::steal::Steal;
 use ty::BindingMode;
@@ -78,7 +78,7 @@ use hir;
 /// Internal storage
 pub struct GlobalArenas<'tcx> {
     // internings
-    layout: TypedArena<Layout<'tcx>>,
+    layout: TypedArena<CachedLayout>,
 
     // references
     generics: TypedArena<ty::Generics>,
@@ -918,7 +918,7 @@ pub struct GlobalCtxt<'tcx> {
 
     stability_interner: RefCell<FxHashSet<&'tcx attr::Stability>>,
 
-    layout_interner: RefCell<FxHashSet<&'tcx Layout<'tcx>>>,
+    layout_interner: RefCell<FxHashSet<&'tcx CachedLayout>>,
 
     /// A vector of every trait accessible in the whole crate
     /// (i.e. including those from subcrates). This is used only for
@@ -1016,7 +1016,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         interned
     }
 
-    pub fn intern_layout(self, layout: Layout<'gcx>) -> &'gcx Layout<'gcx> {
+    pub fn intern_layout(self, layout: CachedLayout) -> &'gcx CachedLayout {
         if let Some(layout) = self.layout_interner.borrow().get(&layout) {
             return layout;
         }
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 13d3ec68a31..1a8f46d8344 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -12,7 +12,6 @@ pub use self::Integer::*;
 pub use self::Layout::*;
 pub use self::Primitive::*;
 
-use rustc_back::slice::ref_slice;
 use session::{self, DataTypeKind, Session};
 use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
 
@@ -623,303 +622,6 @@ impl<'a, 'tcx> Primitive {
     }
 }
 
-/// A structure, a product type in ADT terms.
-#[derive(PartialEq, Eq, Hash, Debug)]
-pub struct Struct {
-    /// Maximum alignment of fields and repr alignment.
-    align: Align,
-
-    /// Primitive alignment of fields without repr alignment.
-    primitive_align: Align,
-
-    /// If true, no alignment padding is used.
-    packed: bool,
-
-    /// If true, the size is exact, otherwise it's only a lower bound.
-    sized: bool,
-
-    /// Offsets for the first byte of each field, ordered to match the source definition order.
-    /// This vector does not go in increasing order.
-    /// FIXME(eddyb) use small vector optimization for the common case.
-    offsets: Vec<Size>,
-
-    /// Maps source order field indices to memory order indices, depending how fields were permuted.
-    /// FIXME (camlorn) also consider small vector  optimization here.
-    pub memory_index: Vec<u32>,
-
-    min_size: Size,
-}
-
-/// Info required to optimize struct layout.
-#[derive(Copy, Clone, Debug)]
-enum StructKind {
-    /// A tuple, closure, or univariant which cannot be coerced to unsized.
-    AlwaysSizedUnivariant,
-    /// A univariant, the last field of which may be coerced to unsized.
-    MaybeUnsizedUnivariant,
-    /// A univariant, but part of an enum.
-    EnumVariant(Integer),
-}
-
-impl<'a, 'tcx> Struct {
-    fn new(dl: &TargetDataLayout,
-           fields: &[FullLayout],
-           repr: &ReprOptions,
-           kind: StructKind,
-           scapegoat: Ty<'tcx>)
-           -> Result<Struct, LayoutError<'tcx>> {
-        if repr.packed() && repr.align > 0 {
-            bug!("Struct cannot be packed and aligned");
-        }
-
-        let align = if repr.packed() {
-            dl.i8_align
-        } else {
-            dl.aggregate_align
-        };
-
-        let mut ret = Struct {
-            align,
-            primitive_align: align,
-            packed: repr.packed(),
-            sized: true,
-            offsets: vec![],
-            memory_index: vec![],
-            min_size: Size::from_bytes(0),
-        };
-
-        // Anything with repr(C) or repr(packed) doesn't optimize.
-        // Neither do  1-member and 2-member structs.
-        // In addition, code in trans assume that 2-element structs can become pairs.
-        // It's easier to just short-circuit here.
-        let (mut optimize, sort_ascending) = match kind {
-            StructKind::AlwaysSizedUnivariant |
-            StructKind::MaybeUnsizedUnivariant => (fields.len() > 2, false),
-            StructKind::EnumVariant(discr) => {
-                (discr.size().bytes() == 1, true)
-            }
-        };
-
-        optimize &= (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
-
-        ret.offsets = vec![Size::from_bytes(0); fields.len()];
-        let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
-
-        if optimize {
-            let end = if let StructKind::MaybeUnsizedUnivariant = kind {
-                fields.len() - 1
-            } else {
-                fields.len()
-            };
-            if end > 0 {
-                let optimizing  = &mut inverse_memory_index[..end];
-                if sort_ascending {
-                    optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi());
-                } else {
-                    optimizing.sort_by(| &a, &b | {
-                        let a = fields[a as usize].align(dl).abi();
-                        let b = fields[b as usize].align(dl).abi();
-                        b.cmp(&a)
-                    });
-                }
-            }
-        }
-
-        // inverse_memory_index holds field indices by increasing memory offset.
-        // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
-        // We now write field offsets to the corresponding offset slot;
-        // field 5 with offset 0 puts 0 in offsets[5].
-        // At the bottom of this function, we use inverse_memory_index to produce memory_index.
-
-        let mut offset = Size::from_bytes(0);
-
-        if let StructKind::EnumVariant(discr) = kind {
-            offset = discr.size();
-            if !ret.packed {
-                let align = discr.align(dl);
-                ret.align = ret.align.max(align);
-                ret.primitive_align = ret.primitive_align.max(align);
-            }
-        }
-
-        for i in inverse_memory_index.iter() {
-            let field = fields[*i as usize];
-            if !ret.sized {
-                bug!("Struct::new: field #{} of `{}` comes after unsized field",
-                     ret.offsets.len(), scapegoat);
-            }
-
-            if field.is_unsized() {
-                ret.sized = false;
-            }
-
-            // Invariant: offset < dl.obj_size_bound() <= 1<<61
-            if !ret.packed {
-                let align = field.align(dl);
-                let primitive_align = field.primitive_align(dl);
-                ret.align = ret.align.max(align);
-                ret.primitive_align = ret.primitive_align.max(primitive_align);
-                offset = offset.abi_align(align);
-            }
-
-            debug!("Struct::new offset: {:?} field: {:?} {:?}", offset, field, field.size(dl));
-            ret.offsets[*i as usize] = offset;
-
-            offset = offset.checked_add(field.size(dl), dl)
-                           .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?;
-        }
-
-        if repr.align > 0 {
-            let repr_align = repr.align as u64;
-            ret.align = ret.align.max(Align::from_bytes(repr_align, repr_align).unwrap());
-            debug!("Struct::new repr_align: {:?}", repr_align);
-        }
-
-        debug!("Struct::new min_size: {:?}", offset);
-        ret.min_size = offset;
-
-        // As stated above, inverse_memory_index holds field indices by increasing offset.
-        // This makes it an already-sorted view of the offsets vec.
-        // To invert it, consider:
-        // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0.
-        // Field 5 would be the first element, so memory_index is i:
-        // Note: if we didn't optimize, it's already right.
-
-        if optimize {
-            ret.memory_index = vec![0; inverse_memory_index.len()];
-
-            for i in 0..inverse_memory_index.len() {
-                ret.memory_index[inverse_memory_index[i] as usize]  = i as u32;
-            }
-        } else {
-            ret.memory_index = inverse_memory_index;
-        }
-
-        Ok(ret)
-    }
-
-    /// Get the size with trailing alignment padding.
-    fn stride(&self) -> Size {
-        self.min_size.abi_align(self.align)
-    }
-
-    /// Get indices of the tys that made this struct by increasing offset.
-    #[inline]
-    pub fn field_index_by_increasing_offset<'b>(&'b self) -> impl iter::Iterator<Item=usize>+'b {
-        let mut inverse_small = [0u8; 64];
-        let mut inverse_big = vec![];
-        let use_small = self.memory_index.len() <= inverse_small.len();
-
-        // We have to write this logic twice in order to keep the array small.
-        if use_small {
-            for i in 0..self.memory_index.len() {
-                inverse_small[self.memory_index[i] as usize] = i as u8;
-            }
-        } else {
-            inverse_big = vec![0; self.memory_index.len()];
-            for i in 0..self.memory_index.len() {
-                inverse_big[self.memory_index[i] as usize] = i as u32;
-            }
-        }
-
-        (0..self.memory_index.len()).map(move |i| {
-            if use_small { inverse_small[i] as usize }
-            else { inverse_big[i] as usize }
-        })
-    }
-
-    /// Find the offset of a non-zero leaf field, starting from
-    /// the given type and recursing through aggregates.
-    /// The tuple is `(offset, primitive, source_path)`.
-    // FIXME(eddyb) track value ranges and traverse already optimized enums.
-    fn non_zero_field_in_type(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              param_env: ty::ParamEnv<'tcx>,
-                              layout: FullLayout<'tcx>)
-                              -> Result<Option<(Size, Primitive)>, LayoutError<'tcx>> {
-        let cx = (tcx, param_env);
-        match (layout.layout, layout.abi, &layout.ty.sty) {
-            (&Scalar, Abi::Scalar(Pointer), _) if !layout.ty.is_unsafe_ptr() => {
-                Ok(Some((Size::from_bytes(0), Pointer)))
-            }
-            (&General { discr, .. }, _, &ty::TyAdt(def, _)) => {
-                if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) {
-                    Ok(Some((layout.fields.offset(0), discr)))
-                } else {
-                    Ok(None)
-                }
-            }
-
-            (&FatPointer, _, _) if !layout.ty.is_unsafe_ptr() => {
-                Ok(Some((layout.fields.offset(FAT_PTR_ADDR), Pointer)))
-            }
-
-            // Is this the NonZero lang item wrapping a pointer or integer type?
-            (_, _, &ty::TyAdt(def, _)) if Some(def.did) == tcx.lang_items().non_zero() => {
-                let field = layout.field(cx, 0)?;
-                match (field.layout, field.abi) {
-                    (&Scalar, Abi::Scalar(value)) => {
-                        Ok(Some((layout.fields.offset(0), value)))
-                    }
-                    (&FatPointer, _) => {
-                        Ok(Some((layout.fields.offset(0) +
-                                 field.fields.offset(FAT_PTR_ADDR),
-                                 Pointer)))
-                    }
-                    _ => Ok(None)
-                }
-            }
-
-            // Perhaps one of the fields is non-zero, let's recurse and find out.
-            (&Univariant(ref variant), _, _) => {
-                variant.non_zero_field(
-                    tcx,
-                    param_env,
-                    (0..layout.fields.count()).map(|i| layout.field(cx, i)))
-            }
-
-            // Is this a fixed-size array of something non-zero
-            // with at least one element?
-            (_, _, &ty::TyArray(ety, mut count)) => {
-                if count.has_projections() {
-                    count = tcx.normalize_associated_type_in_env(&count, param_env);
-                    if count.has_projections() {
-                        return Err(LayoutError::Unknown(layout.ty));
-                    }
-                }
-                if count.val.to_const_int().unwrap().to_u64().unwrap() != 0 {
-                    Struct::non_zero_field_in_type(tcx, param_env, cx.layout_of(ety)?)
-                } else {
-                    Ok(None)
-                }
-            }
-
-            (_, _, &ty::TyProjection(_)) | (_, _, &ty::TyAnon(..)) => {
-                bug!("Struct::non_zero_field_in_type: {:?} not normalized", layout);
-            }
-
-            // Anything else is not a non-zero type.
-            _ => Ok(None)
-        }
-    }
-
-    /// Find the offset of a non-zero leaf field, starting from
-    /// the given set of fields and recursing through aggregates.
-    /// Returns Some((offset, primitive, source_path)) on success.
-    fn non_zero_field<I>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                         param_env: ty::ParamEnv<'tcx>,
-                         fields: I)
-                         -> Result<Option<(Size, Primitive)>, LayoutError<'tcx>>
-    where I: Iterator<Item = Result<FullLayout<'tcx>, LayoutError<'tcx>>> {
-        for (field, &field_offset) in fields.zip(&self.offsets) {
-            let r = Struct::non_zero_field_in_type(tcx, param_env, field?)?;
-            if let Some((offset, primitive)) = r {
-                return Ok(Some((field_offset + offset, primitive)));
-            }
-        }
-        Ok(None)
-    }
-}
-
 /// The first half of a fat pointer.
 /// - For a trait object, this is the address of the box.
 /// - For a slice, this is the base address.
@@ -931,8 +633,8 @@ pub const FAT_PTR_ADDR: usize = 0;
 pub const FAT_PTR_EXTRA: usize = 1;
 
 /// Describes how the fields of a type are located in memory.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum FieldPlacement<'a> {
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub enum FieldPlacement {
     /// Array-like placement. Can also express
     /// unions, by using a stride of zero bytes.
     Linear {
@@ -948,11 +650,20 @@ pub enum FieldPlacement<'a> {
     /// For example, enum variants leave a gap at the start,
     /// where the discriminant field in the enum layout goes.
     Arbitrary {
-        offsets: &'a [Size]
+        /// Offsets for the first byte of each field,
+        /// ordered to match the source definition order.
+        /// This vector does not go in increasing order.
+        // FIXME(eddyb) use small vector optimization for the common case.
+        offsets: Vec<Size>,
+
+        /// Maps source order field indices to memory order indices,
+        /// depending how fields were permuted.
+        // FIXME(camlorn) also consider small vector  optimization here.
+        memory_index: Vec<u32>
     }
 }
 
-impl<'a> FieldPlacement<'a> {
+impl FieldPlacement {
     pub fn union(count: usize) -> Self {
         FieldPlacement::Linear {
             stride: Size::from_bytes(0),
@@ -967,19 +678,62 @@ impl<'a> FieldPlacement<'a> {
                 assert_eq!(usize_count as u64, count);
                 usize_count
             }
-            FieldPlacement::Arbitrary { offsets } => offsets.len()
+            FieldPlacement::Arbitrary { ref offsets, .. } => offsets.len()
         }
     }
 
     pub fn offset(&self, i: usize) -> Size {
         match *self {
-            FieldPlacement::Linear { stride, count, .. } => {
+            FieldPlacement::Linear { stride, count } => {
                 let i = i as u64;
                 assert!(i < count);
                 stride * i
             }
-            FieldPlacement::Arbitrary { offsets } => offsets[i]
+            FieldPlacement::Arbitrary { ref offsets, .. } => offsets[i]
+        }
+    }
+
+    pub fn memory_index(&self, i: usize) -> usize {
+        match *self {
+            FieldPlacement::Linear { .. } => i,
+            FieldPlacement::Arbitrary { ref memory_index, .. } => {
+                let r = memory_index[i];
+                assert_eq!(r as usize as u32, r);
+                r as usize
+            }
+        }
+    }
+
+    /// Get source indices of the fields by increasing offsets.
+    #[inline]
+    pub fn index_by_increasing_offset<'a>(&'a self) -> impl iter::Iterator<Item=usize>+'a {
+        let mut inverse_small = [0u8; 64];
+        let mut inverse_big = vec![];
+        let use_small = self.count() <= inverse_small.len();
+
+        // We have to write this logic twice in order to keep the array small.
+        if let FieldPlacement::Arbitrary { ref memory_index, .. } = *self {
+            if use_small {
+                for i in 0..self.count() {
+                    inverse_small[memory_index[i] as usize] = i as u8;
+                }
+            } else {
+                inverse_big = vec![0; self.count()];
+                for i in 0..self.count() {
+                    inverse_big[memory_index[i] as usize] = i as u32;
+                }
+            }
         }
+
+        (0..self.count()).map(move |i| {
+            match *self {
+                FieldPlacement::Linear { .. } => i,
+                FieldPlacement::Arbitrary { .. } => {
+                    if use_small { inverse_small[i] as usize }
+                    else { inverse_big[i] as usize }
+                }
+            }
+        })
     }
 }
 
@@ -1078,7 +832,7 @@ impl Abi {
 /// NOTE: Because Layout is interned, redundant information should be
 /// kept to a minimum, e.g. it includes no sub-component Ty or Layout.
 #[derive(PartialEq, Eq, Hash, Debug)]
-pub enum Layout<'a> {
+pub enum Layout {
     /// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr.
     Scalar,
 
@@ -1094,7 +848,7 @@ pub enum Layout<'a> {
     // Remaining variants are all ADTs such as structs, enums or tuples.
 
     /// Single-case enums, and structs/tuples.
-    Univariant(Struct),
+    Univariant,
 
     /// Untagged unions.
     UntaggedUnion,
@@ -1110,12 +864,12 @@ pub enum Layout<'a> {
         // the largest space between two consecutive discriminants and
         // taking everything else as the (shortest) discriminant range.
         discr_range: RangeInclusive<u64>,
-        variants: Vec<CachedLayout<'a>>,
+        variants: Vec<CachedLayout>,
     },
 
     /// Two cases distinguished by a nullable pointer: the case with discriminant
-    /// `nndiscr` is represented by the struct `nonnull`, where the field at the
-    /// `discr_offset` offset is known to be nonnull due to its type; if that field is null, then
+    /// `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.
     ///
     /// For example, `std::option::Option` instantiated at a safe pointer type
@@ -1124,8 +878,7 @@ pub enum Layout<'a> {
     NullablePointer {
         nndiscr: u64,
         discr: Primitive,
-        discr_offset: Size,
-        variants: Vec<CachedLayout<'a>>,
+        variants: Vec<CachedLayout>,
     }
 }
 
@@ -1148,16 +901,16 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub struct CachedLayout<'tcx> {
-    pub layout: &'tcx Layout<'tcx>,
-    pub fields: FieldPlacement<'tcx>,
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct CachedLayout {
+    pub layout: Layout,
+    pub fields: FieldPlacement,
     pub abi: Abi,
 }
 
 fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
-                        -> Result<CachedLayout<'tcx>, LayoutError<'tcx>>
+                        -> Result<&'tcx CachedLayout, LayoutError<'tcx>>
 {
     let (param_env, ty) = query.into_parts();
 
@@ -1182,47 +935,168 @@ pub fn provide(providers: &mut ty::maps::Providers) {
     };
 }
 
-impl<'a, 'tcx> Layout<'tcx> {
+impl<'a, 'tcx> Layout {
     fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         param_env: ty::ParamEnv<'tcx>,
                         ty: Ty<'tcx>)
-                        -> Result<CachedLayout<'tcx>, LayoutError<'tcx>> {
+                        -> Result<&'tcx CachedLayout, LayoutError<'tcx>> {
         let cx = (tcx, param_env);
         let dl = cx.data_layout();
         let scalar = |value| {
-            CachedLayout {
-                layout: &Layout::Scalar,
+            tcx.intern_layout(CachedLayout {
+                layout: Layout::Scalar,
                 fields: FieldPlacement::union(0),
                 abi: Abi::Scalar(value)
-            }
+            })
         };
-        let univariant = |st| {
-            let layout = tcx.intern_layout(Layout::Univariant(st));
-            let fields = match *layout {
-                Univariant(ref variant) => {
-                    FieldPlacement::Arbitrary {
-                        offsets: &variant.offsets
-                    }
+        #[derive(Copy, Clone, Debug)]
+        enum StructKind {
+            /// A tuple, closure, or univariant which cannot be coerced to unsized.
+            AlwaysSized,
+            /// A univariant, the last field of which may be coerced to unsized.
+            MaybeUnsized,
+            /// A univariant, but part of an enum.
+            EnumVariant(Integer),
+        }
+        let univariant_uninterned = |fields: &[FullLayout], repr: &ReprOptions, kind| {
+            let packed = repr.packed();
+            if packed && repr.align > 0 {
+                bug!("struct cannot be packed and aligned");
+            }
+
+            let mut align = if packed {
+                dl.i8_align
+            } else {
+                dl.aggregate_align
+            };
+
+            let mut primitive_align = align;
+            let mut sized = true;
+
+            // Anything with repr(C) or repr(packed) doesn't optimize.
+            // Neither do  1-member and 2-member structs.
+            // In addition, code in trans assume that 2-element structs can become pairs.
+            // It's easier to just short-circuit here.
+            let (mut optimize, sort_ascending) = match kind {
+                StructKind::AlwaysSized |
+                StructKind::MaybeUnsized => (fields.len() > 2, false),
+                StructKind::EnumVariant(discr) => {
+                    (discr.size().bytes() == 1, true)
                 }
-                _ => bug!()
             };
-            let abi = match *layout {
-                Univariant(ref st) => {
-                    Abi::Aggregate {
-                        sized: st.sized,
-                        packed: st.packed,
-                        align: st.align,
-                        primitive_align: st.primitive_align,
-                        size: st.stride()
+
+            optimize &= (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
+
+            let mut offsets = vec![Size::from_bytes(0); fields.len()];
+            let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
+
+            if optimize {
+                let end = if let StructKind::MaybeUnsized = kind {
+                    fields.len() - 1
+                } else {
+                    fields.len()
+                };
+                if end > 0 {
+                    let optimizing  = &mut inverse_memory_index[..end];
+                    if sort_ascending {
+                        optimizing.sort_by_key(|&x| fields[x as usize].align(dl).abi());
+                    } else {
+                        optimizing.sort_by(| &a, &b | {
+                            let a = fields[a as usize].align(dl).abi();
+                            let b = fields[b as usize].align(dl).abi();
+                            b.cmp(&a)
+                        });
                     }
                 }
-                _ => bug!()
-            };
-            CachedLayout {
-                layout,
-                fields,
-                abi
             }
+
+            // inverse_memory_index holds field indices by increasing memory offset.
+            // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
+            // We now write field offsets to the corresponding offset slot;
+            // field 5 with offset 0 puts 0 in offsets[5].
+            // At the bottom of this function, we use inverse_memory_index to produce memory_index.
+
+            let mut offset = Size::from_bytes(0);
+
+            if let StructKind::EnumVariant(discr) = kind {
+                offset = discr.size();
+                if !packed {
+                    let discr_align = discr.align(dl);
+                    align = align.max(discr_align);
+                    primitive_align = primitive_align.max(discr_align);
+                }
+            }
+
+            for i in inverse_memory_index.iter() {
+                let field = fields[*i as usize];
+                if !sized {
+                    bug!("univariant: field #{} of `{}` comes after unsized field",
+                        offsets.len(), ty);
+                }
+
+                if field.is_unsized() {
+                    sized = false;
+                }
+
+                // Invariant: offset < dl.obj_size_bound() <= 1<<61
+                if !packed {
+                    let field_align = field.align(dl);
+                    align = align.max(field_align);
+                    primitive_align = primitive_align.max(field.primitive_align(dl));
+                    offset = offset.abi_align(field_align);
+                }
+
+                debug!("univariant offset: {:?} field: {:?} {:?}", offset, field, field.size(dl));
+                offsets[*i as usize] = offset;
+
+                offset = offset.checked_add(field.size(dl), dl)
+                    .ok_or(LayoutError::SizeOverflow(ty))?;
+            }
+
+            if repr.align > 0 {
+                let repr_align = repr.align as u64;
+                align = align.max(Align::from_bytes(repr_align, repr_align).unwrap());
+                debug!("univariant repr_align: {:?}", repr_align);
+            }
+
+            debug!("univariant min_size: {:?}", offset);
+            let min_size = offset;
+
+            // As stated above, inverse_memory_index holds field indices by increasing offset.
+            // This makes it an already-sorted view of the offsets vec.
+            // To invert it, consider:
+            // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0.
+            // Field 5 would be the first element, so memory_index is i:
+            // Note: if we didn't optimize, it's already right.
+
+            let mut memory_index;
+            if optimize {
+                memory_index = vec![0; inverse_memory_index.len()];
+
+                for i in 0..inverse_memory_index.len() {
+                    memory_index[inverse_memory_index[i] as usize]  = i as u32;
+                }
+            } else {
+                memory_index = inverse_memory_index;
+            }
+
+            Ok(CachedLayout {
+                layout: Layout::Univariant,
+                fields: FieldPlacement::Arbitrary {
+                    offsets,
+                    memory_index
+                },
+                abi: Abi::Aggregate {
+                    sized,
+                    packed,
+                    align,
+                    primitive_align,
+                    size: min_size.abi_align(align)
+                }
+            })
+        };
+        let univariant = |fields: &[FullLayout], repr: &ReprOptions, kind| {
+            Ok(tcx.intern_layout(univariant_uninterned(fields, repr, kind)?))
         };
         assert!(!ty.has_infer_types());
 
@@ -1250,18 +1124,17 @@ impl<'a, 'tcx> Layout<'tcx> {
             };
             let meta_offset = fields.offset(1);
             assert_eq!(meta_offset, meta_offset.abi_align(metadata.align(dl)));
-            Ok(CachedLayout {
-                layout: tcx.intern_layout(Layout::FatPointer),
+            Ok(tcx.intern_layout(CachedLayout {
+                layout: Layout::FatPointer,
                 fields,
-                abi:
-                Abi::Aggregate {
+                abi: Abi::Aggregate {
                     sized: true,
                     packed: false,
                     align,
                     primitive_align: align,
                     size: (meta_offset + metadata.size(dl)).abi_align(align)
                 }
-            })
+            }))
         };
 
         Ok(match ty.sty {
@@ -1280,8 +1153,7 @@ impl<'a, 'tcx> Layout<'tcx> {
 
             // The never type.
             ty::TyNever => {
-                univariant(Struct::new(dl, &[], &ReprOptions::default(),
-                                       StructKind::AlwaysSizedUnivariant, ty)?)
+                univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?
             }
 
             // Potentially-fat pointers.
@@ -1308,8 +1180,8 @@ impl<'a, 'tcx> Layout<'tcx> {
                 let size = element_size.checked_mul(count, dl)
                     .ok_or(LayoutError::SizeOverflow(ty))?;
 
-                CachedLayout {
-                    layout: &Layout::Array,
+                tcx.intern_layout(CachedLayout {
+                    layout: Layout::Array,
                     fields: FieldPlacement::Linear {
                         stride: element_size,
                         count
@@ -1321,12 +1193,12 @@ impl<'a, 'tcx> Layout<'tcx> {
                         primitive_align: element.primitive_align(dl),
                         size
                     }
-                }
+                })
             }
             ty::TySlice(element) => {
                 let element = cx.layout_of(element)?;
-                CachedLayout {
-                    layout: &Layout::Array,
+                tcx.intern_layout(CachedLayout {
+                    layout: Layout::Array,
                     fields: FieldPlacement::Linear {
                         stride: element.size(dl),
                         count: 0
@@ -1338,11 +1210,11 @@ impl<'a, 'tcx> Layout<'tcx> {
                         primitive_align: element.primitive_align(dl),
                         size: Size::from_bytes(0)
                     }
-                }
+                })
             }
             ty::TyStr => {
-                CachedLayout {
-                    layout: &Layout::Array,
+                tcx.intern_layout(CachedLayout {
+                    layout: Layout::Array,
                     fields: FieldPlacement::Linear {
                         stride: Size::from_bytes(1),
                         count: 0
@@ -1354,51 +1226,47 @@ impl<'a, 'tcx> Layout<'tcx> {
                         primitive_align: dl.i8_align,
                         size: Size::from_bytes(0)
                     }
-                }
+                })
             }
 
             // Odd unit types.
             ty::TyFnDef(..) => {
-                univariant(Struct::new(dl, &[], &ReprOptions::default(),
-                                       StructKind::AlwaysSizedUnivariant, ty)?)
+                univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?
             }
             ty::TyDynamic(..) | ty::TyForeign(..) => {
-                let mut unit = Struct::new(dl, &[], &ReprOptions::default(),
-                  StructKind::AlwaysSizedUnivariant, ty)?;
-                unit.sized = false;
-                univariant(unit)
+                let mut unit = univariant_uninterned(&[], &ReprOptions::default(),
+                  StructKind::AlwaysSized)?;
+                match unit.abi {
+                    Abi::Aggregate { ref mut sized, .. } => *sized = false,
+                    _ => bug!()
+                }
+                tcx.intern_layout(unit)
             }
 
             // Tuples, generators and closures.
             ty::TyGenerator(def_id, ref substs, _) => {
                 let tys = substs.field_tys(def_id, tcx);
-                univariant(Struct::new(dl,
-                    &tys.map(|ty| cx.layout_of(ty))
-                      .collect::<Result<Vec<_>, _>>()?,
+                univariant(&tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
                     &ReprOptions::default(),
-                    StructKind::AlwaysSizedUnivariant, ty)?)
+                    StructKind::AlwaysSized)?
             }
 
             ty::TyClosure(def_id, ref substs) => {
                 let tys = substs.upvar_tys(def_id, tcx);
-                univariant(Struct::new(dl,
-                    &tys.map(|ty| cx.layout_of(ty))
-                      .collect::<Result<Vec<_>, _>>()?,
+                univariant(&tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
                     &ReprOptions::default(),
-                    StructKind::AlwaysSizedUnivariant, ty)?)
+                    StructKind::AlwaysSized)?
             }
 
             ty::TyTuple(tys, _) => {
                 let kind = if tys.len() == 0 {
-                    StructKind::AlwaysSizedUnivariant
+                    StructKind::AlwaysSized
                 } else {
-                    StructKind::MaybeUnsizedUnivariant
+                    StructKind::MaybeUnsized
                 };
 
-                univariant(Struct::new(dl,
-                    &tys.iter().map(|ty| cx.layout_of(ty))
-                      .collect::<Result<Vec<_>, _>>()?,
-                    &ReprOptions::default(), kind, ty)?)
+                univariant(&tys.iter().map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
+                    &ReprOptions::default(), kind)?
             }
 
             // SIMD vector types.
@@ -1413,14 +1281,14 @@ impl<'a, 'tcx> Layout<'tcx> {
                                                 ty, element));
                     }
                 };
-                CachedLayout {
-                    layout: &Layout::Vector,
+                tcx.intern_layout(CachedLayout {
+                    layout: Layout::Vector,
                     fields: FieldPlacement::Linear {
                         stride: element.size(tcx),
                         count
                     },
                     abi: Abi::Vector { element, count }
-                }
+                })
             }
 
             // ADTs.
@@ -1436,8 +1304,7 @@ impl<'a, 'tcx> Layout<'tcx> {
                     // Uninhabitable; represent as unit
                     // (Typechecking will reject discriminant-sizing attrs.)
 
-                    return Ok(univariant(Struct::new(dl, &[],
-                          &def.repr, StructKind::AlwaysSizedUnivariant, ty)?));
+                    return univariant(&[], &def.repr, StructKind::AlwaysSized);
                 }
 
                 if def.is_union() {
@@ -1471,8 +1338,8 @@ impl<'a, 'tcx> Layout<'tcx> {
                         size = cmp::max(size, field.size(dl));
                     }
 
-                    return Ok(CachedLayout {
-                        layout: &Layout::UntaggedUnion,
+                    return Ok(tcx.intern_layout(CachedLayout {
+                        layout: Layout::UntaggedUnion,
                         fields: FieldPlacement::union(variants[0].len()),
                         abi: Abi::Aggregate {
                             sized: true,
@@ -1481,7 +1348,7 @@ impl<'a, 'tcx> Layout<'tcx> {
                             primitive_align,
                             size: size.abi_align(align)
                         }
-                    });
+                    }));
                 }
 
                 if !def.is_enum() || (variants.len() == 1 &&
@@ -1491,17 +1358,17 @@ impl<'a, 'tcx> Layout<'tcx> {
                     // (Typechecking will reject discriminant-sizing attrs.)
 
                     let kind = if def.is_enum() || variants[0].len() == 0 {
-                        StructKind::AlwaysSizedUnivariant
+                        StructKind::AlwaysSized
                     } else {
                         let param_env = tcx.param_env(def.did);
                         let last_field = def.variants[0].fields.last().unwrap();
                         let always_sized = tcx.type_of(last_field.did)
                           .is_sized(tcx, param_env, DUMMY_SP);
-                        if !always_sized { StructKind::MaybeUnsizedUnivariant }
-                        else { StructKind::AlwaysSizedUnivariant }
+                        if !always_sized { StructKind::MaybeUnsized }
+                        else { StructKind::AlwaysSized }
                     };
 
-                    return Ok(univariant(Struct::new(dl, &variants[0], &def.repr, kind, ty)?));
+                    return univariant(&variants[0], &def.repr, kind);
                 }
 
                 let no_explicit_discriminants = def.variants.iter().enumerate()
@@ -1511,71 +1378,56 @@ impl<'a, 'tcx> Layout<'tcx> {
                    !def.repr.inhibit_enum_layout_opt() &&
                    no_explicit_discriminants {
                     // Nullable pointer optimization
-                    let mut st = vec![
-                        Struct::new(dl, &variants[0],
-                            &def.repr, StructKind::AlwaysSizedUnivariant, ty)?,
-                        Struct::new(dl, &variants[1],
-                            &def.repr, StructKind::AlwaysSizedUnivariant, ty)?
-                    ];
-
-                    let mut choice = None;
-                    for discr in 0..2 {
-                        if st[1 - discr].stride().bytes() > 0 {
+                    for i in 0..2 {
+                        if !variants[1 - i].iter().all(|f| f.size(dl).bytes() == 0) {
                             continue;
                         }
 
-                        let field = st[discr].non_zero_field(tcx, param_env,
-                            variants[discr].iter().map(|&f| Ok(f)))?;
-                        if let Some((offset, primitive)) = field {
-                            choice = Some((discr, offset, primitive));
-                            break;
-                        }
-                    }
-
-                    if let Some((nndiscr, offset, discr)) = choice {
-                        let variants: Vec<_> = st.into_iter().map(&univariant).collect();
-                        let mut abi = variants[nndiscr].abi;
-
-                        let mut discr_align = discr.align(dl);
-                        match abi {
-                            Abi::Aggregate {
-                                ref mut align,
-                                ref mut primitive_align,
-                                ref mut packed,
-                                ..
-                            } => {
-                                if offset.abi_align(discr_align) != offset {
-                                    *packed = true;
-                                    discr_align = dl.i8_align;
+                        for (field_index, field) in variants[i].iter().enumerate() {
+                            if let Some((offset, discr)) = field.non_zero_field(cx)? {
+                                let st = vec![
+                                    univariant_uninterned(&variants[0],
+                                        &def.repr, StructKind::AlwaysSized)?,
+                                    univariant_uninterned(&variants[1],
+                                        &def.repr, StructKind::AlwaysSized)?
+                                ];
+                                let offset = st[i].fields.offset(field_index) + offset;
+                                let mut abi = st[i].abi;
+                                if offset.bytes() == 0 && discr.size(dl) == abi.size(dl) {
+                                    abi = Abi::Scalar(discr);
                                 }
-                                *align = align.max(discr_align);
-                                *primitive_align = primitive_align.max(discr_align);
-                            }
-                            _ => {}
-                        }
-
-                        let layout = tcx.intern_layout(Layout::NullablePointer {
-                            nndiscr: nndiscr as u64,
-                            discr,
-                            discr_offset: offset,
-                            variants,
-                        });
-                        return Ok(CachedLayout {
-                            layout,
-                            fields: match *layout {
-                                Layout::NullablePointer { ref discr_offset, .. } => {
-                                    FieldPlacement::Arbitrary {
-                                        offsets: ref_slice(discr_offset)
+                                let mut discr_align = discr.align(dl);
+                                match abi {
+                                    Abi::Aggregate {
+                                        ref mut align,
+                                        ref mut primitive_align,
+                                        ref mut packed,
+                                        ..
+                                    } => {
+                                        if offset.abi_align(discr_align) != offset {
+                                            *packed = true;
+                                            discr_align = dl.i8_align;
+                                        }
+                                        *align = align.max(discr_align);
+                                        *primitive_align = primitive_align.max(discr_align);
                                     }
+                                    _ => {}
                                 }
-                                _ => bug!()
-                            },
-                            abi: if offset.bytes() == 0 && discr.size(dl) == abi.size(dl) {
-                                Abi::Scalar(discr)
-                            } else {
-                                abi
+                                return Ok(tcx.intern_layout(CachedLayout {
+                                    layout: Layout::NullablePointer {
+                                        nndiscr: i as u64,
+
+                                        discr,
+                                        variants: st,
+                                    },
+                                    fields: FieldPlacement::Arbitrary {
+                                        offsets: vec![offset],
+                                        memory_index: vec![0]
+                                    },
+                                    abi
+                                }));
                             }
-                        });
+                        }
                     }
                 }
 
@@ -1598,22 +1450,22 @@ impl<'a, 'tcx> Layout<'tcx> {
                 assert_eq!(Integer::for_abi_align(dl, start_align), None);
 
                 // Create the set of structs that represent each variant.
-                let mut variants = variants.into_iter().map(|fields| {
-                    let st = Struct::new(dl, &fields,
-                        &def.repr, StructKind::EnumVariant(min_ity), ty)?;
+                let mut variants = variants.into_iter().map(|field_layouts| {
+                    let st = univariant_uninterned(&field_layouts,
+                        &def.repr, StructKind::EnumVariant(min_ity))?;
                     // Find the first field we can't move later
                     // to make room for a larger discriminant.
-                    for i in st.field_index_by_increasing_offset() {
-                        let field = fields[i];
+                    for i in st.fields.index_by_increasing_offset() {
+                        let field = field_layouts[i];
                         let field_align = field.align(dl);
                         if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
                             start_align = start_align.min(field_align);
                             break;
                         }
                     }
-                    size = cmp::max(size, st.min_size);
-                    align = align.max(st.align);
-                    primitive_align = primitive_align.max(st.primitive_align);
+                    size = cmp::max(size, st.abi.size(dl));
+                    align = align.max(st.abi.align(dl));
+                    primitive_align = primitive_align.max(st.abi.primitive_align(dl));
                     Ok(st)
                 }).collect::<Result<Vec<_>, _>>()?;
 
@@ -1662,29 +1514,38 @@ impl<'a, 'tcx> Layout<'tcx> {
                     let old_ity_size = min_ity.size();
                     let new_ity_size = ity.size();
                     for variant in &mut variants {
-                        for i in variant.offsets.iter_mut() {
-                            if *i <= old_ity_size {
-                                assert_eq!(*i, old_ity_size);
-                                *i = new_ity_size;
+                        match (&mut variant.fields, &mut variant.abi) {
+                            (&mut FieldPlacement::Arbitrary { ref mut offsets, .. },
+                             &mut Abi::Aggregate { ref mut size, .. }) => {
+                                for i in offsets {
+                                    if *i <= old_ity_size {
+                                        assert_eq!(*i, old_ity_size);
+                                        *i = new_ity_size;
+                                    }
+                                }
+                                // We might be making the struct larger.
+                                if *size <= old_ity_size {
+                                    *size = new_ity_size;
+                                }
                             }
-                        }
-                        // We might be making the struct larger.
-                        if variant.min_size <= old_ity_size {
-                            variant.min_size = new_ity_size;
+                            _ => bug!()
                         }
                     }
                 }
 
                 let discr = Int(ity, signed);
-                CachedLayout {
-                    layout: tcx.intern_layout(Layout::General {
+                tcx.intern_layout(CachedLayout {
+                    layout: Layout::General {
                         discr,
 
                         // FIXME: should be u128?
                         discr_range: (min as u64)..=(max as u64),
-                        variants: variants.into_iter().map(&univariant).collect(),
-                    }),
-                    fields: FieldPlacement::union(1),
+                        variants
+                    },
+                    fields: FieldPlacement::Arbitrary {
+                        offsets: vec![Size::from_bytes(0)],
+                        memory_index: vec![0]
+                    },
                     abi: if discr.size(dl) == size {
                         Abi::Scalar(discr)
                     } else {
@@ -1696,7 +1557,7 @@ impl<'a, 'tcx> Layout<'tcx> {
                             size
                         }
                     }
-                }
+                })
             }
 
             // Types with no meaningful known layout.
@@ -1705,12 +1566,7 @@ impl<'a, 'tcx> Layout<'tcx> {
                 if ty == normalized {
                     return Err(LayoutError::Unknown(ty));
                 }
-                let layout = cx.layout_of(normalized)?;
-                CachedLayout {
-                    layout: layout.layout,
-                    fields: layout.fields,
-                    abi: layout.abi
-                }
+                tcx.layout_raw(param_env.and(normalized))?
             }
             ty::TyParam(_) => {
                 return Err(LayoutError::Unknown(ty));
@@ -1727,7 +1583,7 @@ impl<'a, 'tcx> Layout<'tcx> {
     fn record_layout_for_printing(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   ty: Ty<'tcx>,
                                   param_env: ty::ParamEnv<'tcx>,
-                                  layout: FullLayout) {
+                                  layout: FullLayout<'tcx>) {
         // If we are running with `-Zprint-type-sizes`, record layouts for
         // dumping later. Ignore layouts that are done with non-empty
         // environments or non-monomorphic layouts, as the user only wants
@@ -1747,7 +1603,8 @@ impl<'a, 'tcx> Layout<'tcx> {
     fn record_layout_for_printing_outlined(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                            ty: Ty<'tcx>,
                                            param_env: ty::ParamEnv<'tcx>,
-                                           layout: FullLayout) {
+                                           layout: FullLayout<'tcx>) {
+        let cx = (tcx, param_env);
         // (delay format until we actually need it)
         let record = |kind, opt_discr_size, variants| {
             let type_desc = format!("{:?}", ty);
@@ -1761,10 +1618,10 @@ impl<'a, 'tcx> Layout<'tcx> {
                                                               variants);
         };
 
-        let (adt_def, substs) = match ty.sty {
-            ty::TyAdt(ref adt_def, substs) => {
+        let adt_def = match ty.sty {
+            ty::TyAdt(ref adt_def, _) => {
                 debug!("print-type-size t: `{:?}` process adt", ty);
-                (adt_def, substs)
+                adt_def
             }
 
             ty::TyClosure(..) => {
@@ -1781,62 +1638,67 @@ impl<'a, 'tcx> Layout<'tcx> {
 
         let adt_kind = adt_def.adt_kind();
 
-        let build_field_info = |(field_name, field_ty): (ast::Name, Ty<'tcx>), offset: &Size| {
-            match (tcx, param_env).layout_of(field_ty) {
-                Err(_) => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty),
-                Ok(field_layout) => {
-                    session::FieldInfo {
-                        name: field_name.to_string(),
-                        offset: offset.bytes(),
-                        size: field_layout.size(tcx).bytes(),
-                        align: field_layout.align(tcx).abi(),
+        let build_variant_info = |n: Option<ast::Name>,
+                                  flds: &[ast::Name],
+                                  layout: FullLayout<'tcx>| {
+            let mut min_size = Size::from_bytes(0);
+            let field_info: Vec<_> = flds.iter().enumerate().map(|(i, &name)| {
+                match layout.field(cx, i) {
+                    Err(err) => {
+                        bug!("no layout found for field {}: `{:?}`", name, err);
+                    }
+                    Ok(field_layout) => {
+                        let offset = layout.fields.offset(i);
+                        let field_size = field_layout.size(tcx);
+                        let field_end = offset + field_size;
+                        if min_size < field_end {
+                            min_size = field_end;
+                        }
+                        session::FieldInfo {
+                            name: name.to_string(),
+                            offset: offset.bytes(),
+                            size: field_size.bytes(),
+                            align: field_layout.align(tcx).abi(),
+                        }
                     }
                 }
-            }
-        };
-
-        let build_variant_info = |n: Option<ast::Name>,
-                                  flds: &[(ast::Name, Ty<'tcx>)],
-                                  s: &Struct| {
-            let field_info: Vec<_> =
-                flds.iter()
-                    .zip(&s.offsets)
-                    .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset))
-                    .collect();
+            }).collect();
 
             session::VariantInfo {
                 name: n.map(|n|n.to_string()),
-                kind: if s.sized {
+                kind: if layout.is_unsized() {
+                    session::SizeKind::Min
+                } else {
                     session::SizeKind::Exact
+                },
+                align: layout.align(tcx).abi(),
+                size: if min_size.bytes() == 0 {
+                    layout.size(tcx).bytes()
                 } else {
-                    session::SizeKind::Min
+                    min_size.bytes()
                 },
-                align: s.align.abi(),
-                size: s.min_size.bytes(),
                 fields: field_info,
             }
         };
 
         match *layout.layout {
-            Layout::Univariant(ref variant_layout) => {
+            Layout::Univariant => {
                 let variant_names = || {
                     adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::<Vec<_>>()
                 };
-                debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}",
-                       ty, variant_layout, variant_names());
+                debug!("print-type-size `{:#?}` variants: {:?}",
+                       layout, variant_names());
                 assert!(adt_def.variants.len() <= 1,
                         "univariant with variants {:?}", variant_names());
                 if adt_def.variants.len() == 1 {
                     let variant_def = &adt_def.variants[0];
                     let fields: Vec<_> =
-                        variant_def.fields.iter()
-                                          .map(|f| (f.name, f.ty(tcx, substs)))
-                                          .collect();
+                        variant_def.fields.iter().map(|f| f.name).collect();
                     record(adt_kind.into(),
                            None,
                            vec![build_variant_info(Some(variant_def.name),
                                                    &fields,
-                                                   variant_layout)]);
+                                                   layout)]);
                 } else {
                     // (This case arises for *empty* enums; so give it
                     // zero variants.)
@@ -1844,28 +1706,19 @@ impl<'a, 'tcx> Layout<'tcx> {
                 }
             }
 
-            Layout::NullablePointer { ref variants, .. } |
-            Layout::General { ref variants, .. } => {
-                debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}",
-                       ty, adt_def.variants.len(), variants.len(), variants);
+            Layout::NullablePointer { .. } |
+            Layout::General { .. } => {
+                debug!("print-type-size `{:#?}` adt general variants def {}",
+                       ty, adt_def.variants.len());
                 let variant_infos: Vec<_> =
-                    adt_def.variants.iter()
-                                    .zip(variants.iter())
-                                    .map(|(variant_def, variant_layout)| {
-                                        let fields: Vec<_> =
-                                            variant_def.fields
-                                                       .iter()
-                                                       .map(|f| (f.name, f.ty(tcx, substs)))
-                                                       .collect();
-                                        let variant_layout = match *variant_layout.layout {
-                                            Univariant(ref variant) => variant,
-                                            _ => bug!()
-                                        };
-                                        build_variant_info(Some(variant_def.name),
-                                                           &fields,
-                                                           variant_layout)
-                                    })
-                                    .collect();
+                    adt_def.variants.iter().enumerate().map(|(i, variant_def)| {
+                        let fields: Vec<_> =
+                            variant_def.fields.iter().map(|f| f.name).collect();
+                        build_variant_info(Some(variant_def.name),
+                                            &fields,
+                                            layout.for_variant(i))
+                    })
+                    .collect();
                 record(adt_kind.into(), match *layout.layout {
                     Layout::General { discr, .. } => Some(discr.size(tcx)),
                     _ => None
@@ -2048,8 +1901,8 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> {
 pub struct FullLayout<'tcx> {
     pub ty: Ty<'tcx>,
     pub variant_index: Option<usize>,
-    pub layout: &'tcx Layout<'tcx>,
-    pub fields: FieldPlacement<'tcx>,
+    pub layout: &'tcx Layout,
+    pub fields: &'tcx FieldPlacement,
     pub abi: Abi,
 }
 
@@ -2101,8 +1954,8 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for (TyCtxt<'a, 'tcx, 'tcx>, ty::ParamEnv<'tcx
         let layout = FullLayout {
             ty,
             variant_index: None,
-            layout: cached.layout,
-            fields: cached.fields,
+            layout: &cached.layout,
+            fields: &cached.fields,
             abi: cached.abi
         };
 
@@ -2133,8 +1986,8 @@ impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for (ty::maps::TyCtxtAt<'a, 'tcx, 'tcx>,
         let layout = FullLayout {
             ty,
             variant_index: None,
-            layout: cached.layout,
-            fields: cached.fields,
+            layout: &cached.layout,
+            fields: &cached.fields,
             abi: cached.abi
         };
 
@@ -2163,12 +2016,12 @@ impl<'a, 'tcx> FullLayout<'tcx> {
         };
 
         let (layout, fields, abi) = match *self.layout {
-            Univariant(_) => (self.layout, self.fields, self.abi),
+            Univariant => (self.layout, self.fields, self.abi),
 
             NullablePointer { ref variants, .. } |
             General { ref variants, .. } => {
-                let variant = variants[variant_index];
-                (variant.layout, variant.fields, variant.abi)
+                let variant = &variants[variant_index];
+                (&variant.layout, &variant.fields, variant.abi)
             }
 
             _ => bug!()
@@ -2310,9 +2163,81 @@ impl<'a, 'tcx> FullLayout<'tcx> {
     pub fn primitive_align<C: HasDataLayout>(&self, cx: C) -> Align {
         self.abi.primitive_align(cx)
     }
+
+    /// Find the offset of a non-zero leaf field, starting from
+    /// the given type and recursing through aggregates.
+    /// The tuple is `(offset, primitive, source_path)`.
+    // FIXME(eddyb) track value ranges and traverse already optimized enums.
+    fn non_zero_field<C>(&self, cx: C)
+        -> Result<Option<(Size, Primitive)>, LayoutError<'tcx>>
+        where C: LayoutOf<Ty<'tcx>, FullLayout = Result<Self, LayoutError<'tcx>>> +
+                 HasTyCtxt<'tcx>
+    {
+        let tcx = cx.tcx();
+        match (self.layout, self.abi, &self.ty.sty) {
+            (&Scalar, Abi::Scalar(Pointer), _) if !self.ty.is_unsafe_ptr() => {
+                Ok(Some((Size::from_bytes(0), Pointer)))
+            }
+            (&General { discr, .. }, _, &ty::TyAdt(def, _)) => {
+                if def.discriminants(tcx).all(|d| d.to_u128_unchecked() != 0) {
+                    Ok(Some((self.fields.offset(0), discr)))
+                } else {
+                    Ok(None)
+                }
+            }
+
+            (&FatPointer, _, _) if !self.ty.is_unsafe_ptr() => {
+                Ok(Some((self.fields.offset(FAT_PTR_ADDR), Pointer)))
+            }
+
+            // Is this the NonZero lang item wrapping a pointer or integer type?
+            (_, _, &ty::TyAdt(def, _)) if Some(def.did) == tcx.lang_items().non_zero() => {
+                let field = self.field(cx, 0)?;
+                match (field.layout, field.abi) {
+                    (&Scalar, Abi::Scalar(value)) => {
+                        Ok(Some((self.fields.offset(0), value)))
+                    }
+                    (&FatPointer, _) => {
+                        Ok(Some((self.fields.offset(0) +
+                                 field.fields.offset(FAT_PTR_ADDR),
+                                 Pointer)))
+                    }
+                    _ => Ok(None)
+                }
+            }
+
+            // Perhaps one of the fields is non-zero, let's recurse and find out.
+            (&Univariant, _, _) => {
+                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)));
+                    }
+                }
+                Ok(None)
+            }
+
+            // Is this a fixed-size array of something non-zero
+            // with at least one element?
+            (_, _, &ty::TyArray(ety, _)) => {
+                if self.fields.count() != 0 {
+                    cx.layout_of(ety)?.non_zero_field(cx)
+                } else {
+                    Ok(None)
+                }
+            }
+
+            (_, _, &ty::TyProjection(_)) | (_, _, &ty::TyAnon(..)) => {
+                bug!("FullLayout::non_zero_field: {:#?} not normalized", self);
+            }
+
+            // Anything else is not a non-zero type.
+            _ => Ok(None)
+        }
+    }
 }
 
-impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout<'gcx> {
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'gcx>,
                                           hasher: &mut StableHasher<W>) {
@@ -2324,9 +2249,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout<'gcx> {
             Vector => {}
             Array => {}
             FatPointer => {}
-            Univariant(ref variant) => {
-                variant.hash_stable(hcx, hasher);
-            }
+            Univariant => {}
             UntaggedUnion => {}
             General {
                 discr,
@@ -2342,18 +2265,16 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Layout<'gcx> {
                 nndiscr,
                 ref variants,
                 ref discr,
-                discr_offset,
             } => {
                 nndiscr.hash_stable(hcx, hasher);
                 variants.hash_stable(hcx, hasher);
                 discr.hash_stable(hcx, hasher);
-                discr_offset.hash_stable(hcx, hasher);
             }
         }
     }
 }
 
-impl<'gcx> HashStable<StableHashingContext<'gcx>> for FieldPlacement<'gcx> {
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for FieldPlacement {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'gcx>,
                                           hasher: &mut StableHasher<W>) {
@@ -2365,8 +2286,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for FieldPlacement<'gcx> {
                 count.hash_stable(hcx, hasher);
                 stride.hash_stable(hcx, hasher);
             }
-            Arbitrary { offsets } => {
+            Arbitrary { ref offsets, ref memory_index } => {
                 offsets.hash_stable(hcx, hasher);
+                memory_index.hash_stable(hcx, hasher);
             }
         }
     }
@@ -2398,7 +2320,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for Abi {
     }
 }
 
-impl_stable_hash_for!(struct ::ty::layout::CachedLayout<'tcx> {
+impl_stable_hash_for!(struct ::ty::layout::CachedLayout {
     layout,
     fields,
     abi
@@ -2443,13 +2365,3 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for LayoutError<'gcx>
         }
     }
 }
-
-impl_stable_hash_for!(struct ::ty::layout::Struct {
-    align,
-    primitive_align,
-    packed,
-    sized,
-    offsets,
-    memory_index,
-    min_size
-});
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index 67467763089..ebd17ebabe7 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -264,7 +264,7 @@ define_maps! { <'tcx>
     [] fn is_freeze_raw: is_freeze_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
     [] fn needs_drop_raw: needs_drop_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool,
     [] fn layout_raw: layout_dep_node(ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
-                                  -> Result<ty::layout::CachedLayout<'tcx>,
+                                  -> Result<&'tcx ty::layout::CachedLayout,
                                             ty::layout::LayoutError<'tcx>>,
 
     [] fn dylib_dependency_formats: DylibDepFormats(CrateNum)
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index b727629e233..739b2a3789a 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -316,7 +316,7 @@ impl<'tcx> LayoutExt<'tcx> for FullLayout<'tcx> {
                 let mut total = Size::from_bytes(0);
                 let mut result = None;
 
-                let is_union = match self.fields {
+                let is_union = match *self.fields {
                     layout::FieldPlacement::Linear { stride, .. } => {
                         stride.bytes() == 0
                     }
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index cd68d042473..07c64c35c07 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -72,7 +72,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         return;
     }
     match *l.layout {
-        layout::Univariant(_) => {
+        layout::Univariant => {
             let is_enum = if let ty::TyAdt(def, _) = t.sty {
                 def.is_enum()
             } else {
@@ -100,7 +100,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         return cx.llvm_type_of(value.to_ty(cx.tcx()));
     }
     match *l.layout {
-        layout::Univariant(_) => {
+        layout::Univariant => {
             match name {
                 None => {
                     Type::struct_(cx, &struct_llfields(cx, l), l.is_packed())
@@ -152,11 +152,7 @@ pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
     let mut offset = Size::from_bytes(0);
     let mut result: Vec<Type> = Vec::with_capacity(1 + field_count * 2);
-    let field_index_by_increasing_offset = match *layout.layout {
-        layout::Univariant(ref variant) => variant.field_index_by_increasing_offset(),
-        _ => bug!("unexpected {:#?}", layout)
-    };
-    for i in field_index_by_increasing_offset {
+    for i in layout.fields.index_by_increasing_offset() {
         let field = layout.field(cx, i);
         let target_offset = layout.fields.offset(i as usize);
         debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}",
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index 82a4095aa01..d7397e359a1 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -64,8 +64,8 @@ pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
                                   -> bool {
     let layout = ccx.layout_of(ty);
     match *layout.layout {
-        Layout::FatPointer { .. } => true,
-        Layout::Univariant(_) => {
+        Layout::FatPointer => true,
+        Layout::Univariant => {
             // There must be only 2 fields.
             if layout.fields.count() != 2 {
                 return false;
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index b9ff46166a8..5948c3a3e59 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1159,7 +1159,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     }
                 }).collect()
             },
-            layout::Univariant(_) => {
+            layout::Univariant => {
                 assert!(adt.variants.len() <= 1);
 
                 if adt.variants.is_empty() {
@@ -1194,7 +1194,6 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
             layout::NullablePointer {
                 nndiscr,
                 discr,
-                discr_offset,
                 ..
             } => {
                 let variant = self.type_rep.for_variant(nndiscr as usize);
@@ -1239,7 +1238,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 }
                 compute_field_path(cx, &mut name,
                                    self.type_rep,
-                                   discr_offset,
+                                   self.type_rep.fields.offset(0),
                                    discr.size(cx));
                 name.push_str(&adt.variants[(1 - nndiscr) as usize].name.as_str());
 
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index d6e2257ab24..ae19c865d1c 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -1117,11 +1117,11 @@ fn trans_const_adt<'a, 'tcx>(
 
             Const::new(C_struct(ccx, &contents, l.is_packed()), t)
         }
-        layout::Univariant(_) => {
+        layout::Univariant => {
             assert_eq!(variant_index, 0);
             build_const_struct(ccx, l, vals, None)
         }
-        layout::Vector { .. } => {
+        layout::Vector => {
             Const::new(C_vector(&vals.iter().map(|x| x.llval).collect::<Vec<_>>()), t)
         }
         layout::NullablePointer { nndiscr, .. } => {
@@ -1162,11 +1162,7 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         offset = ccx.size_of(discr.ty);
     }
 
-    let field_index_by_increasing_offset = match *layout.layout {
-        layout::Univariant(ref variant) => variant.field_index_by_increasing_offset(),
-        _ => bug!("unexpected {:#?}", layout)
-    };
-    let parts = field_index_by_increasing_offset.map(|i| {
+    let parts = layout.fields.index_by_increasing_offset().map(|i| {
         (vals[i], layout.fields.offset(i))
     });
     for (val, target_offset) in parts {
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index 679632d9113..bd37bfb01d7 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -240,25 +240,18 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
         if let layout::Abi::Scalar(_) = self.abi {
             bug!("FullLayout::llvm_field_index({:?}): not applicable", self);
         }
+        let index = self.fields.memory_index(index);
         match *self.layout {
-            Layout::Scalar { .. } |
-            Layout::UntaggedUnion { .. } |
-            Layout::NullablePointer { .. } |
-            Layout::General { .. } => {
-                bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
-            }
-
-            Layout::Vector { .. } |
-            Layout::Array { .. } => {
+            Layout::Vector | Layout::Array => {
                 index as u64
             }
 
-            Layout::FatPointer { .. } => {
+            Layout::FatPointer | Layout::Univariant => {
                 adt::memory_index_to_gep(index as u64)
             }
 
-            Layout::Univariant(ref variant) => {
-                adt::memory_index_to_gep(variant.memory_index[index] as u64)
+            _ => {
+                bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
             }
         }
     }