Skip to content

Commit

Permalink
Implementation of #[repr(packed(n))] RFC 1399.
Browse files Browse the repository at this point in the history
  • Loading branch information
bitshifter committed Apr 11, 2018
1 parent ca26ef3 commit 15d1c4d
Show file tree
Hide file tree
Showing 26 changed files with 844 additions and 162 deletions.
8 changes: 8 additions & 0 deletions src/doc/unstable-book/src/language-features/repr-packed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `repr_packed`

The tracking issue for this feature is [#33158]

[#33158]: https://github.com/rust-lang/rust/issues/33158

------------------------

37 changes: 21 additions & 16 deletions src/librustc/session/code_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct TypeSizeInfo {
pub type_description: String,
pub align: u64,
pub overall_size: u64,
pub packed: bool,
pub opt_discr_size: Option<u64>,
pub variants: Vec<VariantInfo>,
}
Expand All @@ -79,13 +80,15 @@ impl CodeStats {
type_desc: S,
align: Align,
overall_size: Size,
packed: bool,
opt_discr_size: Option<Size>,
variants: Vec<VariantInfo>) {
let info = TypeSizeInfo {
kind,
type_description: type_desc.to_string(),
align: align.abi(),
overall_size: overall_size.bytes(),
packed: packed,
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
variants,
};
Expand Down Expand Up @@ -153,24 +156,26 @@ impl CodeStats {
for field in fields.iter() {
let FieldInfo { ref name, offset, size, align } = *field;

// Include field alignment in output only if it caused padding injection
if min_offset != offset {
if offset > min_offset {
let pad = offset - min_offset;
println!("print-type-size {}padding: {} bytes",
indent, pad);
println!("print-type-size {}field `.{}`: {} bytes, \
alignment: {} bytes",
indent, name, size, align);
} else {
println!("print-type-size {}field `.{}`: {} bytes, \
offset: {} bytes, \
alignment: {} bytes",
indent, name, size, offset, align);
}
} else {
if offset > min_offset {
let pad = offset - min_offset;
println!("print-type-size {}padding: {} bytes",
indent, pad);
}

if offset < min_offset {
// if this happens something is very wrong
println!("print-type-size {}field `.{}`: {} bytes, \
offset: {} bytes, \
alignment: {} bytes",
indent, name, size, offset, align);
} else if info.packed || offset == min_offset {
println!("print-type-size {}field `.{}`: {} bytes",
indent, name, size);
} else {
// Include field alignment in output only if it caused padding injection
println!("print-type-size {}field `.{}`: {} bytes, \
alignment: {} bytes",
indent, name, size, align);
}

min_offset = offset + size;
Expand Down
78 changes: 52 additions & 26 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use self::Integer::*;
pub use self::Primitive::*;

use session::{self, DataTypeKind, Session};
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};

use syntax::ast::{self, FloatTy, IntTy, UintTy};
use syntax::attr;
Expand Down Expand Up @@ -344,8 +344,8 @@ impl AddAssign for Size {
/// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct Align {
abi: u8,
pref: u8,
abi_pow2: u8,
pref_pow2: u8,
}

impl Align {
Expand Down Expand Up @@ -377,17 +377,17 @@ impl Align {
};

Ok(Align {
abi: log2(abi)?,
pref: log2(pref)?,
abi_pow2: log2(abi)?,
pref_pow2: log2(pref)?,
})
}

pub fn abi(self) -> u64 {
1 << self.abi
1 << self.abi_pow2
}

pub fn pref(self) -> u64 {
1 << self.pref
1 << self.pref_pow2
}

pub fn abi_bits(self) -> u64 {
Expand All @@ -400,15 +400,15 @@ impl Align {

pub fn min(self, other: Align) -> Align {
Align {
abi: cmp::min(self.abi, other.abi),
pref: cmp::min(self.pref, other.pref),
abi_pow2: cmp::min(self.abi_pow2, other.abi_pow2),
pref_pow2: cmp::min(self.pref_pow2, other.pref_pow2),
}
}

pub fn max(self, other: Align) -> Align {
Align {
abi: cmp::max(self.abi, other.abi),
pref: cmp::max(self.pref, other.pref),
abi_pow2: cmp::max(self.abi_pow2, other.abi_pow2),
pref_pow2: cmp::max(self.pref_pow2, other.pref_pow2),
}
}
}
Expand Down Expand Up @@ -974,6 +974,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
bug!("struct cannot be packed and aligned");
}

let pack = {
let pack = repr.pack as u64;
Align::from_bytes(pack, pack).unwrap()
};

let mut align = if packed {
dl.i8_align
} else {
Expand All @@ -984,8 +989,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let mut offsets = vec![Size::from_bytes(0); fields.len()];
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();

// Anything with repr(C) or repr(packed) doesn't optimize.
let mut optimize = (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
let mut optimize = !repr.inhibit_struct_field_reordering_opt();
if let StructKind::Prefixed(_, align) = kind {
optimize &= align.abi() == 1;
}
Expand All @@ -997,18 +1001,21 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
fields.len()
};
let optimizing = &mut inverse_memory_index[..end];
let field_align = |f: &TyLayout| {
if packed { f.align.min(pack).abi() } else { f.align.abi() }
};
match kind {
StructKind::AlwaysSized |
StructKind::MaybeUnsized => {
optimizing.sort_by_key(|&x| {
// Place ZSTs first to avoid "interesting offsets",
// especially with only one or two non-ZST fields.
let f = &fields[x as usize];
(!f.is_zst(), cmp::Reverse(f.align.abi()))
})
(!f.is_zst(), cmp::Reverse(field_align(f)))
});
}
StructKind::Prefixed(..) => {
optimizing.sort_by_key(|&x| fields[x as usize].align.abi());
optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
}
}
}
Expand All @@ -1022,7 +1029,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let mut offset = Size::from_bytes(0);

if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
if !packed {
if packed {
let prefix_align = prefix_align.min(pack);
align = align.max(prefix_align);
} else {
align = align.max(prefix_align);
}
offset = prefix_size.abi_align(prefix_align);
Expand All @@ -1044,7 +1054,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
}

// Invariant: offset < dl.obj_size_bound() <= 1<<61
if !packed {
if packed {
let field_pack = field.align.min(pack);
offset = offset.abi_align(field_pack);
align = align.max(field_pack);
}
else {
offset = offset.abi_align(field.align);
align = align.max(field.align);
}
Expand Down Expand Up @@ -1377,7 +1392,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
bug!("Union cannot be packed and aligned");
}

let mut align = if def.repr.packed() {
let pack = {
let pack = def.repr.pack as u64;
Align::from_bytes(pack, pack).unwrap()
};

let mut align = if packed {
dl.i8_align
} else {
dl.aggregate_align
Expand All @@ -1393,7 +1413,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
for field in &variants[0] {
assert!(!field.is_unsized());

if !packed {
if packed {
let field_pack = field.align.min(pack);
align = align.max(field_pack);
} else {
align = align.max(field.align);
}
size = cmp::max(size, field.size);
Expand Down Expand Up @@ -1740,12 +1763,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {

fn record_layout_for_printing_outlined(self, layout: TyLayout<'tcx>) {
// (delay format until we actually need it)
let record = |kind, opt_discr_size, variants| {
let record = |kind, packed, opt_discr_size, variants| {
let type_desc = format!("{:?}", layout.ty);
self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
type_desc,
layout.align,
layout.size,
packed,
opt_discr_size,
variants);
};
Expand All @@ -1758,7 +1782,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {

ty::TyClosure(..) => {
debug!("print-type-size t: `{:?}` record closure", layout.ty);
record(DataTypeKind::Closure, None, vec![]);
record(DataTypeKind::Closure, false, None, vec![]);
return;
}

Expand All @@ -1769,6 +1793,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
};

let adt_kind = adt_def.adt_kind();
let adt_packed = adt_def.repr.packed();

let build_variant_info = |n: Option<ast::Name>,
flds: &[ast::Name],
Expand Down Expand Up @@ -1821,14 +1846,15 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let fields: Vec<_> =
variant_def.fields.iter().map(|f| f.name).collect();
record(adt_kind.into(),
adt_packed,
None,
vec![build_variant_info(Some(variant_def.name),
&fields,
layout)]);
} else {
// (This case arises for *empty* enums; so give it
// zero variants.)
record(adt_kind.into(), None, vec![]);
record(adt_kind.into(), adt_packed, None, vec![]);
}
}

Expand All @@ -1845,7 +1871,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
layout.for_variant(self, i))
})
.collect();
record(adt_kind.into(), match layout.variants {
record(adt_kind.into(), adt_packed, match layout.variants {
Variants::Tagged { ref discr, .. } => Some(discr.value.size(self)),
_ => None
}, variant_infos);
Expand Down Expand Up @@ -2518,8 +2544,8 @@ impl_stable_hash_for!(enum ::ty::layout::Primitive {
});

impl_stable_hash_for!(struct ::ty::layout::Align {
abi,
pref
abi_pow2,
pref_pow2
});

impl_stable_hash_for!(struct ::ty::layout::Size {
Expand Down
30 changes: 22 additions & 8 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1623,15 +1623,13 @@ bitflags! {
#[derive(RustcEncodable, RustcDecodable, Default)]
pub struct ReprFlags: u8 {
const IS_C = 1 << 0;
const IS_PACKED = 1 << 1;
const IS_SIMD = 1 << 2;
const IS_TRANSPARENT = 1 << 3;
const IS_SIMD = 1 << 1;
const IS_TRANSPARENT = 1 << 2;
// Internal only for now. If true, don't reorder fields.
const IS_LINEAR = 1 << 4;
const IS_LINEAR = 1 << 3;

// Any of these flags being set prevent field reordering optimisation.
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
ReprFlags::IS_PACKED.bits |
ReprFlags::IS_SIMD.bits |
ReprFlags::IS_LINEAR.bits;
}
Expand All @@ -1648,11 +1646,13 @@ impl_stable_hash_for!(struct ReprFlags {
pub struct ReprOptions {
pub int: Option<attr::IntType>,
pub align: u32,
pub pack: u32,
pub flags: ReprFlags,
}

impl_stable_hash_for!(struct ReprOptions {
align,
pack,
int,
flags
});
Expand All @@ -1662,11 +1662,19 @@ impl ReprOptions {
let mut flags = ReprFlags::empty();
let mut size = None;
let mut max_align = 0;
let mut min_pack = 0;
for attr in tcx.get_attrs(did).iter() {
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
flags.insert(match r {
attr::ReprC => ReprFlags::IS_C,
attr::ReprPacked => ReprFlags::IS_PACKED,
attr::ReprPacked(pack) => {
min_pack = if min_pack > 0 {
cmp::min(pack, min_pack)
} else {
pack
};
ReprFlags::empty()
},
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
attr::ReprSimd => ReprFlags::IS_SIMD,
attr::ReprInt(i) => {
Expand All @@ -1685,15 +1693,15 @@ impl ReprOptions {
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
flags.insert(ReprFlags::IS_LINEAR);
}
ReprOptions { int: size, align: max_align, flags: flags }
ReprOptions { int: size, align: max_align, pack: min_pack, flags: flags }
}

#[inline]
pub fn simd(&self) -> bool { self.flags.contains(ReprFlags::IS_SIMD) }
#[inline]
pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
#[inline]
pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
pub fn packed(&self) -> bool { self.pack > 0 }
#[inline]
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
#[inline]
Expand All @@ -1709,6 +1717,12 @@ impl ReprOptions {
pub fn inhibit_enum_layout_opt(&self) -> bool {
self.c() || self.int.is_some()
}

/// Returns true if this `#[repr()]` should inhibit struct field reordering
/// optimizations, such as with repr(C) or repr(packed(1)).
pub fn inhibit_struct_field_reordering_opt(&self) -> bool {
!(self.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty() || (self.pack == 1)
}
}

impl<'a, 'gcx, 'tcx> AdtDef {
Expand Down
Loading

0 comments on commit 15d1c4d

Please sign in to comment.