Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of #[repr(packed(n))] RFC 1399. #48528

Merged
merged 1 commit into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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