Skip to content

Commit 6d841da

Browse files
committed
Auto merge of #39999 - bitshifter:struct_align, r=eddyb
Implementation of repr struct alignment RFC 1358. The main changes around rustc::ty::Layout::struct: * Added abi_align field which stores abi alignment before repr align is applied * align field contains transitive repr alignment * Added padding vec which stores padding required after fields The main user of this information is rustc_trans::adt::struct_llfields which determines the LLVM fields to be used by LLVM, including padding fields. A possible future optimisation would be to put the padding Vec in an Option, since it will be unused unless you are using repr align.
2 parents ff13b7c + 946f8e6 commit 6d841da

30 files changed

+692
-86
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
- [proc_macro](language-features/proc-macro.md)
7373
- [quote](language-features/quote.md)
7474
- [relaxed_adts](language-features/relaxed-adts.md)
75+
- [repr_align](language-features/repr-align.md)
7576
- [repr_simd](language-features/repr-simd.md)
7677
- [rustc_attrs](language-features/rustc-attrs.md)
7778
- [rustc_diagnostic_macros](language-features/rustc-diagnostic-macros.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# `repr_align`
2+
3+
The tracking issue for this feature is: [#33626]
4+
5+
[#33626]: https://github.com/rust-lang/rust/issues/33626
6+
7+
------------------------
8+
9+
10+
11+

src/librustc/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1847,5 +1847,6 @@ register_diagnostics! {
18471847
E0489, // type/lifetime parameter not in scope here
18481848
E0490, // a value of type `..` is borrowed for too long
18491849
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
1850-
E0566 // conflicting representation hints
1850+
E0566, // conflicting representation hints
1851+
E0587, // conflicting packed and align representation hints
18511852
}

src/librustc/hir/check_attr.rs

+17
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ impl<'a> CheckAttrVisitor<'a> {
5757
};
5858

5959
let mut conflicting_reprs = 0;
60+
let mut found_packed = false;
61+
let mut found_align = false;
62+
6063
for word in words {
6164

6265
let name = match word.name() {
@@ -84,6 +87,7 @@ impl<'a> CheckAttrVisitor<'a> {
8487
("attribute should be applied to struct or union",
8588
"a struct or union")
8689
} else {
90+
found_packed = true;
8791
continue
8892
}
8993
}
@@ -96,6 +100,15 @@ impl<'a> CheckAttrVisitor<'a> {
96100
continue
97101
}
98102
}
103+
"align" => {
104+
found_align = true;
105+
if target != Target::Struct {
106+
("attribute should be applied to struct",
107+
"a struct")
108+
} else {
109+
continue
110+
}
111+
}
99112
"i8" | "u8" | "i16" | "u16" |
100113
"i32" | "u32" | "i64" | "u64" |
101114
"isize" | "usize" => {
@@ -117,6 +130,10 @@ impl<'a> CheckAttrVisitor<'a> {
117130
span_warn!(self.sess, attr.span, E0566,
118131
"conflicting representation hints");
119132
}
133+
if found_align && found_packed {
134+
struct_span_err!(self.sess, attr.span, E0587,
135+
"conflicting packed and align representation hints").emit();
136+
}
120137
}
121138

122139
fn check_attribute(&self, attr: &ast::Attribute, target: Target) {

src/librustc/ty/layout.rs

+83-6
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,12 @@ pub type FieldPath = Vec<u32>;
548548
/// A structure, a product type in ADT terms.
549549
#[derive(PartialEq, Eq, Hash, Debug)]
550550
pub struct Struct {
551+
/// Maximum alignment of fields and repr alignment.
551552
pub align: Align,
552553

554+
/// Primitive alignment of fields without repr alignment.
555+
pub primitive_align: Align,
556+
553557
/// If true, no alignment padding is used.
554558
pub packed: bool,
555559

@@ -583,10 +587,20 @@ impl<'a, 'gcx, 'tcx> Struct {
583587
fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>,
584588
repr: &ReprOptions, kind: StructKind,
585589
scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> {
586-
let packed = repr.packed();
590+
if repr.packed() && repr.align > 0 {
591+
bug!("Struct cannot be packed and aligned");
592+
}
593+
594+
let align = if repr.packed() {
595+
dl.i8_align
596+
} else {
597+
dl.aggregate_align
598+
};
599+
587600
let mut ret = Struct {
588-
align: if packed { dl.i8_align } else { dl.aggregate_align },
589-
packed: packed,
601+
align: align,
602+
primitive_align: align,
603+
packed: repr.packed(),
590604
sized: true,
591605
offsets: vec![],
592606
memory_index: vec![],
@@ -660,7 +674,9 @@ impl<'a, 'gcx, 'tcx> Struct {
660674
// Invariant: offset < dl.obj_size_bound() <= 1<<61
661675
if !ret.packed {
662676
let align = field.align(dl);
677+
let primitive_align = field.primitive_align(dl);
663678
ret.align = ret.align.max(align);
679+
ret.primitive_align = ret.primitive_align.max(primitive_align);
664680
offset = offset.abi_align(align);
665681
}
666682

@@ -671,6 +687,11 @@ impl<'a, 'gcx, 'tcx> Struct {
671687
.map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?;
672688
}
673689

690+
if repr.align > 0 {
691+
let repr_align = repr.align as u64;
692+
ret.align = ret.align.max(Align::from_bytes(repr_align, repr_align).unwrap());
693+
debug!("Struct::new repr_align: {:?}", repr_align);
694+
}
674695

675696
debug!("Struct::new min_size: {:?}", offset);
676697
ret.min_size = offset;
@@ -836,12 +857,23 @@ impl<'a, 'gcx, 'tcx> Struct {
836857
}
837858
Ok(None)
838859
}
860+
861+
pub fn over_align(&self) -> Option<u32> {
862+
let align = self.align.abi();
863+
let primitive_align = self.primitive_align.abi();
864+
if align > primitive_align {
865+
Some(align as u32)
866+
} else {
867+
None
868+
}
869+
}
839870
}
840871

841872
/// An untagged union.
842873
#[derive(PartialEq, Eq, Hash, Debug)]
843874
pub struct Union {
844875
pub align: Align,
876+
pub primitive_align: Align,
845877

846878
pub min_size: Size,
847879

@@ -851,8 +883,10 @@ pub struct Union {
851883

852884
impl<'a, 'gcx, 'tcx> Union {
853885
fn new(dl: &TargetDataLayout, packed: bool) -> Union {
886+
let align = if packed { dl.i8_align } else { dl.aggregate_align };
854887
Union {
855-
align: if packed { dl.i8_align } else { dl.aggregate_align },
888+
align: align,
889+
primitive_align: align,
856890
min_size: Size::from_bytes(0),
857891
packed: packed,
858892
}
@@ -875,6 +909,7 @@ impl<'a, 'gcx, 'tcx> Union {
875909

876910
if !self.packed {
877911
self.align = self.align.max(field.align(dl));
912+
self.primitive_align = self.primitive_align.max(field.primitive_align(dl));
878913
}
879914
self.min_size = cmp::max(self.min_size, field.size(dl));
880915
}
@@ -888,6 +923,16 @@ impl<'a, 'gcx, 'tcx> Union {
888923
pub fn stride(&self) -> Size {
889924
self.min_size.abi_align(self.align)
890925
}
926+
927+
pub fn over_align(&self) -> Option<u32> {
928+
let align = self.align.abi();
929+
let primitive_align = self.primitive_align.abi();
930+
if align > primitive_align {
931+
Some(align as u32)
932+
} else {
933+
None
934+
}
935+
}
891936
}
892937

893938
/// The first half of a fat pointer.
@@ -924,6 +969,7 @@ pub enum Layout {
924969
/// If true, the size is exact, otherwise it's only a lower bound.
925970
sized: bool,
926971
align: Align,
972+
primitive_align: Align,
927973
element_size: Size,
928974
count: u64
929975
},
@@ -970,7 +1016,8 @@ pub enum Layout {
9701016
discr: Integer,
9711017
variants: Vec<Struct>,
9721018
size: Size,
973-
align: Align
1019+
align: Align,
1020+
primitive_align: Align,
9741021
},
9751022

9761023
/// Two cases distinguished by a nullable pointer: the case with discriminant
@@ -1118,6 +1165,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11181165
Array {
11191166
sized: true,
11201167
align: element.align(dl),
1168+
primitive_align: element.primitive_align(dl),
11211169
element_size: element_size,
11221170
count: count
11231171
}
@@ -1127,6 +1175,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11271175
Array {
11281176
sized: false,
11291177
align: element.align(dl),
1178+
primitive_align: element.primitive_align(dl),
11301179
element_size: element.size(dl),
11311180
count: 0
11321181
}
@@ -1135,6 +1184,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11351184
Array {
11361185
sized: false,
11371186
align: dl.i8_align,
1187+
primitive_align: dl.i8_align,
11381188
element_size: Size::from_bytes(1),
11391189
count: 0
11401190
}
@@ -1340,6 +1390,7 @@ impl<'a, 'gcx, 'tcx> Layout {
13401390
assert!(discr_max >= 0);
13411391
let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max);
13421392
let mut align = dl.aggregate_align;
1393+
let mut primitive_align = dl.aggregate_align;
13431394
let mut size = Size::from_bytes(0);
13441395

13451396
// We're interested in the smallest alignment, so start large.
@@ -1369,6 +1420,7 @@ impl<'a, 'gcx, 'tcx> Layout {
13691420
}
13701421
size = cmp::max(size, st.min_size);
13711422
align = align.max(st.align);
1423+
primitive_align = primitive_align.max(st.primitive_align);
13721424
Ok(st)
13731425
}).collect::<Result<Vec<_>, _>>()?;
13741426

