Skip to content

Commit 4005d43

Browse files
committed
Auto merge of #30245 - Aatch:dynamic-align-dst, r=pnkfelix
Fixes #26403 This adjusts the pointer, if needed, to the correct alignment by using the alignment information in the vtable. Handling zero might not be necessary, as it shouldn't actually occur. I've left it as it's own commit so it can be removed fairly easily if people don't think it's worth doing. The way it's handled though means that there shouldn't be much impact on performance.
2 parents 56a1f51 + d6eb063 commit 4005d43

File tree

11 files changed

+365
-63
lines changed

11 files changed

+365
-63
lines changed

Diff for: src/librustc_trans/trans/_match.rs

+57-15
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,10 @@ fn extract_variant_args<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
708708
val: MatchInput)
709709
-> ExtractedBlock<'blk, 'tcx> {
710710
let _icx = push_ctxt("match::extract_variant_args");
711+
// Assume enums are always sized for now.
712+
let val = adt::MaybeSizedValue::sized(val.val);
711713
let args = (0..adt::num_args(repr, disr_val)).map(|i| {
712-
adt::trans_field_ptr(bcx, repr, val.val, disr_val, i)
714+
adt::trans_field_ptr(bcx, repr, val, disr_val, i)
713715
}).collect();
714716

715717
ExtractedBlock { vals: args, bcx: bcx }
@@ -1198,7 +1200,8 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
11981200
(arg_count - 1, Load(bcx, expr::get_dataptr(bcx, val.val)))
11991201
};
12001202
let mut field_vals: Vec<ValueRef> = (0..arg_count).map(|ix|
1201-
adt::trans_field_ptr(bcx, &*repr, struct_val, 0, ix)
1203+
// By definition, these are all sized
1204+
adt::trans_field_ptr(bcx, &*repr, adt::MaybeSizedValue::sized(struct_val), 0, ix)
12021205
).collect();
12031206

