Skip to content

Commit 9966397

Browse files
authored
Auto merge of #36151 - camlorn:struct_layout_optimization, r=eddyb
refactor to remove trans::adt and make rustc::ty::layout authoritative I asked on IRC about optimizing struct layout by reordering fields from most-aligned to least-aligned and somehow ended up getting talked into doing this. The goal here is to make `layout` authoritative and to remove `adt`. The former has been accomplished by reimplementing `represent_type_uncached` and the latter is in progress. @eddyb thought I should make the PR now. My plan is to reserve the actual optimization for a second PR, as this work is useful by itself.
2 parents 95abee1 + 467454b commit 9966397

21 files changed

+539
-1007
lines changed

Diff for: src/librustc/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,9 @@ pub struct GlobalCtxt<'tcx> {
489489
/// Cache for layouts computed from types.
490490
pub layout_cache: RefCell<FnvHashMap<Ty<'tcx>, &'tcx Layout>>,
491491

492+
/// Used to prevent layout from recursing too deeply.
493+
pub layout_depth: Cell<usize>,
494+
492495
/// Map from function to the `#[derive]` mode that it's defining. Only used
493496
/// by `rustc-macro` crates.
494497
pub derive_macros: RefCell<NodeMap<token::InternedString>>,
@@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
760763
crate_name: token::intern_and_get_ident(crate_name),
761764
data_layout: data_layout,
762765
layout_cache: RefCell::new(FnvHashMap()),
766+
layout_depth: Cell::new(0),
763767
derive_macros: RefCell::new(NodeMap()),
764768
}, f)
765769
}

Diff for: src/librustc/ty/layout.rs

+61-17
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,42 @@ pub enum Integer {
328328
}
329329

330330
impl Integer {
331+
pub fn size(&self) -> Size {
332+
match *self {
333+
I1 => Size::from_bits(1),
334+
I8 => Size::from_bytes(1),
335+
I16 => Size::from_bytes(2),
336+
I32 => Size::from_bytes(4),
337+
I64 => Size::from_bytes(8),
338+
}
339+
}
340+
341+
pub fn align(&self, dl: &TargetDataLayout)-> Align {
342+
match *self {
343+
I1 => dl.i1_align,
344+
I8 => dl.i8_align,
345+
I16 => dl.i16_align,
346+
I32 => dl.i32_align,
347+
I64 => dl.i64_align,
348+
}
349+
}
350+
351+
pub fn to_ty<'a, 'tcx>(&self, tcx: &ty::TyCtxt<'a, 'tcx, 'tcx>,
352+
signed: bool) -> Ty<'tcx> {
353+
match (*self, signed) {
354+
(I1, false) => tcx.types.u8,
355+
(I8, false) => tcx.types.u8,
356+
(I16, false) => tcx.types.u16,
357+
(I32, false) => tcx.types.u32,
358+
(I64, false) => tcx.types.u64,
359+
(I1, true) => tcx.types.i8,
360+
(I8, true) => tcx.types.i8,
361+
(I16, true) => tcx.types.i16,
362+
(I32, true) => tcx.types.i32,
363+
(I64, true) => tcx.types.i64,
364+
}
365+
}
366+
331367
/// Find the smallest Integer type which can represent the signed value.
332368
pub fn fit_signed(x: i64) -> Integer {
333369
match x {
@@ -350,6 +386,18 @@ impl Integer {
350386
}
351387
}
352388

389+
/// Find the smallest integer with the given alignment.
390+
pub fn for_abi_align(dl: &TargetDataLayout, align: Align) -> Option<Integer> {
391+
let wanted = align.abi();
392+
for &candidate in &[I8, I16, I32, I64] {
393+
let ty = Int(candidate);
394+
if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
395+
return Some(candidate);
396+
}
397+
}
398+
None
399+
}
400+
353401
/// Get the Integer type from an attr::IntType.
354402
pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer {
355403
match ity {
@@ -623,6 +671,15 @@ impl<'a, 'gcx, 'tcx> Struct {
623671
}
624672
Ok(None)
625673
}
674+
675+
pub fn offset_of_field(&self, index: usize) -> Size {
676+
assert!(index < self.offset_after_field.len());
677+
if index == 0 {
678+
Size::from_bytes(0)
679+
} else {
680+
self.offset_after_field[index-1]
681+
}
682+
}
626683
}
627684

628685
/// An untagged union.
@@ -912,7 +969,7 @@ impl<'a, 'gcx, 'tcx> Layout {
912969
Univariant { variant: unit, non_zero: false }
913970
}
914971

915-
// Tuples.
972+
// Tuples and closures.
916973
ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
917974
ty::TyTuple(tys) => {
918975
let mut st = Struct::new(dl, false);
@@ -972,10 +1029,10 @@ impl<'a, 'gcx, 'tcx> Layout {
9721029
});
9731030
}
9741031

