Skip to content

Commit a2557d4

Browse files
committed
Align pointers to DST fields properly
DST fields, being of an unknown type, are not automatically aligned properly, so a pointer to the field needs to be aligned using the information in the vtable. Fixes rust-lang#26403 and a number of other DST-related bugs discovered while implementing this.
1 parent 7b77f67 commit a2557d4

File tree

11 files changed

+337
-61
lines changed

11 files changed

+337
-61
lines changed

src/librustc_trans/trans/_match.rs

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

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

12051208
match left_ty.sty {
@@ -1211,10 +1214,13 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
12111214
monomorphize::field_ty(bcx.tcx(), substs, field)
12121215
}).unwrap();
12131216
let scratch = alloc_ty(bcx, unsized_ty, "__struct_field_fat_ptr");
1217+
1218+
let meta = Load(bcx, expr::get_meta(bcx, val.val));
1219+
let struct_val = adt::MaybeSizedValue::unsized_(struct_val, meta);
1220+
12141221
let data = adt::trans_field_ptr(bcx, &*repr, struct_val, 0, arg_count);
1215-
let len = Load(bcx, expr::get_meta(bcx, val.val));
12161222
Store(bcx, data, expr::get_dataptr(bcx, scratch));
1217-
Store(bcx, len, expr::get_meta(bcx, scratch));
1223+
Store(bcx, meta, expr::get_meta(bcx, scratch));
12181224
field_vals.push(scratch);
12191225
}
12201226
_ => {}
@@ -1785,9 +1791,10 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
17851791
val: MatchInput,
17861792
cleanup_scope: cleanup::ScopeId)
17871793
-> Block<'blk, 'tcx> {
1788-
debug!("bind_irrefutable_pat(bcx={}, pat={:?})",
1794+
debug!("bind_irrefutable_pat(bcx={}, pat={:?}, val={})",
17891795
bcx.to_str(),
1790-
pat);
1796+
pat,
1797+
bcx.val_to_string(val.val));
17911798

17921799
if bcx.sess().asm_comments() {
17931800
add_comment(bcx, &format!("bind_irrefutable_pat(pat={:?})",
@@ -1866,9 +1873,10 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
18661873
Some(ref elems) => {
18671874
// This is the tuple struct case.
18681875
let repr = adt::represent_node(bcx, pat.id);
1876+
let val = adt::MaybeSizedValue::sized(val.val);
18691877
for (i, elem) in elems.iter().enumerate() {
18701878
let fldptr = adt::trans_field_ptr(bcx, &*repr,
1871-
val.val, 0, i);
1879+
val, 0, i);
18721880
bcx = bind_irrefutable_pat(
18731881
bcx,
18741882
&**elem,
@@ -1888,14 +1896,35 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
18881896
let pat_ty = node_id_type(bcx, pat.id);
18891897
let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
18901898
let pat_v = VariantInfo::of_node(tcx, pat_ty, pat.id);
1899+
1900+
let val = if type_is_sized(tcx, pat_ty) {
1901+
adt::MaybeSizedValue::sized(val.val)
1902+
} else {
1903+
let data = Load(bcx, expr::get_dataptr(bcx, val.val));
1904+
let meta = Load(bcx, expr::get_meta(bcx, val.val));
1905+
adt::MaybeSizedValue::unsized_(data, meta)
1906+
};
1907+
18911908
for f in fields {
18921909
let name = f.node.name;
1893-
let fldptr = adt::trans_field_ptr(
1910+
let field_idx = pat_v.field_index(name);
1911+
let mut fldptr = adt::trans_field_ptr(
18941912
bcx,
18951913
&*pat_repr,
1896-
val.val,
1914+
val,
18971915
pat_v.discr,
1898-
pat_v.field_index(name));
1916+
field_idx);
1917+
1918+
let fty = pat_v.fields[field_idx].1;
1919+
// If it's not sized, then construct a fat pointer instead of
1920+
// a regular one
1921+
if !type_is_sized(tcx, fty) {
1922+
let scratch = alloc_ty(bcx, fty, "__struct_field_fat_ptr");
1923+
debug!("Creating fat pointer {}", bcx.val_to_string(scratch));
1924+
Store(bcx, fldptr, expr::get_dataptr(bcx, scratch));
1925+
Store(bcx, val.meta, expr::get_meta(bcx, scratch));
1926+
fldptr = scratch;
1927+
}
18991928
bcx = bind_irrefutable_pat(bcx,
19001929
&*f.node.pat,
19011930
MatchInput::from_val(fldptr),
@@ -1904,8 +1933,9 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
19041933
}
19051934
hir::PatTup(ref elems) => {
19061935
let repr = adt::represent_node(bcx, pat.id);
1936+
let val = adt::MaybeSizedValue::sized(val.val);
19071937
for (i, elem) in elems.iter().enumerate() {
1908-
let fldptr = adt::trans_field_ptr(bcx, &*repr, val.val, 0, i);
1938+
let fldptr = adt::trans_field_ptr(bcx, &*repr, val, 0, i);
19091939
bcx = bind_irrefutable_pat(
19101940
bcx,
19111941
&**elem,
@@ -1914,16 +1944,28 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
19141944
}
19151945
}
19161946
hir::PatBox(ref inner) => {
1917-
let llbox = Load(bcx, val.val);
1947+
let pat_ty = node_id_type(bcx, inner.id);
1948+
// Don't load DSTs, instead pass along a fat ptr
1949+
let val = if type_is_sized(tcx, pat_ty) {
1950+
Load(bcx, val.val)
1951+
} else {
1952+
val.val
1953+
};
19181954
bcx = bind_irrefutable_pat(
1919-
bcx, &**inner, MatchInput::from_val(llbox), cleanup_scope);
1955+
bcx, &**inner, MatchInput::from_val(val), cleanup_scope);
19201956
}
19211957
hir::PatRegion(ref inner, _) => {
1922-
let loaded_val = Load(bcx, val.val);
1958+
let pat_ty = node_id_type(bcx, inner.id);
1959+
// Don't load DSTs, instead pass along a fat ptr
1960+
let val = if type_is_sized(tcx, pat_ty) {
1961+
Load(bcx, val.val)
1962+
} else {
1963+
val.val
1964+
};
19231965
bcx = bind_irrefutable_pat(
19241966
bcx,
19251967
&**inner,
1926-
MatchInput::from_val(loaded_val),
1968+
MatchInput::from_val(val),
19271969
cleanup_scope);
19281970
}
19291971
hir::PatVec(ref before, ref slice, ref after) => {

src/librustc_trans/trans/adt.rs

+116-11
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.
@@ -976,7 +1004,7 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
9761004
}
9771005
General(ity, ref cases, dtor) => {
9781006
if dtor_active(dtor) {
979-
let ptr = trans_field_ptr(bcx, r, val, discr,
1007+
let ptr = trans_field_ptr(bcx, r, MaybeSizedValue::sized(val), discr,
9801008
cases[discr as usize].fields.len() - 2);
9811009
Store(bcx, C_u8(bcx.ccx(), DTOR_NEEDED), ptr);
9821010
}
@@ -1037,7 +1065,7 @@ pub fn num_args(r: &Repr, discr: Disr) -> usize {
10371065

10381066
/// Access a field, at a point when the value's case is known.
10391067
pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
1040-
val: ValueRef, discr: Disr, ix: usize) -> ValueRef {
1068+
val: MaybeSizedValue, discr: Disr, ix: usize) -> ValueRef {
10411069
// Note: if this ever needs to generate conditionals (e.g., if we
10421070
// decide to do some kind of cdr-coding-like non-unique repr
10431071
// someday), it will need to return a possibly-new bcx as well.
@@ -1060,13 +1088,13 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10601088
assert_eq!(machine::llsize_of_alloc(bcx.ccx(), ty), 0);
10611089
// The contents of memory at this pointer can't matter, but use
10621090
// the value that's "reasonable" in case of pointer comparison.
1063-
PointerCast(bcx, val, ty.ptr_to())
1091+
PointerCast(bcx, val.value, ty.ptr_to())
10641092
}
10651093
RawNullablePointer { nndiscr, nnty, .. } => {
10661094
assert_eq!(ix, 0);
10671095
assert_eq!(discr, nndiscr);
10681096
let ty = type_of::type_of(bcx.ccx(), nnty);
1069-
PointerCast(bcx, val, ty.ptr_to())
1097+
PointerCast(bcx, val.value, ty.ptr_to())
10701098
}
10711099
StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => {
10721100
assert_eq!(discr, nndiscr);
@@ -1075,18 +1103,94 @@ pub fn trans_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
10751103
}
10761104
}
10771105

1078-
pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: ValueRef,
1106+
pub fn struct_field_ptr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, st: &Struct<'tcx>, val: MaybeSizedValue,
10791107
ix: usize, needs_cast: bool) -> ValueRef {
1080-
let val = if needs_cast {
1108+
let ptr_val = if needs_cast {
10811109
let ccx = bcx.ccx();
1082-
let fields = st.fields.iter().map(|&ty| type_of::type_of(ccx, ty)).collect::<Vec<_>>();
1110+
let fields = st.fields.iter().map(|&ty| {
1111+
type_of::in_memory_type_of(ccx, ty)
1112+
}).collect::<Vec<_>>();
10831113
let real_ty = Type::struct_(ccx, &fields[..], st.packed);
1084-
PointerCast(bcx, val, real_ty.ptr_to())
1114+
PointerCast(bcx, val.value, real_ty.ptr_to())
10851115
} else {
1086-
val
1116+
val.value
10871117
};
10881118

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

10921196
pub fn fold_variants<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
@@ -1168,7 +1272,8 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
11681272
cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx
11691273
));
11701274
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);
1275+
let ptr = struct_field_ptr(variant_cx, st, MaybeSizedValue::sized(value),
1276+
(st.fields.len() - 1), false);
11721277
datum::Datum::new(ptr, ptr_ty, datum::Lvalue::new("adt::trans_drop_flag_ptr"))
11731278
.store_to(variant_cx, scratch.val)
11741279
});

0 commit comments

Comments
 (0)