12041207
match left_ty.sty {
@@ -1210,10 +1213,13 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
12101213
monomorphize::field_ty(bcx.tcx(), substs, field)
12111214
}).unwrap();
12121215
let scratch = alloc_ty(bcx, unsized_ty, "__struct_field_fat_ptr");
1216+
1217+
let meta = Load(bcx, expr::get_meta(bcx, val.val));
1218+
let struct_val = adt::MaybeSizedValue::unsized_(struct_val, meta);
1219+
12131220
let data = adt::trans_field_ptr(bcx, &*repr, struct_val, 0, arg_count);
1214-
let len = Load(bcx, expr::get_meta(bcx, val.val));
12151221
Store(bcx, data, expr::get_dataptr(bcx, scratch));
1216-
Store(bcx, len, expr::get_meta(bcx, scratch));
1222+
Store(bcx, meta, expr::get_meta(bcx, scratch));
12171223
field_vals.push(scratch);
12181224
}
12191225
_ => {}
@@ -1784,9 +1790,10 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
17841790
val: MatchInput,
17851791
cleanup_scope: cleanup::ScopeId)
17861792
-> Block<'blk, 'tcx> {
1787-
debug!("bind_irrefutable_pat(bcx={}, pat={:?})",
1793+
debug!("bind_irrefutable_pat(bcx={}, pat={:?}, val={})",
17881794
bcx.to_str(),
1789-
pat);
1795+
pat,
1796+
bcx.val_to_string(val.val));
17901797

17911798
if bcx.sess().asm_comments() {
17921799
add_comment(bcx, &format!("bind_irrefutable_pat(pat={:?})",
@@ -1865,9 +1872,10 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
18651872
Some(ref elems) => {
18661873
// This is the tuple struct case.
18671874
let repr = adt::represent_node(bcx, pat.id);
1875+
let val = adt::MaybeSizedValue::sized(val.val);
18681876
for (i, elem) in elems.iter().enumerate() {
18691877
let fldptr = adt::trans_field_ptr(bcx, &*repr,
1870-
val.val, 0, i);
1878+
val, 0, i);
18711879
bcx = bind_irrefutable_pat(
18721880
bcx,
18731881
&**elem,
@@ -1887,14 +1895,35 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
18871895
let pat_ty = node_id_type(bcx, pat.id);
18881896
let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
18891897
let pat_v = VariantInfo::of_node(tcx, pat_ty, pat.id);
1898+
1899+
let val = if type_is_sized(tcx, pat_ty) {
1900+
adt::MaybeSizedValue::sized(val.val)
1901+
} else {
1902+
let data = Load(bcx, expr::get_dataptr(bcx, val.val));
1903+
let meta = Load(bcx, expr::get_meta(bcx, val.val));
1904+
adt::MaybeSizedValue::unsized_(data, meta)
1905+
};
1906+
18901907
for f in fields {
18911908
let name = f.node.name;
1892-
let fldptr = adt::trans_field_ptr(
1909+
let field_idx = pat_v.field_index(name);
1910+
let mut fldptr = adt::trans_field_ptr(
18931911
bcx,
18941912
&*pat_repr,
1895-
val.val,
1913+
val,
18961914
pat_v.discr,
1897-
pat_v.field_index(name));
1915+
field_idx);
1916+
1917+
let fty = pat_v.fields[field_idx].1;
1918+
// If it's not sized, then construct a fat pointer instead of
1919+
// a regular one
1920+
if !type_is_sized(tcx, fty) {
1921+
let scratch = alloc_ty(bcx, fty, "__struct_field_fat_ptr");
1922+
debug!("Creating fat pointer {}", bcx.val_to_string(scratch));
1923+
Store(bcx, fldptr, expr::get_dataptr(bcx, scratch));
1924+
Store(bcx, val.meta, expr::get_meta(bcx, scratch));
1925+
fldptr = scratch;
1926+
}
18981927
bcx = bind_irrefutable_pat(bcx,
18991928
&*f.node.pat,
19001929
MatchInput::from_val(fldptr),
@@ -1903,8 +1932,9 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
19031932
}
19041933
hir::PatTup(ref elems) => {
19051934
let repr = adt::represent_node(bcx, pat.id);
1935+
let val = adt::MaybeSizedValue::sized(val.val);
19061936
for (i, elem) in elems.iter().enumerate() {
1907-
let fldptr = adt::trans_field_ptr(bcx, &*repr, val.val, 0, i);
1937+
let fldptr = adt::trans_field_ptr(bcx, &*repr, val, 0, i);
19081938
bcx = bind_irrefutable_pat(
19091939
bcx,
19101940
&**elem,
@@ -1913,16 +1943,28 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
19131943
}
19141944
}
19151945
hir::PatBox(ref inner) => {
1916-
let llbox = Load(bcx, val.val);
1946+
let pat_ty = node_id_type(bcx, inner.id);
1947+
// Don't load DSTs, instead pass along a fat ptr
1948+
let val = if type_is_sized(tcx, pat_ty) {
1949+
Load(bcx, val.val)
1950+
} else {
1951+
val.val
1952+
};
19171953
bcx = bind_irrefutable_pat(
1918-
bcx, &**inner, MatchInput::from_val(llbox), cleanup_scope);
1954+
bcx, &**inner, MatchInput::from_val(val), cleanup_scope);
19191955
}
19201956
hir::PatRegion(ref inner, _) => {
1921-
let loaded_val = Load(bcx, val.val);
1957+
let pat_ty = node_id_type(bcx, inner.id);
1958+
// Don't load DSTs, instead pass along a fat ptr
1959+
let val = if type_is_sized(tcx, pat_ty) {
1960+
Load(bcx, val.val)
1961+
} else {
1962+
val.val
1963+
};
19221964
bcx = bind_irrefutable_pat(
19231965
bcx,
19241966
&**inner,
1925-
MatchInput::from_val(loaded_val),
1967+
MatchInput::from_val(val),
19261968
cleanup_scope);
19271969
}
19281970
hir::PatVec(ref before, ref slice, ref after) => {

Diff for: src/librustc_trans/trans/adt.rs

+128-13
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
4444
pub use self::Repr::*;
4545

46+
use std;
4647
use std::rc::Rc;
4748

4849
use llvm::{ValueRef, True, IntEQ, IntNE};
@@ -60,6 +61,7 @@ use trans::cleanup::CleanupMethods;
6061
use trans::common::*;
6162
use trans::datum;
6263
use trans::debuginfo::DebugLoc;
64+
use trans::glue;
6365
use trans::machine;
6466
use trans::monomorphize;
6567
use trans::type_::Type;
@@ -153,6 +155,32 @@ pub struct Struct<'tcx> {
153155
pub fields: Vec<Ty<'tcx>>,
154156
}
155157

158+
#[derive(Copy, Clone)]
159+
pub struct MaybeSizedValue {
160+
pub value: ValueRef,
161+
pub meta: ValueRef,
162+
}
163+
164+
impl MaybeSizedValue {
165+
pub fn sized(value: ValueRef) -> MaybeSizedValue {
166+
MaybeSizedValue {
167+
value: value,
168+
meta: std::ptr::null_mut()
169+
}
170+
}
171+
172+
pub fn unsized_(value: ValueRef, meta: ValueRef) -> MaybeSizedValue {
173+
MaybeSizedValue {
174+
value: value,
175+
meta: meta
176+
}
177+
}
178+
179+
pub fn has_meta(&self) -> bool {
180+
!self.meta.is_null()
181+
}
182+
}
183+
156184
/// Convenience for `represent_type`. There should probably be more or
157185
/// these, for places in trans where the `Ty` isn't directly
158186
/// available.
@@ -247,7 +275,11 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
247275
monomorphize::field_ty(cx.tcx(), substs, field)
248276
}).collect::<Vec<_>>();
249277
let packed = cx.tcx().lookup_packed(def.did);
250-
let dtor = def.dtor_kind().has_drop_flag();
278+
// FIXME(16758) don't add a drop flag to unsized structs, as it
279+
// won't actually be in the location we say it is because it'll be after
280+
// the unsized field. Several other pieces of code assume that the unsized
281+
// field is definitely the last one.
282+
let dtor = def.dtor_kind().has_drop_flag() && type_is_sized(cx.tcx(), t);
251283
if dtor {
252284
ftys.push(cx.tcx().dtor_type());
253285
}
@@ -976,7 +1008,7 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
9761008
}
9771009
General(ity, ref cases, dtor) => {
9781010
if dtor_active(dtor) {
979-
let ptr = trans_field_ptr(bcx, r, val, discr,
1011+
let ptr = trans_field_ptr(bcx, r, MaybeSizedValue::sized(val), discr,
9801012
cases[discr as usize].fields.len() - 2);
9811013
Store(bcx, C_u8(bcx.ccx(), DTOR_NEEDED), ptr);
9821014
}
@@ -1037,7 +1069,7 @@ pub fn num_args(r: &Repr, discr: Disr) -> usize {
10371069

10381070
/// Access a field, at a point when the value's case is known.
10391071
pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
1040-
val: ValueRef, discr: Disr, ix: usize) -> ValueRef {
1072+
val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef {
10411073
// Note: if this ever needs to generate conditionals (e.g., if we
10421074
// decide to do some kind of cdr-coding-like non-unique repr
10431075
// someday), it will need to return a possibly-new bcx as well.
@@ -1060,13 +1092,13 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10601092
assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0);
10611093
// The contents of memory at this pointer can't matter, but use
10621094
// the value that's "reasonable" in case of pointer comparison.
1063-
PointerCast(bcx, val, ty.ptr_to())
1095+
PointerCast(bcx, val.value, ty.ptr_to())
10641096
}
10651097
RawNullablePointer { nndiscr, nnty, .. } => {
10661098
assert_eq!(ix, 0);
10671099
assert_eq!(discr, nndiscr);
10681100
let ty = type_of::type_of(bcx.ccx(), nnty);
1069-
PointerCast(bcx, val, ty.ptr_to())
1101+
PointerCast(bcx, val.value, ty.ptr_to())
10701102
}
10711103
StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
10721104
assert_eq!(discr, nndiscr);
@@ -1075,18 +1107,100 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10751107
}
10761108
}
10771109

1078-
pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: ValueRef,
1110+
pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: MaybeSizedValue,
10791111
ix: usize, needs_cast: bool) -> ValueRef {
1080-
let val = if needs_cast {
1081-
let ccx = bcx.ccx();
1082-
let fields = st.fields.iter().map(|&ty| type_of::type_of(ccx, ty)).collect::<Vec<_>>();
1112+
let ccx = bcx.ccx();
1113+
let ptr_val = if needs_cast {
1114+
let fields = st.fields.iter().map(|&ty| {
1115+
type_of::in_memory_type_of(ccx, ty)
1116+
}).collect::<Vec<_>>();
10831117
let real_ty = Type::struct_(ccx, &fields[..], st.packed);
1084-
PointerCast(bcx, val, real_ty.ptr_to())
1118+
PointerCast(bcx, val.value, real_ty.ptr_to())
10851119
} else {
1086-
val
1120+
val.value
10871121
};
10881122

1089-
StructGEP(bcx, val, ix)
1123+
let fty = st.fields[ix];
1124+
// Simple case - we can just GEP the field
1125+
// * First field - Always aligned properly
1126+
// * Packed struct - There is no alignment padding
1127+
// * Field is sized - pointer is properly aligned already
1128+
if ix == 0 || st.packed || type_is_sized(bcx.tcx(), fty) {
1129+
return StructGEP(bcx, ptr_val, ix);
1130+
}
1131+
1132+
// If the type of the last field is [T] or str, then we don't need to do
1133+
// any adjusments
1134+
match fty.sty {
1135+
ty::TySlice(..) | ty::TyStr => {
1136+
return StructGEP(bcx, ptr_val, ix);
1137+
}
1138+
_ => ()
1139+
}
1140+
1141+
// There's no metadata available, log the case and just do the GEP.
1142+
if !val.has_meta() {
1143+
debug!("Unsized field `{}`, of `{}` has no metadata for adjustment",
1144+
ix,
1145+
bcx.val_to_string(ptr_val));
1146+
return StructGEP(bcx, ptr_val, ix);
1147+
}
1148+
1149+
let dbloc = DebugLoc::None;
1150+
1151+
// We need to get the pointer manually now.
1152+
// We do this by casting to a *i8, then offsetting it by the appropriate amount.
1153+
// We do this instead of, say, simply adjusting the pointer from the result of a GEP
1154+
// because the field may have an arbitrary alignment in the LLVM representation
1155+
// anyway.
1156+
//
1157+
// To demonstrate:
1158+
// struct Foo<T: ?Sized> {
1159+
// x: u16,
1160+
// y: T
1161+
// }
1162+
//
1163+
// The type Foo<Foo<Trait>> is represented in LLVM as { u16, { u16, u8 }}, meaning that
1164+
// the `y` field has 16-bit alignment.
1165+
1166+
let meta = val.meta;
1167+
1168+
// Calculate the unaligned offset of the the unsized field.
1169+
let mut offset = 0;
1170+
for &ty in &st.fields[0..ix] {
1171+
let llty = type_of::sizing_type_of(ccx, ty);
1172+
let type_align = type_of::align_of(ccx, ty);
1173+
offset = roundup(offset, type_align);
1174+
offset += machine::llsize_of_alloc(ccx, llty);
1175+
}
1176+
let unaligned_offset = C_uint(bcx.ccx(), offset);
1177+
1178+
// Get the alignment of the field
1179+
let (_, align) = glue::size_and_align_of_dst(bcx, fty, meta);
1180+
1181+
// Bump the unaligned offset up to the appropriate alignment using the
1182+
// following expression:
1183+
//
1184+
// (unaligned offset + (align - 1)) & -align
1185+
1186+
// Calculate offset
1187+
let align_sub_1 = Sub(bcx, align, C_uint(bcx.ccx(), 1u64), dbloc);
1188+
let offset = And(bcx,
1189+
Add(bcx, unaligned_offset, align_sub_1, dbloc),
1190+
Neg(bcx, align, dbloc),
1191+
dbloc);
1192+
1193+
debug!("struct_field_ptr: DST field offset: {}",
1194+
bcx.val_to_string(offset));
1195+
1196+
// Cast and adjust pointer
1197+
let byte_ptr = PointerCast(bcx, ptr_val, Type::i8p(bcx.ccx()));
1198+
let byte_ptr = GEP(bcx, byte_ptr, &[offset]);
1199+
1200+
// Finally, cast back to the type expected
1201+
let ll_fty = type_of::in_memory_type_of(bcx.ccx(), fty);
1202+
debug!("struct_field_ptr: Field type is {}", ll_fty.to_string());
1203+
PointerCast(bcx, byte_ptr, ll_fty.ptr_to())
10901204
}
10911205

10921206
pub fn fold_variants<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
@@ -1168,7 +1282,8 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
11681282
cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx
11691283
));
11701284
bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
1171-
let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false);
1285+
let ptr = struct_field_ptr(variant_cx, st, MaybeSizedValue::sized(value),
1286+
(st.fields.len() - 1), false);
11721287
datum::Datum::new(ptr, ptr_ty, datum::Lvalue::new("adt::trans_drop_flag_ptr"))
11731288
.store_to(variant_cx, scratch.val)
11741289
});

0 commit comments

Comments
 (0)