975-
if def.variants.len() == 1 {
1032+
if !def.is_enum() || def.variants.len() == 1 && hint == attr::ReprAny {
9761033
// Struct, or union, or univariant enum equivalent to a struct.
9771034
// (Typechecking will reject discriminant-sizing attrs.)
978-
assert!(!def.is_enum() || hint == attr::ReprAny);
1035+
9791036
let fields = def.variants[0].fields.iter().map(|field| {
9801037
field.ty(tcx, substs).layout(infcx)
9811038
});
@@ -1103,20 +1160,7 @@ impl<'a, 'gcx, 'tcx> Layout {
11031160
// won't be so conservative.
11041161

11051162
// Use the initial field alignment
1106-
let wanted = start_align.abi();
1107-
let mut ity = min_ity;
1108-
for &candidate in &[I16, I32, I64] {
1109-
let ty = Int(candidate);
1110-
if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
1111-
ity = candidate;
1112-
break;
1113-
}
1114-
}
1115-
1116-
// FIXME(eddyb) conservative only to avoid diverging from trans::adt.
1117-
if align.abi() != start_align.abi() {
1118-
ity = min_ity;
1119-
}
1163+
let mut ity = Integer::for_abi_align(dl, start_align).unwrap_or(min_ity);
11201164

11211165
// If the alignment is not larger than the chosen discriminant size,
11221166
// don't use the alignment as the final size.

Diff for: src/librustc/ty/util.rs

+9
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,19 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
608608
}
609609
}
610610

611+
let rec_limit = tcx.sess.recursion_limit.get();
612+
let depth = tcx.layout_depth.get();
613+
if depth > rec_limit {
614+
tcx.sess.fatal(
615+
&format!("overflow representing the type `{}`", self));
616+
}
617+
618+
tcx.layout_depth.set(depth+1);
611619
let layout = Layout::compute_uncached(self, infcx)?;
612620
if can_cache {
613621
tcx.layout_cache.borrow_mut().insert(self, layout);
614622
}
623+
tcx.layout_depth.set(depth);
615624
Ok(layout)
616625
}
617626

Diff for: src/librustc_trans/abi.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use cabi_s390x;
2424
use cabi_mips;
2525
use cabi_mips64;
2626
use cabi_asmjs;
27-
use machine::{llalign_of_min, llsize_of, llsize_of_real, llsize_of_store};
27+
use machine::{llalign_of_min, llsize_of, llsize_of_alloc};
2828
use type_::Type;
2929
use type_of;
3030

@@ -102,7 +102,7 @@ impl ArgType {
102102
// Wipe old attributes, likely not valid through indirection.
103103
self.attrs = llvm::Attributes::default();
104104

105-
let llarg_sz = llsize_of_real(ccx, self.ty);
105+
let llarg_sz = llsize_of_alloc(ccx, self.ty);
106106

107107
// For non-immediate arguments the callee gets its own copy of
108108
// the value on the stack, so there are no aliases. It's also
@@ -200,7 +200,7 @@ impl ArgType {
200200
base::call_memcpy(bcx,
201201
bcx.pointercast(dst, Type::i8p(ccx)),
202202
bcx.pointercast(llscratch, Type::i8p(ccx)),
203-
C_uint(ccx, llsize_of_store(ccx, self.ty)),
203+
C_uint(ccx, llsize_of_alloc(ccx, self.ty)),
204204
cmp::min(llalign_of_min(ccx, self.ty),
205205
llalign_of_min(ccx, ty)) as u32);
206206

@@ -327,7 +327,7 @@ impl FnType {
327327
if let Layout::CEnum { signed, .. } = *ccx.layout_of(ty) {
328328
arg.signedness = Some(signed);
329329
}
330-
if llsize_of_real(ccx, arg.ty) == 0 {
330+
if llsize_of_alloc(ccx, arg.ty) == 0 {
331331
// For some forsaken reason, x86_64-pc-windows-gnu
332332
// doesn't ignore zero-sized struct arguments.
333333
// The same is true for s390x-unknown-linux-gnu.
@@ -358,7 +358,7 @@ impl FnType {
358358
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
359359
ty::TyBox(ty) => {
360360
let llty = type_of::sizing_type_of(ccx, ty);
361-
let llsz = llsize_of_real(ccx, llty);
361+
let llsz = llsize_of_alloc(ccx, llty);
362362
ret.attrs.set_dereferenceable(llsz);
363363
}
364364
_ => {}
@@ -427,7 +427,7 @@ impl FnType {
427427
} else {
428428
if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
429429
let llty = type_of::sizing_type_of(ccx, inner);
430-
let llsz = llsize_of_real(ccx, llty);
430+
let llsz = llsize_of_alloc(ccx, llty);
431431
arg.attrs.set_dereferenceable(llsz);
432432
}
433433
args.push(arg);
@@ -469,8 +469,8 @@ impl FnType {
469469
return;
470470
}
471471

472-
let size = llsize_of_real(ccx, llty);
473-
if size > llsize_of_real(ccx, ccx.int_type()) {
472+
let size = llsize_of_alloc(ccx, llty);
473+
if size > llsize_of_alloc(ccx, ccx.int_type()) {
474474
arg.make_indirect(ccx);
475475
} else if size > 0 {
476476
// We want to pass small aggregates as immediates, but using

0 commit comments

Comments
 (0)