@@ -1435,7 +1487,8 @@ impl<'a, 'gcx, 'tcx> Layout {
14351487
discr: ity,
14361488
variants: variants,
14371489
size: size,
1438-
align: align
1490+
align: align,
1491+
primitive_align: primitive_align
14391492
}
14401493
}
14411494

@@ -1557,6 +1610,30 @@ impl<'a, 'gcx, 'tcx> Layout {
15571610
}
15581611
}
15591612

1613+
/// Returns alignment before repr alignment is applied
1614+
pub fn primitive_align(&self, dl: &TargetDataLayout) -> Align {
1615+
match *self {
1616+
Array { primitive_align, .. } | General { primitive_align, .. } => primitive_align,
1617+
Univariant { ref variant, .. } |
1618+
StructWrappedNullablePointer { nonnull: ref variant, .. } => {
1619+
variant.primitive_align
1620+
},
1621+
1622+
_ => self.align(dl)
1623+
}
1624+
}
1625+
1626+
/// Returns repr alignment if it is greater than the primitive alignment.
1627+
pub fn over_align(&self, dl: &TargetDataLayout) -> Option<u32> {
1628+
let align = self.align(dl);
1629+
let primitive_align = self.primitive_align(dl);
1630+
if align.abi() > primitive_align.abi() {
1631+
Some(align.abi() as u32)
1632+
} else {
1633+
None
1634+
}
1635+
}
1636+
15601637
pub fn field_offset<C: HasDataLayout>(&self,
15611638
cx: C,
15621639
i: usize,

src/librustc/ty/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use serialize::{self, Encodable, Encoder};
3737
use std::borrow::Cow;
3838
use std::cell::{Cell, RefCell, Ref};
3939
use std::collections::BTreeMap;
40+
use std::cmp;
4041
use std::hash::{Hash, Hasher};
4142
use std::ops::Deref;
4243
use std::rc::Rc;
@@ -1470,10 +1471,12 @@ impl_stable_hash_for!(struct ReprFlags {
14701471
#[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)]
14711472
pub struct ReprOptions {
14721473
pub int: Option<attr::IntType>,
1474+
pub align: u16,
14731475
pub flags: ReprFlags,
14741476
}
14751477

14761478
impl_stable_hash_for!(struct ReprOptions {
1479+
align,
14771480
int,
14781481
flags
14791482
});
@@ -1482,7 +1485,7 @@ impl ReprOptions {
14821485
pub fn new(tcx: TyCtxt, did: DefId) -> ReprOptions {
14831486
let mut flags = ReprFlags::empty();
14841487
let mut size = None;
1485-
1488+
let mut max_align = 0;
14861489
for attr in tcx.get_attrs(did).iter() {
14871490
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
14881491
flags.insert(match r {
@@ -1493,6 +1496,10 @@ impl ReprOptions {
14931496
size = Some(i);
14941497
ReprFlags::empty()
14951498
},
1499+
attr::ReprAlign(align) => {
1500+
max_align = cmp::max(align, max_align);
1501+
ReprFlags::empty()
1502+
},
14961503
});
14971504
}
14981505
}
@@ -1506,7 +1513,7 @@ impl ReprOptions {
15061513
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
15071514
flags.insert(ReprFlags::IS_LINEAR);
15081515
}
1509-
ReprOptions { int: size, flags: flags }
1516+
ReprOptions { int: size, align: max_align, flags: flags }
15101517
}
15111518

15121519
#[inline]

src/librustc_trans/abi.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
553553
// bitcasting to the struct type yields invalid cast errors.
554554

555555
// We instead thus allocate some scratch space...
556-
let llscratch = bcx.alloca(ty, "abi_cast");
556+
let llscratch = bcx.alloca(ty, "abi_cast", None);
557557
base::Lifetime::Start.call(bcx, llscratch);
558558

559559
// ...where we first store the value...

0 commit comments

Comments
 (0)