Skip to content

Commit 4777881

Browse files
committed
Auto merge of #48528 - bitshifter:repr_packed, r=eddyb
Implementation of `#[repr(packed(n))]` RFC 1399. Tracking issue #33158.
2 parents d26f9e4 + 15d1c4d commit 4777881

26 files changed

+844
-162
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# `repr_packed`
2+
3+
The tracking issue for this feature is [#33158]
4+
5+
[#33158]: https://github.com/rust-lang/rust/issues/33158
6+
7+
------------------------
8+

src/librustc/session/code_stats.rs

+21-16
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub struct TypeSizeInfo {
6262
pub type_description: String,
6363
pub align: u64,
6464
pub overall_size: u64,
65+
pub packed: bool,
6566
pub opt_discr_size: Option<u64>,
6667
pub variants: Vec<VariantInfo>,
6768
}
@@ -79,13 +80,15 @@ impl CodeStats {
7980
type_desc: S,
8081
align: Align,
8182
overall_size: Size,
83+
packed: bool,
8284
opt_discr_size: Option<Size>,
8385
variants: Vec<VariantInfo>) {
8486
let info = TypeSizeInfo {
8587
kind,
8688
type_description: type_desc.to_string(),
8789
align: align.abi(),
8890
overall_size: overall_size.bytes(),
91+
packed: packed,
8992
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
9093
variants,
9194
};
@@ -153,24 +156,26 @@ impl CodeStats {
153156
for field in fields.iter() {
154157
let FieldInfo { ref name, offset, size, align } = *field;
155158

156-
// Include field alignment in output only if it caused padding injection
157-
if min_offset != offset {
158-
if offset > min_offset {
159-
let pad = offset - min_offset;
160-
println!("print-type-size {}padding: {} bytes",
161-
indent, pad);
162-
println!("print-type-size {}field `.{}`: {} bytes, \
163-
alignment: {} bytes",
164-
indent, name, size, align);
165-
} else {
166-
println!("print-type-size {}field `.{}`: {} bytes, \
167-
offset: {} bytes, \
168-
alignment: {} bytes",
169-
indent, name, size, offset, align);
170-
}
171-
} else {
159+
if offset > min_offset {
160+
let pad = offset - min_offset;
161+
println!("print-type-size {}padding: {} bytes",
162+
indent, pad);
163+
}
164+
165+
if offset < min_offset {
166+
// if this happens something is very wrong
167+
println!("print-type-size {}field `.{}`: {} bytes, \
168+
offset: {} bytes, \
169+
alignment: {} bytes",
170+
indent, name, size, offset, align);
171+
} else if info.packed || offset == min_offset {
172172
println!("print-type-size {}field `.{}`: {} bytes",
173173
indent, name, size);
174+
} else {
175+
// Include field alignment in output only if it caused padding injection
176+
println!("print-type-size {}field `.{}`: {} bytes, \
177+
alignment: {} bytes",
178+
indent, name, size, align);
174179
}
175180

176181
min_offset = offset + size;

src/librustc/ty/layout.rs

+52-26
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub use self::Integer::*;
1212
pub use self::Primitive::*;
1313

1414
use session::{self, DataTypeKind, Session};
15-
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
15+
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};
1616

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

351351
impl Align {
@@ -377,17 +377,17 @@ impl Align {
377377
};
378378

379379
Ok(Align {
380-
abi: log2(abi)?,
381-
pref: log2(pref)?,
380+
abi_pow2: log2(abi)?,
381+
pref_pow2: log2(pref)?,
382382
})
383383
}
384384

385385
pub fn abi(self) -> u64 {
386-
1 << self.abi
386+
1 << self.abi_pow2
387387
}
388388

389389
pub fn pref(self) -> u64 {
390-
1 << self.pref
390+
1 << self.pref_pow2
391391
}
392392

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

401401
pub fn min(self, other: Align) -> Align {
402402
Align {
403-
abi: cmp::min(self.abi, other.abi),
404-
pref: cmp::min(self.pref, other.pref),
403+
abi_pow2: cmp::min(self.abi_pow2, other.abi_pow2),
404+
pref_pow2: cmp::min(self.pref_pow2, other.pref_pow2),
405405
}
406406
}
407407

408408
pub fn max(self, other: Align) -> Align {
409409
Align {
410-
abi: cmp::max(self.abi, other.abi),
411-
pref: cmp::max(self.pref, other.pref),
410+
abi_pow2: cmp::max(self.abi_pow2, other.abi_pow2),
411+
pref_pow2: cmp::max(self.pref_pow2, other.pref_pow2),
412412
}
413413
}
414414
}
@@ -974,6 +974,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
974974
bug!("struct cannot be packed and aligned");
975975
}
976976

977+
let pack = {
978+
let pack = repr.pack as u64;
979+
Align::from_bytes(pack, pack).unwrap()
980+
};
981+
977982
let mut align = if packed {
978983
dl.i8_align
979984
} else {
@@ -984,8 +989,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
984989
let mut offsets = vec![Size::from_bytes(0); fields.len()];
985990
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
986991

987-
// Anything with repr(C) or repr(packed) doesn't optimize.
988-
let mut optimize = (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
992+
let mut optimize = !repr.inhibit_struct_field_reordering_opt();
989993
if let StructKind::Prefixed(_, align) = kind {
990994
optimize &= align.abi() == 1;
991995
}
@@ -997,18 +1001,21 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
9971001
fields.len()
9981002
};
9991003
let optimizing = &mut inverse_memory_index[..end];
1004+
let field_align = |f: &TyLayout| {
1005+
if packed { f.align.min(pack).abi() } else { f.align.abi() }
1006+
};
10001007
match kind {
10011008
StructKind::AlwaysSized |
10021009
StructKind::MaybeUnsized => {
10031010
optimizing.sort_by_key(|&x| {
10041011
// Place ZSTs first to avoid "interesting offsets",
10051012
// especially with only one or two non-ZST fields.
10061013
let f = &fields[x as usize];
1007-
(!f.is_zst(), cmp::Reverse(f.align.abi()))
1008-
})
1014+
(!f.is_zst(), cmp::Reverse(field_align(f)))
1015+
});
10091016
}
10101017
StructKind::Prefixed(..) => {
1011-
optimizing.sort_by_key(|&x| fields[x as usize].align.abi());
1018+
optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
10121019
}
10131020
}
10141021
}
@@ -1022,7 +1029,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
10221029
let mut offset = Size::from_bytes(0);
10231030

10241031
if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
1025-
if !packed {
1032+
if packed {
1033+
let prefix_align = prefix_align.min(pack);
1034+
align = align.max(prefix_align);
1035+
} else {
10261036
align = align.max(prefix_align);
10271037
}
10281038
offset = prefix_size.abi_align(prefix_align);
@@ -1044,7 +1054,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
10441054
}
10451055

10461056
// Invariant: offset < dl.obj_size_bound() <= 1<<61
1047-
if !packed {
1057+
if packed {
1058+
let field_pack = field.align.min(pack);
1059+
offset = offset.abi_align(field_pack);
1060+
align = align.max(field_pack);
1061+
}
1062+
else {
10481063
offset = offset.abi_align(field.align);
10491064
align = align.max(field.align);
10501065
}
@@ -1377,7 +1392,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
13771392
bug!("Union cannot be packed and aligned");
13781393
}
13791394

1380-
let mut align = if def.repr.packed() {
1395+
let pack = {
1396+
let pack = def.repr.pack as u64;
1397+
Align::from_bytes(pack, pack).unwrap()
1398+
};
1399+
1400+
let mut align = if packed {
13811401
dl.i8_align
13821402
} else {
13831403
dl.aggregate_align
@@ -1393,7 +1413,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
13931413
for field in &variants[0] {
13941414
assert!(!field.is_unsized());
13951415

1396-
if !packed {
1416+
if packed {
1417+
let field_pack = field.align.min(pack);
1418+
align = align.max(field_pack);
1419+
} else {
13971420
align = align.max(field.align);
13981421
}
13991422
size = cmp::max(size, field.size);
@@ -1740,12 +1763,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
17401763

17411764
fn record_layout_for_printing_outlined(self, layout: TyLayout<'tcx>) {
17421765
// (delay format until we actually need it)
1743-
let record = |kind, opt_discr_size, variants| {
1766+
let record = |kind, packed, opt_discr_size, variants| {
17441767
let type_desc = format!("{:?}", layout.ty);
17451768
self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
17461769
type_desc,
17471770
layout.align,
17481771
layout.size,
1772+
packed,
17491773
opt_discr_size,
17501774
variants);
17511775
};
@@ -1758,7 +1782,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
17581782

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

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

17711795
let adt_kind = adt_def.adt_kind();
1796+
let adt_packed = adt_def.repr.packed();
17721797

17731798
let build_variant_info = |n: Option<ast::Name>,
17741799
flds: &[ast::Name],
@@ -1821,14 +1846,15 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
18211846
let fields: Vec<_> =
18221847
variant_def.fields.iter().map(|f| f.name).collect();
18231848
record(adt_kind.into(),
1849+
adt_packed,
18241850
None,
18251851
vec![build_variant_info(Some(variant_def.name),
18261852
&fields,
18271853
layout)]);
18281854
} else {
18291855
// (This case arises for *empty* enums; so give it
18301856
// zero variants.)
1831-
record(adt_kind.into(), None, vec![]);
1857+
record(adt_kind.into(), adt_packed, None, vec![]);
18321858
}
18331859
}
18341860

@@ -1845,7 +1871,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
18451871
layout.for_variant(self, i))
18461872
})
18471873
.collect();
1848-
record(adt_kind.into(), match layout.variants {
1874+
record(adt_kind.into(), adt_packed, match layout.variants {
18491875
Variants::Tagged { ref discr, .. } => Some(discr.value.size(self)),
18501876
_ => None
18511877
}, variant_infos);
@@ -2518,8 +2544,8 @@ impl_stable_hash_for!(enum ::ty::layout::Primitive {
25182544
});
25192545

25202546
impl_stable_hash_for!(struct ::ty::layout::Align {
2521-
abi,
2522-
pref
2547+
abi_pow2,
2548+
pref_pow2
25232549
});
25242550

25252551
impl_stable_hash_for!(struct ::ty::layout::Size {

src/librustc/ty/mod.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -1623,15 +1623,13 @@ bitflags! {
16231623
#[derive(RustcEncodable, RustcDecodable, Default)]
16241624
pub struct ReprFlags: u8 {
16251625
const IS_C = 1 << 0;
1626-
const IS_PACKED = 1 << 1;
1627-
const IS_SIMD = 1 << 2;
1628-
const IS_TRANSPARENT = 1 << 3;
1626+
const IS_SIMD = 1 << 1;
1627+
const IS_TRANSPARENT = 1 << 2;
16291628
// Internal only for now. If true, don't reorder fields.
1630-
const IS_LINEAR = 1 << 4;
1629+
const IS_LINEAR = 1 << 3;
16311630

16321631
// Any of these flags being set prevent field reordering optimisation.
16331632
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
1634-
ReprFlags::IS_PACKED.bits |
16351633
ReprFlags::IS_SIMD.bits |
16361634
ReprFlags::IS_LINEAR.bits;
16371635
}
@@ -1648,11 +1646,13 @@ impl_stable_hash_for!(struct ReprFlags {
16481646
pub struct ReprOptions {
16491647
pub int: Option<attr::IntType>,
16501648
pub align: u32,
1649+
pub pack: u32,
16511650
pub flags: ReprFlags,
16521651
}
16531652

16541653
impl_stable_hash_for!(struct ReprOptions {
16551654
align,
1655+
pack,
16561656
int,
16571657
flags
16581658
});
@@ -1662,11 +1662,19 @@ impl ReprOptions {
16621662
let mut flags = ReprFlags::empty();
16631663
let mut size = None;
16641664
let mut max_align = 0;
1665+
let mut min_pack = 0;
16651666
for attr in tcx.get_attrs(did).iter() {
16661667
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
16671668
flags.insert(match r {
16681669
attr::ReprC => ReprFlags::IS_C,
1669-
attr::ReprPacked => ReprFlags::IS_PACKED,
1670+
attr::ReprPacked(pack) => {
1671+
min_pack = if min_pack > 0 {
1672+
cmp::min(pack, min_pack)
1673+
} else {
1674+
pack
1675+
};
1676+
ReprFlags::empty()
1677+
},
16701678
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
16711679
attr::ReprSimd => ReprFlags::IS_SIMD,
16721680
attr::ReprInt(i) => {
@@ -1685,15 +1693,15 @@ impl ReprOptions {
16851693
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
16861694
flags.insert(ReprFlags::IS_LINEAR);
16871695
}
1688-
ReprOptions { int: size, align: max_align, flags: flags }
1696+
ReprOptions { int: size, align: max_align, pack: min_pack, flags: flags }
16891697
}
16901698

16911699
#[inline]
16921700
pub fn simd(&self) -> bool { self.flags.contains(ReprFlags::IS_SIMD) }
16931701
#[inline]
16941702
pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
16951703
#[inline]
1696-
pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
1704+
pub fn packed(&self) -> bool { self.pack > 0 }
16971705
#[inline]
16981706
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
16991707
#[inline]
@@ -1709,6 +1717,12 @@ impl ReprOptions {
17091717
pub fn inhibit_enum_layout_opt(&self) -> bool {
17101718
self.c() || self.int.is_some()
17111719
}
1720+
1721+
/// Returns true if this `#[repr()]` should inhibit struct field reordering
1722+
/// optimizations, such as with repr(C) or repr(packed(1)).
1723+
pub fn inhibit_struct_field_reordering_opt(&self) -> bool {
1724+
!(self.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty() || (self.pack == 1)
1725+
}
17121726
}
17131727

17141728
impl<'a, 'gcx, 'tcx> AdtDef {

0 commit comments

Comments
 (0)