Skip to content

Commit 1dc572b

Browse files
committed
rustc: represent the discriminant as a field for Layout::{Raw,StructWrapped}NullablePointer.
1 parent 3071060 commit 1dc572b

File tree

4 files changed

+112
-81
lines changed

4 files changed

+112
-81
lines changed

src/librustc/ty/layout.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub use self::Integer::*;
1212
pub use self::Layout::*;
1313
pub use self::Primitive::*;
1414

15+
use rustc_back::slice::ref_slice;
1516
use session::{self, DataTypeKind, Session};
1617
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
1718

@@ -582,7 +583,7 @@ pub enum Primitive {
582583
Pointer
583584
}
584585

585-
impl Primitive {
586+
impl<'a, 'tcx> Primitive {
586587
pub fn size<C: HasDataLayout>(self, cx: C) -> Size {
587588
let dl = cx.data_layout();
588589

@@ -611,6 +612,15 @@ impl Primitive {
611612
Pointer => dl.pointer_align
612613
}
613614
}
615+
616+
pub fn to_ty(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
617+
match *self {
618+
Int(i) => i.to_ty(tcx, false),
619+
F32 => tcx.types.f32,
620+
F64 => tcx.types.f64,
621+
Pointer => tcx.mk_mut_ptr(tcx.mk_nil()),
622+
}
623+
}
614624
}
615625

616626
/// A structure, a product type in ADT terms.
@@ -1202,9 +1212,7 @@ impl<'a, 'tcx> Layout {
12021212
let layout = tcx.intern_layout(layout);
12031213
let fields = match *layout {
12041214
Scalar { .. } |
1205-
CEnum { .. } |
1206-
RawNullablePointer { .. } |
1207-
StructWrappedNullablePointer { .. } => {
1215+
CEnum { .. } => {
12081216
FieldPlacement::union(0)
12091217
}
12101218

@@ -1241,7 +1249,14 @@ impl<'a, 'tcx> Layout {
12411249
FieldPlacement::union(def.struct_variant().fields.len())
12421250
}
12431251

1244-
General { .. } => FieldPlacement::union(1)
1252+
General { .. } |
1253+
RawNullablePointer { .. } => FieldPlacement::union(1),
1254+
1255+
StructWrappedNullablePointer { ref discr_offset, .. } => {
1256+
FieldPlacement::Arbitrary {
1257+
offsets: ref_slice(discr_offset)
1258+
}
1259+
}
12451260
};
12461261
Ok(CachedLayout {
12471262
layout,
@@ -1520,7 +1535,7 @@ impl<'a, 'tcx> Layout {
15201535
if let Some((discr, offset, primitive)) = choice {
15211536
// HACK(eddyb) work around not being able to move
15221537
// out of arrays with just the indexing operator.
1523-
let st = if discr == 0 { st0 } else { st1 };
1538+
let mut st = if discr == 0 { st0 } else { st1 };
15241539

15251540
// FIXME(eddyb) should take advantage of a newtype.
15261541
if offset.bytes() == 0 && primitive.size(dl) == st.stride() &&
@@ -1531,6 +1546,14 @@ impl<'a, 'tcx> Layout {
15311546
});
15321547
}
15331548

1549+
let mut discr_align = primitive.align(dl);
1550+
if offset.abi_align(discr_align) != offset {
1551+
st.packed = true;
1552+
discr_align = dl.i8_align;
1553+
}
1554+
st.align = st.align.max(discr_align);
1555+
st.primitive_align = st.primitive_align.max(discr_align);
1556+
15341557
return success(StructWrappedNullablePointer {
15351558
nndiscr: discr as u64,
15361559
nonnull: st,
@@ -2292,7 +2315,7 @@ impl<'a, 'tcx> FullLayout<'tcx> {
22922315
match tcx.struct_tail(pointee).sty {
22932316
ty::TySlice(element) => slice(element),
22942317
ty::TyStr => slice(tcx.types.u8),
2295-
ty::TyDynamic(..) => tcx.mk_mut_ptr(tcx.mk_nil()),
2318+
ty::TyDynamic(..) => Pointer.to_ty(tcx),
22962319
_ => bug!("FullLayout::field_type({:?}): not applicable", self)
22972320
}
22982321
};
@@ -2350,6 +2373,10 @@ impl<'a, 'tcx> FullLayout<'tcx> {
23502373
General { discr, .. } => {
23512374
return [discr.to_ty(tcx, false)][i];
23522375
}
2376+
RawNullablePointer { discr, .. } |
2377+
StructWrappedNullablePointer { discr, .. } => {
2378+
return [discr.to_ty(tcx)][i];
2379+
}
23532380
_ if def.variants.len() > 1 => return [][i],
23542381

23552382
// Enums with one variant behave like structs.

src/librustc_trans/mir/lvalue.rs

Lines changed: 53 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use llvm::{self, ValueRef};
1212
use rustc::ty::{self, Ty, TypeFoldable};
13-
use rustc::ty::layout::{self, Align, Layout, LayoutOf, Size};
13+
use rustc::ty::layout::{self, Align, Layout, LayoutOf};
1414
use rustc::mir;
1515
use rustc::mir::tcx::LvalueTy;
1616
use rustc_data_structures::indexed_vec::Idx;
@@ -205,50 +205,68 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
205205
l = l.for_variant(variant_index)
206206
}
207207
}
208-
let fty = l.field(ccx, ix).ty;
208+
let field = l.field(ccx, ix);
209+
let offset = l.fields.offset(ix).bytes();
209210

210211
let alignment = self.alignment | Alignment::from(&*l);
211212

212213
// Handle all the non-aggregate cases first.
213214
match *l {
214215
layout::UntaggedUnion { .. } => {
215-
let ty = ccx.llvm_type_of(fty);
216+
let ty = ccx.llvm_type_of(field.ty);
216217
return LvalueRef::new_sized(
217-
bcx.pointercast(self.llval, ty.ptr_to()), fty, alignment);
218+
bcx.pointercast(self.llval, ty.ptr_to()), field.ty, alignment);
218219
}
219-
layout::General { .. } if l.variant_index.is_none() => {
220-
let ty = ccx.llvm_type_of(fty);
220+
// Discriminant field of enums.
221+
layout::General { .. } |
222+
layout::RawNullablePointer { .. } |
223+
layout::StructWrappedNullablePointer { .. } if l.variant_index.is_none() => {
224+
let ty = ccx.llvm_type_of(field.ty);
225+
let size = field.size(ccx).bytes();
226+
227+
// If the discriminant is not on a multiple of the primitive's size,
228+
// we need to go through i8*. Also assume the worst alignment.
229+
if offset % size != 0 {
230+
let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
231+
let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
232+
let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
233+
return LvalueRef::new_sized(
234+
bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
235+
}
236+
237+
let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
221238
return LvalueRef::new_sized(
222-
bcx.pointercast(self.llval, ty.ptr_to()), fty, alignment);
239+
bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
240+
field.ty, alignment);
223241
}
224242
layout::RawNullablePointer { nndiscr, .. } |
225243
layout::StructWrappedNullablePointer { nndiscr, .. }
226244
if l.variant_index.unwrap() as u64 != nndiscr => {
227245
// The unit-like case might have a nonzero number of unit-like fields.
228246
// (e.d., Result of Either with (), as one side.)
229-
let ty = ccx.llvm_type_of(fty);
230-
assert_eq!(ccx.size_of(fty).bytes(), 0);
247+
let ty = ccx.llvm_type_of(field.ty);
248+
assert_eq!(field.size(ccx).bytes(), 0);
231249
return LvalueRef::new_sized(
232-
bcx.pointercast(self.llval, ty.ptr_to()), fty,
250+
bcx.pointercast(self.llval, ty.ptr_to()), field.ty,
233251
Alignment::Packed(Align::from_bytes(1, 1).unwrap()));
234252
}
235253
layout::RawNullablePointer { .. } => {
236-
let ty = ccx.llvm_type_of(fty);
254+
let ty = ccx.llvm_type_of(field.ty);
237255
return LvalueRef::new_sized(
238-
bcx.pointercast(self.llval, ty.ptr_to()), fty, alignment);
256+
bcx.pointercast(self.llval, ty.ptr_to()), field.ty, alignment);
239257
}
240258
_ => {}
241259
}
242260

243261
let simple = || {
244262
LvalueRef {
245263
llval: bcx.struct_gep(self.llval, l.llvm_field_index(ix)),
246-
llextra: if !ccx.shared().type_has_metadata(fty) {
247-
ptr::null_mut()
248-
} else {
264+
llextra: if ccx.shared().type_has_metadata(field.ty) {
249265
self.llextra
266+
} else {
267+
ptr::null_mut()
250268
},
251-
ty: LvalueTy::from_ty(fty),
269+
ty: LvalueTy::from_ty(field.ty),
252270
alignment,
253271
}
254272
};
@@ -264,13 +282,13 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
264282
// Simple case - we can just GEP the field
265283
// * Packed struct - There is no alignment padding
266284
// * Field is sized - pointer is properly aligned already
267-
if is_packed || ccx.shared().type_is_sized(fty) {
285+
if is_packed || !field.is_unsized() {
268286
return simple();
269287
}
270288

271289
// If the type of the last field is [T], str or a foreign type, then we don't need to do
272290
// any adjusments
273-
match fty.sty {
291+
match field.ty.sty {
274292
ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(),
275293
_ => ()
276294
}
@@ -299,12 +317,10 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
299317

300318
let meta = self.llextra;
301319

302-
303-
let offset = l.fields.offset(ix).bytes();
304320
let unaligned_offset = C_usize(ccx, offset);
305321

306322
// Get the alignment of the field
307-
let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);
323+
let (_, align) = glue::size_and_align_of_dst(bcx, field.ty, meta);
308324

309325
// Bump the unaligned offset up to the appropriate alignment using the
310326
// following expression:
@@ -323,39 +339,17 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
323339
let byte_ptr = bcx.gep(byte_ptr, &[offset]);
324340

325341
// Finally, cast back to the type expected
326-
let ll_fty = ccx.llvm_type_of(fty);
342+
let ll_fty = ccx.llvm_type_of(field.ty);
327343
debug!("struct_field_ptr: Field type is {:?}", ll_fty);
328344

329345
LvalueRef {
330346
llval: bcx.pointercast(byte_ptr, ll_fty.ptr_to()),
331347
llextra: self.llextra,
332-
ty: LvalueTy::from_ty(fty),
348+
ty: LvalueTy::from_ty(field.ty),
333349
alignment,
334350
}
335351
}
336352

337-
// Return a pointer to the discriminant, given its type and offset.
338-
fn gepi_discr_at_offset(self, bcx: &Builder,
339-
discr: ty::layout::Primitive,
340-
offset: Size)
341-
-> (ValueRef, Alignment) {
342-
let size = discr.size(bcx.ccx);
343-
let ptr_ty = Type::from_primitive(bcx.ccx, discr).ptr_to();
344-
345-
// If the discriminant is not on a multiple of the primitive's size,
346-
// we need to go through i8*. Also assume the worst alignment.
347-
if offset.bytes() % size.bytes() != 0 {
348-
let byte_ptr = bcx.pointercast(self.llval, Type::i8p(bcx.ccx));
349-
let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(bcx.ccx, offset.bytes())]);
350-
let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
351-
return (bcx.pointercast(byte_ptr, ptr_ty), byte_align);
352-
}
353-
354-
let discr_ptr = bcx.pointercast(self.llval, ptr_ty);
355-
(bcx.inbounds_gep(discr_ptr, &[C_usize(bcx.ccx, offset.bytes() / size.bytes())]),
356-
self.alignment)
357-
}
358-
359353
/// Helper for cases where the discriminant is simply loaded.
360354
fn load_discr(self, bcx: &Builder, ity: layout::Integer, ptr: ValueRef,
361355
min: u64, max: u64) -> ValueRef {
@@ -394,16 +388,12 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
394388
self.load_discr(bcx, discr, ptr.llval, 0, variants.len() as u64 - 1)
395389
}
396390
layout::Univariant { .. } | layout::UntaggedUnion { .. } => C_u8(bcx.ccx, 0),
397-
layout::RawNullablePointer { nndiscr, discr } |
398-
layout::StructWrappedNullablePointer { nndiscr, discr, .. } => {
399-
let discr_offset = match *l {
400-
layout::StructWrappedNullablePointer { discr_offset, .. } => discr_offset,
401-
_ => Size::from_bytes(0),
402-
};
403-
let (lldiscrptr, alignment) = self.gepi_discr_at_offset(bcx, discr, discr_offset);
404-
let lldiscr = bcx.load(lldiscrptr, alignment.non_abi());
391+
layout::RawNullablePointer { nndiscr, .. } |
392+
layout::StructWrappedNullablePointer { nndiscr, .. } => {
393+
let ptr = self.project_field(bcx, 0);
394+
let lldiscr = bcx.load(ptr.llval, ptr.alignment.non_abi());
405395
let cmp = if nndiscr == 0 { llvm::IntEQ } else { llvm::IntNE };
406-
bcx.icmp(cmp, lldiscr, C_null(Type::from_primitive(bcx.ccx, discr)))
396+
bcx.icmp(cmp, lldiscr, C_null(bcx.ccx.llvm_type_of(ptr.ty.to_ty(bcx.tcx()))))
407397
},
408398
_ => bug!("{} is not an enum", l.ty)
409399
};
@@ -434,14 +424,14 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
434424
| layout::Vector { .. } => {
435425
assert_eq!(to, 0);
436426
}
437-
layout::RawNullablePointer { nndiscr, discr, .. } |
438-
layout::StructWrappedNullablePointer { nndiscr, discr, .. } => {
427+
layout::RawNullablePointer { nndiscr, .. } |
428+
layout::StructWrappedNullablePointer { nndiscr, .. } => {
439429
if to != nndiscr {
440-
let (use_memset, discr_offset) = match *l {
441-
layout::StructWrappedNullablePointer { discr_offset, .. } => {
442-
(target_sets_discr_via_memset(bcx), discr_offset)
430+
let use_memset = match *l {
431+
layout::StructWrappedNullablePointer { .. } => {
432+
target_sets_discr_via_memset(bcx)
443433
}
444-
_ => (false, Size::from_bytes(0)),
434+
_ => false,
445435
};
446436
if use_memset {
447437
// Issue #34427: As workaround for LLVM bug on
@@ -454,10 +444,9 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
454444
let align = C_u32(bcx.ccx, align.abi() as u32);
455445
base::call_memset(bcx, llptr, fill_byte, size, align, false);
456446
} else {
457-
let (lldiscrptr, alignment) =
458-
self.gepi_discr_at_offset(bcx, discr, discr_offset);
459-
bcx.store(C_null(Type::from_primitive(bcx.ccx, discr)),
460-
lldiscrptr, alignment.non_abi());
447+
let ptr = self.project_field(bcx, 0);
448+
bcx.store(C_null(bcx.ccx.llvm_type_of(ptr.ty.to_ty(bcx.tcx()))),
449+
ptr.llval, ptr.alignment.non_abi());
461450
}
462451
}
463452
}

src/librustc_trans/type_.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,4 @@ impl Type {
287287
I128 => Type::i128(cx),
288288
}
289289
}
290-
291-
pub fn from_primitive(cx: &CrateContext, p: layout::Primitive) -> Type {
292-
use rustc::ty::layout::Primitive::*;
293-
match p {
294-
Int(i) => Type::from_integer(cx, i),
295-
F32 => Type::f32(cx),
296-
F64 => Type::f64(cx),
297-
Pointer => Type::i8p(cx),
298-
}
299-
}
300290
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[repr(packed)]
12+
#[derive(Copy, Clone)]
13+
struct Packed<T>(T);
14+
15+
fn main() {
16+
let one = (Some(Packed((&(), 0))), true);
17+
let two = [one, one];
18+
let stride = (&two[1] as *const _ as usize) - (&two[0] as *const _ as usize);
19+
20+
// This can fail if rustc and LLVM disagree on the size of a type.
21+
// In this case, `Option<Packed<(&(), u32)>>` was erronously not
22+
// marked as packed despite needing alignment `1` and containing
23+
// its `&()` discriminant, which has alignment larger than `1`.
24+
assert_eq!(stride, std::mem::size_of_val(&one));
25+
}

0 commit comments

Comments
 (0)